// En Services/Distribucion (o donde corresponda)
using GestionIntegral.Api.Data; // Para DbConnectionFactory
using GestionIntegral.Api.Data.Repositories.Distribucion;
using GestionIntegral.Api.Dtos.Auditoria;
using GestionIntegral.Api.Dtos.Distribucion;
using GestionIntegral.Api.Dtos.Reportes;
using GestionIntegral.Api.Models.Distribucion; // Asegúrate que el modelo Canilla tenga NomApe
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data; // Para IDbTransaction
using System.Linq;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Services.Distribucion
{
    public class NovedadCanillaService : INovedadCanillaService
    {
        private readonly INovedadCanillaRepository _novedadRepository;
        private readonly ICanillaRepository _canillaRepository;
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<NovedadCanillaService> _logger;

        public NovedadCanillaService(
            INovedadCanillaRepository novedadRepository,
            ICanillaRepository canillaRepository,
            DbConnectionFactory connectionFactory,
            ILogger<NovedadCanillaService> logger)
        {
            _novedadRepository = novedadRepository;
            _canillaRepository = canillaRepository;
            _connectionFactory = connectionFactory;
            _logger = logger;
        }

        private NovedadCanillaDto MapToDto((NovedadCanilla Novedad, string NombreCanilla) data)
        {
            return new NovedadCanillaDto
            {
                IdNovedad = data.Novedad.IdNovedad,
                IdCanilla = data.Novedad.IdCanilla,
                NombreCanilla = data.NombreCanilla, // Viene de la tupla en GetByCanillaAsync
                Fecha = data.Novedad.Fecha,
                Detalle = data.Novedad.Detalle
            };
        }
        private NovedadCanillaDto MapToDto(NovedadCanilla data, string nombreCanilla)
        {
            return new NovedadCanillaDto
            {
                IdNovedad = data.IdNovedad,
                IdCanilla = data.IdCanilla,
                NombreCanilla = nombreCanilla,
                Fecha = data.Fecha,
                Detalle = data.Detalle
            };
        }

        public async Task<IEnumerable<NovedadCanillaDto>> ObtenerPorCanillaAsync(int idCanilla, DateTime? fechaDesde, DateTime? fechaHasta)
        {
            var data = await _novedadRepository.GetByCanillaAsync(idCanilla, fechaDesde, fechaHasta);
            return data.Select(MapToDto);
        }

        public async Task<NovedadCanillaDto?> ObtenerPorIdAsync(int idNovedad)
        {
            var novedad = await _novedadRepository.GetByIdAsync(idNovedad);
            if (novedad == null) return null;

            // Asumiendo que _canillaRepository.GetByIdAsync devuelve una tupla (Canilla? Canilla, ...)
            // O un DTO CanillaDto que tiene NomApe
            var canillaDataResult = await _canillaRepository.GetByIdAsync(novedad.IdCanilla);

            // Ajusta esto según lo que realmente devuelva GetByIdAsync
            // Si devuelve CanillaDto:
            // string nombreCanilla = canillaDataResult?.NomApe ?? "Desconocido";
            // Si devuelve la tupla (Canilla? Canilla, string? NombreZona, string? NombreEmpresa):
            string nombreCanilla = canillaDataResult.Canilla?.NomApe ?? "Desconocido";

            return MapToDto(novedad, nombreCanilla);
        }

        public async Task<(NovedadCanillaDto? Novedad, string? Error)> CrearAsync(CreateNovedadCanillaDto createDto, int idUsuario)
        {
            // Asegúrate que GetByIdSimpleAsync devuelva un objeto Canilla o algo con NomApe
            var canilla = await _canillaRepository.GetByIdSimpleAsync(createDto.IdCanilla);
            if (canilla == null)
            {
                return (null, "El canillita especificado no existe.");
            }

            var nuevaNovedad = new NovedadCanilla
            {
                IdCanilla = createDto.IdCanilla,
                Fecha = createDto.Fecha.Date,
                Detalle = createDto.Detalle
            };

            using var connection = _connectionFactory.CreateConnection();
            // Abre la conexión explícitamente si no se usa una transacción externa
            if (connection is System.Data.Common.DbConnection dbConn && connection.State != ConnectionState.Open)
            {
                await dbConn.OpenAsync();
            }
            else if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }

            using var transaction = connection.BeginTransaction();
            try
            {
                var creada = await _novedadRepository.CreateAsync(nuevaNovedad, idUsuario, transaction);
                if (creada == null)
                {
                    transaction.Rollback();
                    return (null, "Error al guardar la novedad en la base de datos.");
                }

                transaction.Commit();
                _logger.LogInformation("Novedad ID {IdNovedad} para Canilla ID {IdCanilla} creada por Usuario ID {UserId}.", creada.IdNovedad, creada.IdCanilla, idUsuario);
                // Asegúrate que 'canilla.NomApe' sea accesible. Si GetByIdSimpleAsync devuelve la entidad Canilla, esto está bien.
                return (MapToDto(creada, canilla.NomApe ?? "Canilla sin nombre"), null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error durante Rollback en CrearAsync NovedadCanilla."); }
                _logger.LogError(ex, "Error CrearAsync NovedadCanilla para Canilla ID: {IdCanilla}", createDto.IdCanilla);
                return (null, $"Error interno al crear la novedad: {ex.Message}");
            }
            finally
            {
                if (connection.State == ConnectionState.Open) connection.Close();
            }
        }

        public async Task<(bool Exito, string? Error)> ActualizarAsync(int idNovedad, UpdateNovedadCanillaDto updateDto, int idUsuario)
        {
            var existente = await _novedadRepository.GetByIdAsync(idNovedad);
            if (existente == null)
            {
                return (false, "Novedad no encontrada.");
            }

            existente.Detalle = updateDto.Detalle;

            using var connection = _connectionFactory.CreateConnection();
            if (connection is System.Data.Common.DbConnection dbConn && connection.State != ConnectionState.Open)
            {
                await dbConn.OpenAsync();
            }
            else if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            using var transaction = connection.BeginTransaction();
            try
            {
                var actualizado = await _novedadRepository.UpdateAsync(existente, idUsuario, transaction);
                if (!actualizado)
                {
                    transaction.Rollback();
                    return (false, "Error al actualizar la novedad en la base de datos.");
                }

                transaction.Commit();
                _logger.LogInformation("Novedad ID {IdNovedad} actualizada por Usuario ID {UserId}.", idNovedad, idUsuario);
                return (true, null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error durante Rollback en ActualizarAsync NovedadCanilla."); }
                _logger.LogError(ex, "Error ActualizarAsync NovedadCanilla ID: {IdNovedad}", idNovedad);
                return (false, $"Error interno al actualizar la novedad: {ex.Message}");
            }
            finally
            {
                if (connection.State == ConnectionState.Open) connection.Close();
            }
        }

        public async Task<(bool Exito, string? Error)> EliminarAsync(int idNovedad, int idUsuario)
        {
            var existente = await _novedadRepository.GetByIdAsync(idNovedad);
            if (existente == null)
            {
                return (false, "Novedad no encontrada.");
            }

            using var connection = _connectionFactory.CreateConnection();
            if (connection is System.Data.Common.DbConnection dbConn && connection.State != ConnectionState.Open)
            {
                await dbConn.OpenAsync();
            }
            else if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            using var transaction = connection.BeginTransaction();
            try
            {
                var eliminado = await _novedadRepository.DeleteAsync(idNovedad, idUsuario, transaction);
                if (!eliminado)
                {
                    transaction.Rollback();
                    return (false, "Error al eliminar la novedad de la base de datos.");
                }
                transaction.Commit();
                _logger.LogInformation("Novedad ID {IdNovedad} eliminada por Usuario ID {UserId}.", idNovedad, idUsuario);
                return (true, null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error durante Rollback en EliminarAsync NovedadCanilla."); }
                _logger.LogError(ex, "Error EliminarAsync NovedadCanilla ID: {IdNovedad}", idNovedad);
                return (false, $"Error interno al eliminar la novedad: {ex.Message}");
            }
            finally
            {
                if (connection.State == ConnectionState.Open) connection.Close();
            }
        }

        public async Task<IEnumerable<NovedadesCanillasReporteDto>> ObtenerReporteNovedadesAsync(int idEmpresa, DateTime fechaDesde, DateTime fechaHasta)
        {
            // Podría añadir validaciones o lógica de negocio adicional si fuera necesario
            // antes de llamar al repositorio. Por ahora, es una llamada directa.
            try
            {
                return await _novedadRepository.GetReporteNovedadesAsync(idEmpresa, fechaDesde, fechaHasta);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener datos para el reporte de novedades de canillitas. Empresa: {IdEmpresa}, Desde: {FechaDesde}, Hasta: {FechaHasta}", idEmpresa, fechaDesde, fechaHasta);
                // Podría relanzar o devolver una lista vacía con un mensaje de error,
                // dependiendo de cómo quiera manejar los errores en la capa de servicio.
                // Por simplicidad, relanzamos para que el controlador lo maneje.
                throw;
            }
        }

        public async Task<IEnumerable<CanillaGananciaReporteDto>> ObtenerReporteGananciasAsync(int idEmpresa, DateTime fechaDesde, DateTime fechaHasta)
        {
            try
            {
                return await _novedadRepository.GetReporteGananciasAsync(idEmpresa, fechaDesde, fechaHasta);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener datos para el reporte de ganancias de canillitas. Empresa: {IdEmpresa}, Desde: {FechaDesde}, Hasta: {FechaHasta}", idEmpresa, fechaDesde, fechaHasta);
                throw;
            }
        }

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

            return historialData.Select(h => new NovedadCanillaHistorialDto
            {
                Id_Novedad = h.Historial.Id_Novedad,
                Id_Canilla = h.Historial.Id_Canilla,
                Fecha = h.Historial.Fecha,
                Detalle = h.Historial.Detalle,
                Id_Usuario = h.Historial.Id_Usuario,
                NombreUsuarioModifico = h.NombreUsuarioModifico,
                FechaMod = h.Historial.FechaMod,
                TipoMod = h.Historial.TipoMod
                // Si decides traer NombreCanilla desde el repo, mapealo aquí.
            }).ToList();
        }
    }
}