using GestionIntegral.Api.Dtos.Empresas; // Para los DTOs (EmpresaDto, CreateEmpresaDto, UpdateEmpresaDto)
using Microsoft.Extensions.Logging; // Para ILogger
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Data;
using GestionIntegral.Api.Data;
using GestionIntegral.Api.Models.Distribucion;
using GestionIntegral.Api.Data.Repositories.Distribucion;
using GestionIntegral.Api.Data.Repositories.Contables;
using GestionIntegral.Api.Dtos.Auditoria; // Para IDbTransaction, ConnectionState

namespace GestionIntegral.Api.Services.Distribucion
{
    public class EmpresaService : IEmpresaService
    {
        private readonly IEmpresaRepository _empresaRepository;
        private readonly ISaldoRepository _saldoRepository;
        private readonly DbConnectionFactory _connectionFactory; // Para manejar la transacción
        private readonly ILogger<EmpresaService> _logger;

        public EmpresaService(
            IEmpresaRepository empresaRepository,
            ISaldoRepository saldoRepository,
            DbConnectionFactory connectionFactory,
            ILogger<EmpresaService> logger)
        {
            _empresaRepository = empresaRepository;
            _saldoRepository = saldoRepository;
            _connectionFactory = connectionFactory;
            _logger = logger;
        }

        public async Task<IEnumerable<EmpresaDto>> ObtenerTodasAsync(string? nombreFilter, string? detalleFilter)
        {
            // El repositorio ya devuelve solo las activas si es necesario
            var empresas = await _empresaRepository.GetAllAsync(nombreFilter, detalleFilter);
            // Mapeo Entidad -> DTO
            return empresas.Select(e => new EmpresaDto
            {
                IdEmpresa = e.IdEmpresa,
                Nombre = e.Nombre,
                Detalle = e.Detalle
            });
        }

        public async Task<IEnumerable<EmpresaDropdownDto>> ObtenerParaDropdown()
        {
            // El repositorio ya devuelve solo las activas si es necesario
            var empresas = await _empresaRepository.GetAllDropdownAsync();
            // Mapeo Entidad -> DTO
            return empresas.Select(e => new EmpresaDropdownDto
            {
                IdEmpresa = e.IdEmpresa,
                Nombre = e.Nombre
            });
        }

        public async Task<EmpresaDto?> ObtenerPorIdAsync(int id)
        {
            // El repositorio ya devuelve solo las activas si es necesario
            var empresa = await _empresaRepository.GetByIdAsync(id);
            if (empresa == null) return null;
            // Mapeo Entidad -> DTO
            return new EmpresaDto
            {
                IdEmpresa = empresa.IdEmpresa,
                Nombre = empresa.Nombre,
                Detalle = empresa.Detalle
            };
        }

        public async Task<EmpresaLookupDto?> ObtenerLookupPorIdAsync(int id)
        {
            // El repositorio ya devuelve solo las activas si es necesario
            var empresa = await _empresaRepository.ObtenerLookupPorIdAsync(id);
            if (empresa == null) return null;
            // Mapeo Entidad -> DTO
            return new EmpresaLookupDto
            {
                IdEmpresa = empresa.IdEmpresa,
                Nombre = empresa.Nombre
            };
        }

        public async Task<(EmpresaDto? Empresa, string? Error)> CrearAsync(CreateEmpresaDto createDto, int idUsuario)
        {
            // Validación de negocio: Nombre duplicado
            if (await _empresaRepository.ExistsByNameAsync(createDto.Nombre))
            {
                return (null, "El nombre de la empresa ya existe.");
            }

            var nuevaEmpresa = new Empresa
            {
                Nombre = createDto.Nombre,
                Detalle = createDto.Detalle
            };

            // --- Transacción ---
            using (var connection = _connectionFactory.CreateConnection())
            {
                // Abrir conexión de forma asíncrona
                if (connection is System.Data.Common.DbConnection dbConnection)
                {
                    await dbConnection.OpenAsync();
                }
                else
                {
                    connection.Open(); // Fallback síncrono
                }

                using (var transaction = connection.BeginTransaction())
                {
                    try
                    {
                        // 1. Crear Empresa (Repo maneja su historial dentro de esta transacción)
                        var empresaCreada = await _empresaRepository.CreateAsync(nuevaEmpresa, idUsuario, transaction);
                        if (empresaCreada == null)
                        {
                            throw new InvalidOperationException("No se pudo crear la empresa en el repositorio.");
                        }

                        // 2. Obtener IDs de Distribuidores
                        var distribuidoresIds = await _saldoRepository.GetAllDistribuidorIdsAsync(); // No necesita transacción si solo lee

                        // 3. Crear Saldos Iniciales (CERO) para cada distribuidor en esta nueva empresa
                        foreach (var idDistribuidor in distribuidoresIds)
                        {
                            bool saldoCreado = await _saldoRepository.CreateSaldoInicialAsync("Distribuidores", idDistribuidor, empresaCreada.IdEmpresa, transaction);
                            if (!saldoCreado)
                            {
                                throw new InvalidOperationException($"Falló al crear saldo inicial para distribuidor {idDistribuidor} y nueva empresa {empresaCreada.IdEmpresa}.");
                            }
                            _logger.LogInformation("Saldo inicial creado para Distribuidor ID {IdDistribuidor}, Empresa ID {IdEmpresa}", idDistribuidor, empresaCreada.IdEmpresa);
                        }

                        transaction.Commit();

                        var empresaDto = new EmpresaDto
                        {
                            IdEmpresa = empresaCreada.IdEmpresa,
                            Nombre = empresaCreada.Nombre,
                            Detalle = empresaCreada.Detalle
                        };
                        _logger.LogInformation("Empresa ID {IdEmpresa} creada exitosamente por Usuario ID {IdUsuario}.", empresaCreada.IdEmpresa, idUsuario);
                        return (empresaDto, null); // Éxito
                    }
                    catch (Exception ex)
                    {
                        try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error al intentar hacer rollback en CrearAsync Empresa."); }
                        _logger.LogError(ex, "Error en transacción CrearAsync para Empresa. Nombre: {Nombre}", createDto.Nombre);
                        return (null, "Error interno al procesar la creación de la empresa.");
                    }
                } // La transacción se dispone aquí (y cierra la conexión si no hubo commit/rollback explícito, aunque ya lo hacemos)
            } // La conexión se cierra/dispone aquí
            // --- Fin Transacción ---
        }

        public async Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateEmpresaDto updateDto, int idUsuario)
        {
            var empresaExistente = await _empresaRepository.GetByIdAsync(id);
            if (empresaExistente == null)
            {
                return (false, "Empresa no encontrada.");
            }

            if (await _empresaRepository.ExistsByNameAsync(updateDto.Nombre, id))
            {
                return (false, "El nombre de la empresa ya existe para otro registro.");
            }

            empresaExistente.Nombre = updateDto.Nombre;
            empresaExistente.Detalle = updateDto.Detalle;

            // --- Transacción ---
            using (var connection = _connectionFactory.CreateConnection())
            {
                if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); }
                using (var transaction = connection.BeginTransaction())
                {
                    try
                    {
                        var actualizado = await _empresaRepository.UpdateAsync(empresaExistente, idUsuario, transaction);
                        if (!actualizado)
                        {
                            throw new InvalidOperationException("La actualización en el repositorio de empresas devolvió false.");
                        }
                        transaction.Commit();
                        _logger.LogInformation("Empresa ID {IdEmpresa} actualizada exitosamente por Usuario ID {IdUsuario}.", id, idUsuario);
                        return (true, null); // Éxito
                    }
                    catch (Exception ex)
                    {
                        try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error al intentar hacer rollback en ActualizarAsync Empresa."); }
                        _logger.LogError(ex, "Error en transacción ActualizarAsync para Empresa ID: {Id}", id);
                        return (false, "Error interno al actualizar la empresa.");
                    }
                }
            }
            // --- Fin Transacción ---
        }

        public async Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario)
        {
            // Primero verificamos si existe, incluso inactiva, para evitar errores 404 si no existe
            var empresaExistente = await _empresaRepository.GetByIdAsync(id);
            if (empresaExistente == null)
            {
                return (false, "Empresa no encontrada.");
            }

            // Validación: ¿Está en uso?
            if (await _empresaRepository.IsInUseAsync(id))
            {
                return (false, "No se puede eliminar. Existen publicaciones relacionadas a la empresa.");
            }

            // --- Transacción ---
            using (var connection = _connectionFactory.CreateConnection())
            {
                if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); }
                using (var transaction = connection.BeginTransaction())
                {
                    try
                    {
                        // 1. Eliminar Saldos asociados
                        bool saldosEliminados = await _saldoRepository.DeleteSaldosByEmpresaAsync(id, transaction);
                        // No lanzamos error si saldosEliminados es false, podría no haber tenido saldos. Loggeamos si es necesario.
                        if (!saldosEliminados && await _saldoRepository.CheckIfSaldosExistForEmpresaAsync(id)) // Necesitarías este método en ISaldoRepository
                        {
                            _logger.LogWarning("Se intentó eliminar Empresa ID {IdEmpresa} pero falló la eliminación de saldos asociados.", id);
                            // Decidir si continuar o fallar. Por ahora, continuamos pero loggeamos.
                            // throw new InvalidOperationException("Error al intentar eliminar los saldos asociados a la empresa.");
                        }
                        else if (!saldosEliminados)
                        {
                            _logger.LogInformation("No se encontraron saldos para eliminar de la Empresa ID {IdEmpresa}.", id);
                        }
                        else
                        {
                            _logger.LogInformation("Saldos eliminados para Empresa ID {IdEmpresa}.", id);
                        }


                        // 2. Eliminar Empresa (Repo maneja historial)
                        var eliminado = await _empresaRepository.DeleteAsync(id, idUsuario, transaction);
                        if (!eliminado)
                        {
                            throw new InvalidOperationException("La eliminación en el repositorio de empresas devolvió false.");
                        }

                        transaction.Commit();
                        _logger.LogInformation("Empresa ID {IdEmpresa} eliminada exitosamente por Usuario ID {IdUsuario}.", id, idUsuario);
                        return (true, null); // Éxito
                    }
                    catch (Exception ex)
                    {
                        try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error al intentar hacer rollback en EliminarAsync Empresa."); }
                        _logger.LogError(ex, "Error en transacción EliminarAsync para Empresa ID: {Id}", id);
                        return (false, "Error interno al eliminar la empresa.");
                    }
                }
            }
        }

        public async Task<IEnumerable<EmpresaHistorialDto>> ObtenerHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idEmpresaAfectada)
        {
            var historialData = await _empresaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idEmpresaAfectada);

            return historialData.Select(h => new EmpresaHistorialDto
            {
                Id_Empresa = h.Historial.Id_Empresa,
                Nombre = h.Historial.Nombre,
                Detalle = h.Historial.Detalle,
                Id_Usuario = h.Historial.Id_Usuario,
                NombreUsuarioModifico = h.NombreUsuarioModifico,
                FechaMod = h.Historial.FechaMod,
                TipoMod = h.Historial.TipoMod
            }).ToList();
        }
    }
}