using Dapper;
using GestionIntegral.Api.Models.Distribucion;
using Microsoft.Extensions.Configuration; // Para IConfiguration
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using Microsoft.Data.SqlClient; // O el proveedor de tu BD
using System.Linq;
using System.Threading.Tasks;
using GestionIntegral.Api.Dtos.Reportes;
using System.Text;

namespace GestionIntegral.Api.Data.Repositories.Distribucion
{
    public class NovedadCanillaRepository : INovedadCanillaRepository
    {
        private readonly DbConnectionFactory _connectionFactory; // Inyecta tu DbConnectionFactory
        private readonly ILogger<NovedadCanillaRepository> _logger;

        public NovedadCanillaRepository(DbConnectionFactory connectionFactory, ILogger<NovedadCanillaRepository> logger)
        {
            _connectionFactory = connectionFactory;
            _logger = logger;
        }

        private async Task LogHistorialAsync(NovedadCanilla novedadOriginal, int idUsuario, string tipoMod, IDbConnection connection, IDbTransaction? transaction)
        {
            var historial = new NovedadCanillaHistorial
            {
                IdNovedad = novedadOriginal.IdNovedad,
                IdCanilla = novedadOriginal.IdCanilla,
                Fecha = novedadOriginal.Fecha,
                Detalle = novedadOriginal.Detalle,
                IdUsuario = idUsuario,
                FechaMod = DateTime.Now,
                TipoMod = tipoMod
            };
            var sqlHistorial = @"
                INSERT INTO dbo.dist_dtNovedadesCanillas_H 
                    (Id_Novedad, Id_Canilla, Fecha, Detalle, Id_Usuario, FechaMod, TipoMod)
                VALUES 
                    (@IdNovedad, @IdCanilla, @Fecha, @Detalle, @IdUsuario, @FechaMod, @TipoMod);";
            await connection.ExecuteAsync(sqlHistorial, historial, transaction);
        }

        public async Task<IEnumerable<(NovedadCanilla Novedad, string NombreCanilla)>> GetByCanillaAsync(int idCanilla, DateTime? fechaDesde, DateTime? fechaHasta)
        {
            using var connection = _connectionFactory.CreateConnection();
            var sqlBuilder = new System.Text.StringBuilder(@"
                SELECT 
                    n.Id_Novedad AS IdNovedad, 
                    n.Id_Canilla AS IdCanilla, 
                    n.Fecha, 
                    n.Detalle,
                    c.NomApe AS NombreCanilla
                FROM dbo.dist_dtNovedadesCanillas n
                JOIN dbo.dist_dtCanillas c ON n.Id_Canilla = c.Id_Canilla
                WHERE n.Id_Canilla = @IdCanilla");

            var parameters = new DynamicParameters();
            parameters.Add("IdCanilla", idCanilla);

            if (fechaDesde.HasValue)
            {
                sqlBuilder.Append(" AND n.Fecha >= @FechaDesde");
                parameters.Add("FechaDesde", fechaDesde.Value.Date); // Solo fecha, sin hora
            }
            if (fechaHasta.HasValue)
            {
                sqlBuilder.Append(" AND n.Fecha <= @FechaHasta");
                // Para incluir todo el día de fechaHasta
                parameters.Add("FechaHasta", fechaHasta.Value.Date.AddDays(1).AddTicks(-1));
            }
            sqlBuilder.Append(" ORDER BY n.Fecha DESC, n.Id_Novedad DESC;");

            var result = await connection.QueryAsync<NovedadCanilla, string, (NovedadCanilla, string)>(
                sqlBuilder.ToString(),
                (novedad, nombreCanilla) => (novedad, nombreCanilla),
                parameters,
                splitOn: "NombreCanilla"
            );
            return result;
        }


        public async Task<NovedadCanilla?> GetByIdAsync(int idNovedad)
        {
            using var connection = _connectionFactory.CreateConnection();
            var sql = "SELECT Id_Novedad AS IdNovedad, Id_Canilla AS IdCanilla, Fecha, Detalle FROM dbo.dist_dtNovedadesCanillas WHERE Id_Novedad = @IdNovedad;";
            return await connection.QuerySingleOrDefaultAsync<NovedadCanilla>(sql, new { IdNovedad = idNovedad });
        }

        public async Task<bool> ExistsByCanillaAndFechaAsync(int idCanilla, DateTime fecha, int? excludeIdNovedad = null)
        {
            using var connection = _connectionFactory.CreateConnection();
            var sqlBuilder = new System.Text.StringBuilder("SELECT COUNT(1) FROM dbo.dist_dtNovedadesCanillas WHERE Id_Canilla = @IdCanilla AND Fecha = @Fecha");
            var parameters = new DynamicParameters();
            parameters.Add("IdCanilla", idCanilla);
            parameters.Add("Fecha", fecha.Date); // Comparar solo la fecha

            if (excludeIdNovedad.HasValue)
            {
                sqlBuilder.Append(" AND Id_Novedad != @ExcludeIdNovedad");
                parameters.Add("ExcludeIdNovedad", excludeIdNovedad.Value);
            }

            var count = await connection.ExecuteScalarAsync<int>(sqlBuilder.ToString(), parameters);
            return count > 0;
        }

        public async Task<NovedadCanilla?> CreateAsync(NovedadCanilla novedad, int idUsuario, IDbTransaction? transaction = null)
        {
            var sql = @"
                INSERT INTO dbo.dist_dtNovedadesCanillas (Id_Canilla, Fecha, Detalle)
                VALUES (@IdCanilla, @Fecha, @Detalle);
                SELECT CAST(SCOPE_IDENTITY() as int);";

            IDbConnection conn = transaction?.Connection ?? _connectionFactory.CreateConnection();
            bool manageConnection = transaction == null; // Solo gestionar si no hay transacción externa

            try
            {
                if (manageConnection && conn.State != ConnectionState.Open) await (conn as SqlConnection)!.OpenAsync();

                var newId = await conn.QuerySingleAsync<int>(sql, novedad, transaction);
                novedad.IdNovedad = newId;
                await LogHistorialAsync(novedad, idUsuario, "Insertada", conn, transaction);
                return novedad;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al crear NovedadCanilla para Canilla ID: {IdCanilla}", novedad.IdCanilla);
                return null;
            }
            finally
            {
                if (manageConnection && conn.State == ConnectionState.Open) conn.Close();
            }
        }

        public async Task<bool> UpdateAsync(NovedadCanilla novedad, int idUsuario, IDbTransaction? transaction = null)
        {
            var novedadOriginal = await GetByIdAsync(novedad.IdNovedad); // Necesitamos el estado original para el log
            if (novedadOriginal == null) return false; // No se encontró

            var sql = @"
                UPDATE dbo.dist_dtNovedadesCanillas SET
                    Detalle = @Detalle 
                    -- No se permite cambiar IdCanilla ni Fecha de una novedad existente
                WHERE Id_Novedad = @IdNovedad;";

            IDbConnection conn = transaction?.Connection ?? _connectionFactory.CreateConnection();
            bool manageConnection = transaction == null;

            try
            {
                if (manageConnection && conn.State != ConnectionState.Open) await (conn as SqlConnection)!.OpenAsync();

                await LogHistorialAsync(novedadOriginal, idUsuario, "Modificada", conn, transaction); // Log con datos ANTES de actualizar
                var affectedRows = await conn.ExecuteAsync(sql, novedad, transaction);
                return affectedRows > 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al actualizar NovedadCanilla ID: {IdNovedad}", novedad.IdNovedad);
                return false;
            }
            finally
            {
                if (manageConnection && conn.State == ConnectionState.Open) conn.Close();
            }
        }

        public async Task<bool> DeleteAsync(int idNovedad, int idUsuario, IDbTransaction? transaction = null)
        {
            var novedadOriginal = await GetByIdAsync(idNovedad);
            if (novedadOriginal == null) return false;

            var sql = "DELETE FROM dbo.dist_dtNovedadesCanillas WHERE Id_Novedad = @IdNovedad;";

            IDbConnection conn = transaction?.Connection ?? _connectionFactory.CreateConnection();
            bool manageConnection = transaction == null;

            try
            {
                if (manageConnection && conn.State != ConnectionState.Open) await (conn as SqlConnection)!.OpenAsync();

                await LogHistorialAsync(novedadOriginal, idUsuario, "Eliminada", conn, transaction);
                var affectedRows = await conn.ExecuteAsync(sql, new { IdNovedad = idNovedad }, transaction);
                return affectedRows > 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al eliminar NovedadCanilla ID: {IdNovedad}", idNovedad);
                return false;
            }
            finally
            {
                if (manageConnection && conn.State == ConnectionState.Open) conn.Close();
            }
        }

        public async Task<IEnumerable<NovedadesCanillasReporteDto>> GetReporteNovedadesAsync(int idEmpresa, DateTime fechaDesde, DateTime fechaHasta)
        {
            using var connection = _connectionFactory.CreateConnection();
            var parameters = new
            {
                idEmpresa,
                fechaDesde = fechaDesde.Date, // Enviar solo la fecha
                fechaHasta = fechaHasta.Date.AddDays(1).AddTicks(-1) // Para incluir todo el día hasta las 23:59:59.999...
            };
            // El nombre del SP en el archivo es SP_DistCanillasNovedades
            return await connection.QueryAsync<NovedadesCanillasReporteDto>(
                "dbo.SP_DistCanillasNovedades", // Asegúrate que el nombre del SP sea exacto
                parameters,
                commandType: CommandType.StoredProcedure
            );
        }

        public async Task<IEnumerable<CanillaGananciaReporteDto>> GetReporteGananciasAsync(int idEmpresa, DateTime fechaDesde, DateTime fechaHasta)
        {
            using var connection = _connectionFactory.CreateConnection();
            var parameters = new
            {
                idEmpresa,
                fechaDesde = fechaDesde.Date,
                fechaHasta = fechaHasta.Date // El SP SP_DistCanillasGanancias maneja el rango inclusivo directamente
            };
            return await connection.QueryAsync<CanillaGananciaReporteDto>(
                "dbo.SP_DistCanillasGanancias", // Nombre del SP
                parameters,
                commandType: CommandType.StoredProcedure
            );
        }
        public async Task<IEnumerable<(NovedadCanillaHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idNovedadOriginal)
        {
            using var connection = _connectionFactory.CreateConnection();
            var sqlBuilder = new StringBuilder(@"
            SELECT 
                h.Id_Novedad, h.Id_Canilla, h.Fecha, h.Detalle,
                h.Id_Usuario, h.FechaMod, h.TipoMod,
                u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico
            FROM dbo.dist_dtNovedadesCanillas_H h
            JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id
            WHERE 1=1");

            var parameters = new DynamicParameters();

            if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); }
            if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); }
            if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); }
            if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); }
            if (idNovedadOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Novedad = @IdNovedadOriginalParam"); parameters.Add("IdNovedadOriginalParam", idNovedadOriginal.Value); }

            sqlBuilder.Append(" ORDER BY h.FechaMod DESC;");

            try
            {
                var result = await connection.QueryAsync<NovedadCanillaHistorico, string, (NovedadCanillaHistorico, string)>(
                    sqlBuilder.ToString(),
                    (hist, userName) => (hist, userName),
                    parameters,
                    splitOn: "NombreUsuarioModifico"
                );
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener historial de Novedades de Canillitas.");
                return Enumerable.Empty<(NovedadCanillaHistorico, string)>();
            }
        }
    }
}