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.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Services.Distribucion
{
    public class PubliSeccionService : IPubliSeccionService
    {
        private readonly IPubliSeccionRepository _publiSeccionRepository;
        private readonly IPublicacionRepository _publicacionRepository; // Para validar IdPublicacion
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<PubliSeccionService> _logger;

        public PubliSeccionService(
            IPubliSeccionRepository publiSeccionRepository,
            IPublicacionRepository publicacionRepository,
            DbConnectionFactory connectionFactory,
            ILogger<PubliSeccionService> logger)
        {
            _publiSeccionRepository = publiSeccionRepository;
            _publicacionRepository = publicacionRepository;
            _connectionFactory = connectionFactory;
            _logger = logger;
        }

        private PubliSeccionDto MapToDto(PubliSeccion seccion) => new PubliSeccionDto
        {
            IdSeccion = seccion.IdSeccion,
            IdPublicacion = seccion.IdPublicacion,
            Nombre = seccion.Nombre,
            Estado = seccion.Estado
        };

        public async Task<IEnumerable<PubliSeccionDto>> ObtenerPorPublicacionIdAsync(int idPublicacion, bool? soloActivas = null)
        {
            var secciones = await _publiSeccionRepository.GetByPublicacionIdAsync(idPublicacion, soloActivas);
            return secciones.Select(MapToDto);
        }

        public async Task<PubliSeccionDto?> ObtenerPorIdAsync(int idSeccion)
        {
            var seccion = await _publiSeccionRepository.GetByIdAsync(idSeccion);
            return seccion == null ? null : MapToDto(seccion);
        }

        public async Task<(PubliSeccionDto? Seccion, string? Error)> CrearAsync(CreatePubliSeccionDto createDto, int idUsuario)
        {
            if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null)
                return (null, "La publicación especificada no existe.");
            if (await _publiSeccionRepository.ExistsByNameInPublicacionAsync(createDto.Nombre, createDto.IdPublicacion))
                return (null, "Ya existe una sección con ese nombre para esta publicación.");

            var nuevaSeccion = new PubliSeccion
            {
                IdPublicacion = createDto.IdPublicacion,
                Nombre = createDto.Nombre,
                Estado = createDto.Estado
            };

            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 seccionCreada = await _publiSeccionRepository.CreateAsync(nuevaSeccion, idUsuario, transaction);
                if (seccionCreada == null) throw new DataException("Error al crear la sección.");

                transaction.Commit();
                _logger.LogInformation("Sección ID {Id} creada por Usuario ID {UserId}.", seccionCreada.IdSeccion, idUsuario);
                return (MapToDto(seccionCreada), null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error CrearAsync PubliSeccion para Pub ID {IdPub}, Nombre: {Nombre}", createDto.IdPublicacion, createDto.Nombre);
                return (null, $"Error interno: {ex.Message}");
            }
        }

        public async Task<(bool Exito, string? Error)> ActualizarAsync(int idSeccion, UpdatePubliSeccionDto 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 seccionExistente = await _publiSeccionRepository.GetByIdAsync(idSeccion); // Obtener dentro de TX
                if (seccionExistente == null) return (false, "Sección no encontrada.");

                // Validar unicidad de nombre solo si el nombre ha cambiado
                if (seccionExistente.Nombre != updateDto.Nombre &&
                    await _publiSeccionRepository.ExistsByNameInPublicacionAsync(updateDto.Nombre, seccionExistente.IdPublicacion, idSeccion))
                {
                    return (false, "Ya existe otra sección con ese nombre para esta publicación.");
                }

                seccionExistente.Nombre = updateDto.Nombre;
                seccionExistente.Estado = updateDto.Estado;

                var actualizado = await _publiSeccionRepository.UpdateAsync(seccionExistente, idUsuario, transaction);
                if (!actualizado) throw new DataException("Error al actualizar la sección.");

                transaction.Commit();
                _logger.LogInformation("Sección ID {Id} actualizada por Usuario ID {UserId}.", idSeccion, idUsuario);
                return (true, null);
            }
            catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Sección no encontrada."); }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error ActualizarAsync PubliSeccion ID: {Id}", idSeccion);
                return (false, $"Error interno: {ex.Message}");
            }
        }

        public async Task<(bool Exito, string? Error)> EliminarAsync(int idSeccion, 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 seccionExistente = await _publiSeccionRepository.GetByIdAsync(idSeccion); // Obtener dentro de TX
                if (seccionExistente == null) return (false, "Sección no encontrada.");

                if (await _publiSeccionRepository.IsInUseAsync(idSeccion))
                {
                    return (false, "No se puede eliminar. La sección está siendo utilizada en registros de tiradas o stock de bobinas.");
                }

                var eliminado = await _publiSeccionRepository.DeleteAsync(idSeccion, idUsuario, transaction);
                if (!eliminado) throw new DataException("Error al eliminar la sección.");

                transaction.Commit();
                _logger.LogInformation("Sección ID {Id} eliminada por Usuario ID {UserId}.", idSeccion, idUsuario);
                return (true, null);
            }
            catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Sección no encontrada."); }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error EliminarAsync PubliSeccion ID: {Id}", idSeccion);
                return (false, $"Error interno: {ex.Message}");
            }
        }

        public async Task<IEnumerable<PubliSeccionHistorialDto>> ObtenerHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idSeccionAfectada, int? idPublicacionAfectada)
        {
            var historialData = await _publiSeccionRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idSeccionAfectada, idPublicacionAfectada);

            // Si necesitas NombrePublicacion, harías la llamada a _publicacionRepository aquí.
            return historialData.Select(h => new PubliSeccionHistorialDto
            {
                Id_Seccion = h.Historial.Id_Seccion,
                Id_Publicacion = h.Historial.Id_Publicacion,
                Nombre = h.Historial.Nombre,
                Estado = h.Historial.Estado,
                Id_Usuario = h.Historial.Id_Usuario,
                NombreUsuarioModifico = h.NombreUsuarioModifico,
                FechaMod = h.Historial.FechaMod,
                TipoMod = h.Historial.TipoMod
            }).ToList();
        }
    }
}