using GestionIntegral.Api.Data;
using GestionIntegral.Api.Data.Repositories.Distribucion;
using GestionIntegral.Api.Dtos.Auditoria;
using GestionIntegral.Api.Dtos.Distribucion;
using GestionIntegral.Api.Models.Distribucion;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Services.Distribucion
{
    public class PorcMonCanillaService : IPorcMonCanillaService
    {
        private readonly IPorcMonCanillaRepository _porcMonCanillaRepository;
        private readonly IPublicacionRepository _publicacionRepository;
        private readonly ICanillaRepository _canillaRepository; // Para validar IdCanilla y obtener nombre
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<PorcMonCanillaService> _logger;

        public PorcMonCanillaService(
            IPorcMonCanillaRepository porcMonCanillaRepository,
            IPublicacionRepository publicacionRepository,
            ICanillaRepository canillaRepository,
            DbConnectionFactory connectionFactory,
            ILogger<PorcMonCanillaService> logger)
        {
            _porcMonCanillaRepository = porcMonCanillaRepository;
            _publicacionRepository = publicacionRepository;
            _canillaRepository = canillaRepository;
            _connectionFactory = connectionFactory;
            _logger = logger;
        }

        private PorcMonCanillaDto MapToDto((PorcMonCanilla Item, string NomApeCanilla) data) => new PorcMonCanillaDto
        {
            IdPorcMon = data.Item.IdPorcMon,
            IdPublicacion = data.Item.IdPublicacion,
            IdCanilla = data.Item.IdCanilla,
            NomApeCanilla = data.NomApeCanilla,
            VigenciaD = data.Item.VigenciaD.ToString("yyyy-MM-dd"),
            VigenciaH = data.Item.VigenciaH?.ToString("yyyy-MM-dd"),
            PorcMon = data.Item.PorcMon,
            EsPorcentaje = data.Item.EsPorcentaje
        };

        private async Task<PorcMonCanillaDto?> MapToDtoWithLookup(PorcMonCanilla? item)
        {
            if (item == null) return null; // Si el item es null, devuelve null

            var canillaData = await _canillaRepository.GetByIdAsync(item.IdCanilla);
            return new PorcMonCanillaDto
            {
                IdPorcMon = item.IdPorcMon,
                IdPublicacion = item.IdPublicacion,
                IdCanilla = item.IdCanilla,
                NomApeCanilla = canillaData.Canilla?.NomApe ?? "Canillita Desconocido",
                VigenciaD = item.VigenciaD.ToString("yyyy-MM-dd"),
                VigenciaH = item.VigenciaH?.ToString("yyyy-MM-dd"),
                PorcMon = item.PorcMon,
                EsPorcentaje = item.EsPorcentaje
            };
        }


        public async Task<IEnumerable<PorcMonCanillaDto>> ObtenerPorPublicacionIdAsync(int idPublicacion)
        {
            var data = await _porcMonCanillaRepository.GetByPublicacionIdAsync(idPublicacion);
            // Filtrar los nulos que MapToDto podría devolver (aunque no debería en este caso si GetAllWithProfileNameAsync no devuelve usuarios nulos en la tupla)
            return data.Select(MapToDto).Where(dto => dto != null).Select(dto => dto!);
        }

        public async Task<PorcMonCanillaDto?> ObtenerPorIdAsync(int idPorcMon)
        {
            var item = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon);
            return await MapToDtoWithLookup(item);
        }

        public async Task<(PorcMonCanillaDto? Item, string? Error)> CrearAsync(CreatePorcMonCanillaDto createDto, int idUsuario)
        {
            if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null)
                return (null, "La publicación especificada no existe.");

            var canillaData = await _canillaRepository.GetByIdAsync(createDto.IdCanilla);
            if (canillaData.Canilla == null) // GetByIdAsync devuelve una tupla
                return (null, "El canillita especificado no existe o no está activo.");
            // Validar que solo canillitas accionistas pueden tener porcentaje/monto (o la regla de negocio que aplique)
            // Por ejemplo, si solo los Accionistas pueden tener esta configuración:
            // if (!canillaData.Canilla.Accionista) {
            //     return (null, "Solo los canillitas accionistas pueden tener un porcentaje/monto de pago configurado.");
            // }


            var itemActivo = await _porcMonCanillaRepository.GetActiveByPublicacionCanillaAndDateAsync(createDto.IdPublicacion, createDto.IdCanilla, createDto.VigenciaD.Date);
            if (itemActivo != null)
            {
                return (null, $"Ya existe un porcentaje/monto activo para esta publicación y canillita en la fecha {createDto.VigenciaD:dd/MM/yyyy}. Cierre el período anterior.");
            }

            var nuevoItem = new PorcMonCanilla
            {
                IdPublicacion = createDto.IdPublicacion,
                IdCanilla = createDto.IdCanilla,
                VigenciaD = createDto.VigenciaD.Date,
                VigenciaH = null,
                PorcMon = createDto.PorcMon,
                EsPorcentaje = createDto.EsPorcentaje
            };

            using var connection = _connectionFactory.CreateConnection();
            if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
            using var transaction = connection.BeginTransaction();
            try
            {
                var itemAnterior = await _porcMonCanillaRepository.GetPreviousActiveAsync(createDto.IdPublicacion, createDto.IdCanilla, nuevoItem.VigenciaD, transaction);
                if (itemAnterior != null)
                {
                    if (itemAnterior.VigenciaD.Date >= nuevoItem.VigenciaD.Date)
                    {
                        transaction.Rollback();
                        return (null, $"La fecha de inicio ({nuevoItem.VigenciaD:dd/MM/yyyy}) no puede ser anterior o igual a la del último período vigente ({itemAnterior.VigenciaD:dd/MM/yyyy}).");
                    }
                    itemAnterior.VigenciaH = nuevoItem.VigenciaD.AddDays(-1);
                    await _porcMonCanillaRepository.UpdateAsync(itemAnterior, idUsuario, transaction);
                }

                var itemCreado = await _porcMonCanillaRepository.CreateAsync(nuevoItem, idUsuario, transaction);
                if (itemCreado == null) throw new DataException("Error al crear el registro.");

                transaction.Commit();
                _logger.LogInformation("PorcMonCanilla ID {Id} creado por Usuario ID {UserId}.", itemCreado.IdPorcMon, idUsuario);
                return (await MapToDtoWithLookup(itemCreado), null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error CrearAsync PorcMonCanilla para Pub ID {IdPub}, Canilla ID {IdCan}", createDto.IdPublicacion, createDto.IdCanilla);
                return (null, $"Error interno: {ex.Message}");
            }
        }

        public async Task<(bool Exito, string? Error)> ActualizarAsync(int idPorcMon, UpdatePorcMonCanillaDto updateDto, int idUsuario)
        {
            using var connection = _connectionFactory.CreateConnection();
            if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
            using var transaction = connection.BeginTransaction();
            try
            {
                var itemExistente = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon);
                if (itemExistente == null) return (false, "Registro de porcentaje/monto no encontrado.");

                if (updateDto.VigenciaH.HasValue && updateDto.VigenciaH.Value.Date < itemExistente.VigenciaD.Date)
                    return (false, "Vigencia Hasta no puede ser anterior a Vigencia Desde.");

                if (updateDto.VigenciaH.HasValue)
                {
                    var itemsPubCanillaData = await _porcMonCanillaRepository.GetByPublicacionIdAsync(itemExistente.IdPublicacion);
                    var itemsPosteriores = itemsPubCanillaData
                       .Where(i => i.Item.IdCanilla == itemExistente.IdCanilla &&
                                   i.Item.IdPorcMon != idPorcMon &&
                                   i.Item.VigenciaD.Date <= updateDto.VigenciaH.Value.Date &&
                                   i.Item.VigenciaD.Date > itemExistente.VigenciaD.Date);
                    if (itemsPosteriores.Any())
                    {
                        return (false, "No se puede cerrar este período porque existen configuraciones posteriores para este canillita que se solaparían.");
                    }
                }

                itemExistente.PorcMon = updateDto.PorcMon;
                itemExistente.EsPorcentaje = updateDto.EsPorcentaje;
                if (updateDto.VigenciaH.HasValue)
                {
                    itemExistente.VigenciaH = updateDto.VigenciaH.Value.Date;
                }

                var actualizado = await _porcMonCanillaRepository.UpdateAsync(itemExistente, idUsuario, transaction);
                if (!actualizado) throw new DataException("Error al actualizar registro.");

                transaction.Commit();
                _logger.LogInformation("PorcMonCanilla ID {Id} actualizado por Usuario ID {UserId}.", idPorcMon, idUsuario);
                return (true, null);
            }
            catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error ActualizarAsync PorcMonCanilla ID: {Id}", idPorcMon);
                return (false, $"Error interno: {ex.Message}");
            }
        }

        public async Task<(bool Exito, string? Error)> EliminarAsync(int idPorcMon, int idUsuario)
        {
            using var connection = _connectionFactory.CreateConnection();
            if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
            using var transaction = connection.BeginTransaction();
            try
            {
                var itemAEliminar = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon);
                if (itemAEliminar == null) return (false, "Registro no encontrado.");

                if (itemAEliminar.VigenciaH == null)
                {
                    var todosItemsPubCanillaData = await _porcMonCanillaRepository.GetByPublicacionIdAsync(itemAEliminar.IdPublicacion);
                    var todosItemsPubCanilla = todosItemsPubCanillaData
                                                .Where(i => i.Item.IdCanilla == itemAEliminar.IdCanilla)
                                                .Select(i => i.Item)
                                                .OrderByDescending(i => i.VigenciaD).ToList();

                    var indiceActual = todosItemsPubCanilla.FindIndex(i => i.IdPorcMon == idPorcMon);
                    if (indiceActual != -1 && (indiceActual + 1) < todosItemsPubCanilla.Count)
                    {
                        var itemAnteriorDirecto = todosItemsPubCanilla[indiceActual + 1];
                        if (itemAnteriorDirecto.VigenciaH.HasValue &&
                           itemAnteriorDirecto.VigenciaH.Value.Date == itemAEliminar.VigenciaD.AddDays(-1).Date)
                        {
                            itemAnteriorDirecto.VigenciaH = null;
                            await _porcMonCanillaRepository.UpdateAsync(itemAnteriorDirecto, idUsuario, transaction);
                        }
                    }
                }

                var eliminado = await _porcMonCanillaRepository.DeleteAsync(idPorcMon, idUsuario, transaction);
                if (!eliminado) throw new DataException("Error al eliminar registro.");

                transaction.Commit();
                _logger.LogInformation("PorcMonCanilla ID {Id} eliminado por Usuario ID {UserId}.", idPorcMon, idUsuario);
                return (true, null);
            }
            catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error EliminarAsync PorcMonCanilla ID: {Id}", idPorcMon);
                return (false, $"Error interno: {ex.Message}");
            }
        }

        public async Task<IEnumerable<PorcMonCanillaHistorialDto>> ObtenerHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idPorcMonAfectado, int? idPublicacionAfectada, int? idCanillaAfectado)
        {
            var historialData = await _porcMonCanillaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPorcMonAfectado, idPublicacionAfectada, idCanillaAfectado);

            return historialData.Select(h => new PorcMonCanillaHistorialDto
            {
                Id_PorcMon = h.Historial.Id_PorcMon,
                Id_Publicacion = h.Historial.Id_Publicacion,
                Id_Canilla = h.Historial.Id_Canilla,
                VigenciaD = h.Historial.VigenciaD,
                VigenciaH = h.Historial.VigenciaH,
                PorcMon = h.Historial.PorcMon,
                EsPorcentaje = h.Historial.EsPorcentaje,
                Id_Usuario = h.Historial.Id_Usuario,
                NombreUsuarioModifico = h.NombreUsuarioModifico,
                FechaMod = h.Historial.FechaMod,
                TipoMod = h.Historial.TipoMod
            }).ToList();
        }
    }
}