using GestionIntegral.Api.Data;
using GestionIntegral.Api.Data.Repositories.Distribucion;
using GestionIntegral.Api.Data.Repositories.Impresion;
using GestionIntegral.Api.Dtos.Auditoria;
using GestionIntegral.Api.Dtos.Impresion;
using GestionIntegral.Api.Models.Distribucion; // Para Publicacion, PubliSeccion
using GestionIntegral.Api.Models.Impresion;   // Para RegTirada, RegPublicacionSeccion
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Services.Impresion
{
    public class TiradaService : ITiradaService
    {
        private readonly IRegTiradaRepository _regTiradaRepository;
        private readonly IRegPublicacionSeccionRepository _regPublicacionSeccionRepository;
        private readonly IPublicacionRepository _publicacionRepository;
        private readonly IPlantaRepository _plantaRepository;
        private readonly IPubliSeccionRepository _publiSeccionRepository; // Para validar IDs de sección
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<TiradaService> _logger;

        public TiradaService(
            IRegTiradaRepository regTiradaRepository,
            IRegPublicacionSeccionRepository regPublicacionSeccionRepository,
            IPublicacionRepository publicacionRepository,
            IPlantaRepository plantaRepository,
            IPubliSeccionRepository publiSeccionRepository,
            DbConnectionFactory connectionFactory,
            ILogger<TiradaService> logger)
        {
            _regTiradaRepository = regTiradaRepository;
            _regPublicacionSeccionRepository = regPublicacionSeccionRepository;
            _publicacionRepository = publicacionRepository;
            _plantaRepository = plantaRepository;
            _publiSeccionRepository = publiSeccionRepository;
            _connectionFactory = connectionFactory;
            _logger = logger;
        }

        public async Task<IEnumerable<TiradaDto>> ObtenerTiradasAsync(DateTime? fecha, int? idPublicacion, int? idPlanta)
        {
            var tiradasPrincipales = await _regTiradaRepository.GetByCriteriaAsync(fecha, idPublicacion, idPlanta);
            var resultado = new List<TiradaDto>();

            foreach (var tiradaP in tiradasPrincipales)
            {
                var publicacion = await _publicacionRepository.GetByIdSimpleAsync(tiradaP.IdPublicacion);
                var planta = await _plantaRepository.GetByIdAsync(tiradaP.IdPlanta);
                var seccionesImpresas = await _regPublicacionSeccionRepository.GetByFechaPublicacionPlantaAsync(tiradaP.Fecha, tiradaP.IdPublicacion, tiradaP.IdPlanta);

                var detallesSeccionDto = new List<DetalleSeccionEnListadoDto>();
                int totalPaginas = 0;
                foreach (var seccionImp in seccionesImpresas)
                {
                    var seccionInfo = await _publiSeccionRepository.GetByIdAsync(seccionImp.IdSeccion);
                    detallesSeccionDto.Add(new DetalleSeccionEnListadoDto
                    {
                        IdSeccion = seccionImp.IdSeccion,
                        NombreSeccion = seccionInfo?.Nombre ?? "Sección Desconocida",
                        CantPag = seccionImp.CantPag,
                        IdRegPublicacionSeccion = seccionImp.IdTirada // Este es el PK de bob_RegPublicaciones
                    });
                    totalPaginas += seccionImp.CantPag;
                }

                resultado.Add(new TiradaDto
                {
                    IdRegistroTirada = tiradaP.IdRegistro,
                    IdPublicacion = tiradaP.IdPublicacion,
                    NombrePublicacion = publicacion?.Nombre ?? "Publicación Desconocida",
                    Fecha = tiradaP.Fecha.ToString("yyyy-MM-dd"),
                    IdPlanta = tiradaP.IdPlanta,
                    NombrePlanta = planta?.Nombre ?? "Planta Desconocida",
                    Ejemplares = tiradaP.Ejemplares,
                    SeccionesImpresas = detallesSeccionDto,
                    TotalPaginasSumadas = totalPaginas
                });
            }
            return resultado;
        }


        public async Task<(TiradaDto? TiradaCreada, string? Error)> RegistrarTiradaCompletaAsync(CreateTiradaRequestDto createDto, int idUsuario)
        {
            // Validaciones previas
            var publicacion = await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion);
            if (publicacion == null) return (null, "La publicación especificada no existe.");
            var planta = await _plantaRepository.GetByIdAsync(createDto.IdPlanta);
            if (planta == null) return (null, "La planta especificada no existe.");

            // Validar que no exista ya una tirada para esa Publicación, Fecha y Planta
            // (bob_RegTiradas debería ser único por estos campos)
            if (await _regTiradaRepository.GetByFechaPublicacionPlantaAsync(createDto.Fecha.Date, createDto.IdPublicacion, createDto.IdPlanta) != null)
            {
                return (null, $"Ya existe una tirada registrada para la publicación '{publicacion.Nombre}' en la planta '{planta.Nombre}' para la fecha {createDto.Fecha:dd/MM/yyyy}.");
            }


            // Validar secciones
            foreach (var seccionDto in createDto.Secciones)
            {
                var seccionDb = await _publiSeccionRepository.GetByIdAsync(seccionDto.IdSeccion);
                if (seccionDb == null || seccionDb.IdPublicacion != createDto.IdPublicacion)
                    return (null, $"La sección con ID {seccionDto.IdSeccion} no es válida o no pertenece a la publicación seleccionada.");
                if (!seccionDb.Estado) // Asumiendo que solo se pueden tirar secciones activas
                    return (null, $"La sección '{seccionDb.Nombre}' no está activa y no puede incluirse en la tirada.");
            }


            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
            {
                // 1. Crear registro en bob_RegTiradas (total de ejemplares)
                var nuevaRegTirada = new RegTirada
                {
                    IdPublicacion = createDto.IdPublicacion,
                    Fecha = createDto.Fecha.Date,
                    IdPlanta = createDto.IdPlanta,
                    Ejemplares = createDto.Ejemplares
                };
                var regTiradaCreada = await _regTiradaRepository.CreateAsync(nuevaRegTirada, idUsuario, transaction);
                if (regTiradaCreada == null) throw new DataException("Error al registrar el total de la tirada.");

                // 2. Crear registros en bob_RegPublicaciones (detalle de secciones y páginas)
                var seccionesImpresasDto = new List<DetalleSeccionEnListadoDto>();
                int totalPaginasSumadas = 0;

                foreach (var seccionDto in createDto.Secciones)
                {
                    var nuevaRegPubSeccion = new RegPublicacionSeccion
                    {
                        IdPublicacion = createDto.IdPublicacion,
                        IdSeccion = seccionDto.IdSeccion,
                        CantPag = seccionDto.CantPag,
                        Fecha = createDto.Fecha.Date,
                        IdPlanta = createDto.IdPlanta
                    };
                    var seccionCreadaEnTirada = await _regPublicacionSeccionRepository.CreateAsync(nuevaRegPubSeccion, idUsuario, transaction);
                    if (seccionCreadaEnTirada == null) throw new DataException($"Error al registrar la sección ID {seccionDto.IdSeccion} en la tirada.");

                    var seccionInfo = await _publiSeccionRepository.GetByIdAsync(seccionDto.IdSeccion); // Para obtener nombre
                    seccionesImpresasDto.Add(new DetalleSeccionEnListadoDto
                    {
                        IdSeccion = seccionCreadaEnTirada.IdSeccion,
                        NombreSeccion = seccionInfo?.Nombre ?? "N/A",
                        CantPag = seccionCreadaEnTirada.CantPag,
                        IdRegPublicacionSeccion = seccionCreadaEnTirada.IdTirada
                    });
                    totalPaginasSumadas += seccionCreadaEnTirada.CantPag;
                }

                transaction.Commit();
                _logger.LogInformation("Tirada completa registrada para Pub ID {IdPub}, Fecha {Fecha}, Planta ID {IdPlanta} por Usuario ID {UserId}.",
                    createDto.IdPublicacion, createDto.Fecha.Date, createDto.IdPlanta, idUsuario);

                return (new TiradaDto
                {
                    IdRegistroTirada = regTiradaCreada.IdRegistro,
                    IdPublicacion = regTiradaCreada.IdPublicacion,
                    NombrePublicacion = publicacion.Nombre,
                    Fecha = regTiradaCreada.Fecha.ToString("yyyy-MM-dd"),
                    IdPlanta = regTiradaCreada.IdPlanta,
                    NombrePlanta = planta.Nombre,
                    Ejemplares = regTiradaCreada.Ejemplares,
                    SeccionesImpresas = seccionesImpresasDto,
                    TotalPaginasSumadas = totalPaginasSumadas
                }, null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error RegistrarTiradaCompletaAsync para Pub ID {IdPub}, Fecha {Fecha}", createDto.IdPublicacion, createDto.Fecha);
                return (null, $"Error interno: {ex.Message}");
            }
        }

        public async Task<(TiradaDto? TiradaActualizada, string? Error)> ModificarTiradaCompletaAsync(DateTime fecha, int idPublicacion, int idPlanta, UpdateTiradaRequestDto updateDto, int idUsuario)
        {
            // 1. Validar que la tirada a modificar exista.
            var tiradaExistente = await _regTiradaRepository.GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta);
            if (tiradaExistente == null)
            {
                return (null, "No se encontró la tirada que intenta modificar.");
            }

            // 2. Validaciones de DTO (secciones válidas, etc.)
            var idsSeccionesUnicasDto = updateDto.Secciones.Select(s => s.IdSeccion).Distinct();
            if (idsSeccionesUnicasDto.Any()) // Solo validar si se enviaron secciones
            {
                var seccionesValidasDb = await _publiSeccionRepository.GetByIdsAndPublicacionAsync(idsSeccionesUnicasDto, idPublicacion, soloActivas: true);

                if (seccionesValidasDb.Count() != idsSeccionesUnicasDto.Count())
                {
                    var idsEncontrados = seccionesValidasDb.Select(s => s.IdSeccion).ToHashSet();
                    var idsFaltantes = string.Join(", ", idsSeccionesUnicasDto.Where(id => !idsEncontrados.Contains(id)));
                    return (null, $"Las siguientes secciones no son válidas, no pertenecen a la publicación o no están activas: {idsFaltantes}.");
                }
            }

            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
            {
                // 3. Actualizar registro principal (bob_RegTiradas) si cambió el número de ejemplares
                if (tiradaExistente.Ejemplares != updateDto.Ejemplares)
                {
                    tiradaExistente.Ejemplares = updateDto.Ejemplares;
                    bool actualizado = await _regTiradaRepository.UpdateAsync(tiradaExistente, idUsuario, transaction);
                    if (!actualizado) throw new DataException("No se pudo actualizar el registro principal de la tirada.");
                }

                // 4. Lógica de reconciliación de secciones
                // 4.1. Obtener estado actual de las secciones en la BBDD
                var seccionesActualesDb = await _regPublicacionSeccionRepository.GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta);

                // 4.2. Procesar Adiciones y Actualizaciones
                foreach (var seccionDto in updateDto.Secciones)
                {
                    if (seccionDto.IdRegPublicacionSeccion == 0) // Es una nueva sección
                    {
                        var nuevaRegPubSeccion = new RegPublicacionSeccion
                        {
                            IdPublicacion = idPublicacion,
                            IdSeccion = seccionDto.IdSeccion,
                            CantPag = seccionDto.CantPag,
                            Fecha = fecha.Date,
                            IdPlanta = idPlanta
                        };
                        await _regPublicacionSeccionRepository.CreateAsync(nuevaRegPubSeccion, idUsuario, transaction);
                    }
                    else // Es una sección existente, verificar si hay cambios
                    {
                        var seccionDb = seccionesActualesDb.FirstOrDefault(s => s.IdTirada == seccionDto.IdRegPublicacionSeccion);
                        if (seccionDb == null) throw new InvalidOperationException($"La sección con ID de registro {seccionDto.IdRegPublicacionSeccion} no pertenece a esta tirada.");

                        if (seccionDb.CantPag != seccionDto.CantPag) // Solo actualizamos si cambió la cantidad de páginas
                        {
                            seccionDb.CantPag = seccionDto.CantPag;
                            await _regPublicacionSeccionRepository.UpdateAsync(seccionDb, idUsuario, transaction);
                        }
                    }
                }

                // 4.3. Procesar Eliminaciones
                var idsSeccionesEntrantes = updateDto.Secciones
                    .Where(s => s.IdRegPublicacionSeccion != 0)
                    .Select(s => s.IdRegPublicacionSeccion)
                    .ToHashSet();

                var seccionesAEliminar = seccionesActualesDb
                    .Where(s => !idsSeccionesEntrantes.Contains(s.IdTirada));

                foreach (var seccionParaBorrar in seccionesAEliminar)
                {
                    await _regPublicacionSeccionRepository.DeleteByIdAsync(seccionParaBorrar.IdTirada, idUsuario, transaction);
                }

                transaction.Commit();
                _logger.LogInformation("Tirada completa modificada (granular) para Pub ID {IdPub}, Fecha {Fecha}, por Usuario ID {UserId}.", idPublicacion, fecha.Date, idUsuario);

                // 5. Devolver el DTO actualizado (tendríamos que volver a consultar o construirlo)
                var tiradaActualizadaResult = await ObtenerTiradasAsync(fecha.Date, idPublicacion, idPlanta);
                return (tiradaActualizadaResult.FirstOrDefault(), null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error en ModificarTiradaCompletaAsync (granular) para Pub ID {IdPub}, Fecha {Fecha}", idPublicacion, fecha);
                return (null, $"Error interno al modificar la tirada: {ex.Message}");
            }
        }

        public async Task<(bool Exito, string? Error)> EliminarTiradaCompletaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario)
        {
            // Verificar que la tirada principal exista
            var tiradaPrincipal = await _regTiradaRepository.GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta);
            if (tiradaPrincipal == null)
            {
                return (false, "No se encontró una tirada principal para los criterios especificados.");
            }

            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
            {
                // 1. Eliminar detalles de secciones de bob_RegPublicaciones
                // El método ya guarda en historial
                await _regPublicacionSeccionRepository.DeleteByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta, idUsuario, transaction);
                _logger.LogInformation("Secciones de tirada eliminadas para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}", fecha.Date, idPublicacion, idPlanta);

                // 2. Eliminar el registro principal de bob_RegTiradas
                // El método ya guarda en historial
                bool principalEliminado = await _regTiradaRepository.DeleteByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta, idUsuario, transaction);
                // bool principalEliminado = await _regTiradaRepository.DeleteAsync(tiradaPrincipal.IdRegistro, idUsuario, transaction); // Alternativa si ya tienes el IdRegistro
                if (!principalEliminado && tiradaPrincipal != null) // Si DeleteByFechaPublicacionPlantaAsync devuelve false porque no encontró nada (raro si pasó la validación)
                {
                    _logger.LogWarning("No se eliminó el registro principal de tirada (bob_RegTiradas) para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}. Pudo haber sido eliminado concurrentemente.", fecha.Date, idPublicacion, idPlanta);
                    // Decidir si esto es un error que debe hacer rollback
                }
                _logger.LogInformation("Registro principal de tirada eliminado para Fecha: {Fecha}, PubID: {IdPublicacion}, PlantaID: {IdPlanta}", fecha.Date, idPublicacion, idPlanta);


                transaction.Commit();
                return (true, null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error EliminarTiradaCompletaAsync para Fecha {Fecha}, PubID {IdPub}, PlantaID {IdPlanta}", fecha.Date, idPublicacion, idPlanta);
                return (false, $"Error interno al eliminar la tirada: {ex.Message}");
            }
        }

        public async Task<IEnumerable<RegTiradaHistorialDto>> ObtenerRegTiradasHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idRegistroAfectado, int? idPublicacionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro)
        {
            var historialData = await _regTiradaRepository.GetRegTiradasHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idRegistroAfectado, idPublicacionFiltro, idPlantaFiltro, fechaTiradaFiltro);

            return historialData.Select(h => new RegTiradaHistorialDto
            {
                Id_Registro = h.Historial.Id_Registro,
                Ejemplares = h.Historial.Ejemplares,
                Id_Publicacion = h.Historial.Id_Publicacion,
                NombrePublicacion = h.NombrePublicacion,
                Fecha = h.Historial.Fecha,
                Id_Planta = h.Historial.Id_Planta,
                NombrePlanta = h.NombrePlanta,
                Id_Usuario = h.Historial.Id_Usuario,
                NombreUsuarioModifico = h.NombreUsuarioModifico,
                FechaMod = h.Historial.FechaMod,
                TipoMod = h.Historial.TipoMod
            }).ToList();
        }

        public async Task<IEnumerable<RegSeccionTiradaHistorialDto>> ObtenerRegSeccionesTiradaHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idTiradaAfectada, int? idPublicacionFiltro, int? idSeccionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro)
        {
            var historialData = await _regTiradaRepository.GetRegSeccionesTiradaHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idTiradaAfectada, idPublicacionFiltro, idSeccionFiltro, idPlantaFiltro, fechaTiradaFiltro);

            return historialData.Select(h => new RegSeccionTiradaHistorialDto
            {
                Id_Tirada = h.Historial.Id_Tirada,
                Id_Publicacion = h.Historial.Id_Publicacion,
                NombrePublicacion = h.NombrePublicacion,
                Id_Seccion = h.Historial.Id_Seccion,
                NombreSeccion = h.NombreSeccion,
                CantPag = h.Historial.CantPag,
                Fecha = h.Historial.Fecha,
                Id_Planta = h.Historial.Id_Planta,
                NombrePlanta = h.NombrePlanta,
                Id_Usuario = h.Historial.Id_Usuario,
                NombreUsuarioModifico = h.NombreUsuarioModifico,
                FechaMod = h.Historial.FechaMod,
                TipoMod = h.Historial.TipoMod
            }).ToList();
        }
    }
}