using Dapper;
using GestionIntegral.Api.Dtos.Distribucion;
using GestionIntegral.Api.Models.Distribucion;
using Microsoft.Extensions.Logging;
using System; // Añadido para Exception
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Data.Repositories.Distribucion
{
    public class DistribuidorRepository : IDistribuidorRepository
    {
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<DistribuidorRepository> _logger;

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

        public async Task<IEnumerable<(Distribuidor Distribuidor, string? NombreZona)>> GetAllAsync(string? nombreFilter, string? nroDocFilter, bool? soloActivos = true)
        {
            var sqlBuilder = new StringBuilder(@"
                SELECT
                    d.Id_Distribuidor AS IdDistribuidor, d.Nombre, d.Contacto, d.NroDoc, d.Id_Zona AS IdZona,
                    d.Calle, d.Numero, d.Piso, d.Depto, d.Telefono, d.Email, d.Localidad, d.Baja, d.FechaBaja,
                    z.Nombre AS NombreZona
                FROM dbo.dist_dtDistribuidores d
                LEFT JOIN dbo.dist_dtZonas z ON d.Id_Zona = z.Id_Zona
                WHERE 1=1");
            var parameters = new DynamicParameters();

            if (!string.IsNullOrWhiteSpace(nombreFilter))
            {
                sqlBuilder.Append(" AND d.Nombre LIKE @NombreParam");
                parameters.Add("NombreParam", $"%{nombreFilter}%");
            }
            if (!string.IsNullOrWhiteSpace(nroDocFilter))
            {
                sqlBuilder.Append(" AND d.NroDoc LIKE @NroDocParam");
                parameters.Add("NroDocParam", $"%{nroDocFilter}%");
            }
            if (soloActivos.HasValue)
            {
                sqlBuilder.Append(" AND d.Baja = @BajaStatus ");
                parameters.Add("BajaStatus", !soloActivos.Value);
            }
            sqlBuilder.Append(" ORDER BY d.Nombre;");

            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.QueryAsync<Distribuidor, string, (Distribuidor, string?)>(
                    sqlBuilder.ToString(),
                    (dist, zona) => (dist, zona),
                    parameters,
                    splitOn: "NombreZona"
                );
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener todos los Distribuidores. Filtros: Nombre='{NombreFilter}', NroDoc='{NroDocFilter}'", nombreFilter, nroDocFilter);
                return Enumerable.Empty<(Distribuidor, string?)>();
            }
        }

        public async Task<IEnumerable<DistribuidorDropdownDto?>> GetAllDropdownAsync(bool? soloActivos = true)
        {
            var sqlBuilder = new StringBuilder(@"
                SELECT
                    Id_Distribuidor AS IdDistribuidor, Nombre
                FROM dbo.dist_dtDistribuidores
                WHERE 1=1");
            var parameters = new DynamicParameters();
            
            if (soloActivos.HasValue)
            {
                sqlBuilder.Append(" AND Baja = @BajaStatus ");
                parameters.Add("BajaStatus", !soloActivos.Value);
            }

            sqlBuilder.Append(" ORDER BY Nombre;");
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.QueryAsync<DistribuidorDropdownDto>(
                    sqlBuilder.ToString(),
                    parameters
                );
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener todos los Distribuidores.");
                return Enumerable.Empty<DistribuidorDropdownDto>();
            }
        }

        public async Task<(Distribuidor? Distribuidor, string? NombreZona)> GetByIdAsync(int id)
        {
            const string sql = @"
                SELECT
                    d.Id_Distribuidor AS IdDistribuidor, d.Nombre, d.Contacto, d.NroDoc, d.Id_Zona AS IdZona,
                    d.Calle, d.Numero, d.Piso, d.Depto, d.Telefono, d.Email, d.Localidad, d.Baja, d.FechaBaja,
                    z.Nombre AS NombreZona
                FROM dbo.dist_dtDistribuidores d
                LEFT JOIN dbo.dist_dtZonas z ON d.Id_Zona = z.Id_Zona
                WHERE d.Id_Distribuidor = @IdParam";
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                var result = await connection.QueryAsync<Distribuidor, string, (Distribuidor?, string?)>(
                    sql,
                    (dist, zona) => (dist, zona),
                    new { IdParam = id },
                    splitOn: "NombreZona"
                );
                return result.SingleOrDefault();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener Distribuidor por ID: {IdDistribuidor}", id);
                return (null, null);
            }
        }

        public async Task<DistribuidorLookupDto?> ObtenerLookupPorIdAsync(int id)
        {
            const string sql = @"
                SELECT 
                    Id_Distribuidor AS IdDistribuidor, Nombre
                FROM dbo.dist_dtDistribuidores 
                WHERE Id_Distribuidor = @IdParam";
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.QuerySingleOrDefaultAsync<DistribuidorLookupDto>(sql, new { IdParam = id });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener Distribuidor por ID: {IdDistribuidor}", id);
                return null;
            }
        }

        public async Task<Distribuidor?> GetByIdSimpleAsync(int id)
        {
            const string sql = @"
                SELECT 
                    Id_Distribuidor AS IdDistribuidor, Nombre, Contacto, NroDoc, Id_Zona AS IdZona,
                    Calle, Numero, Piso, Depto, Telefono, Email, Localidad, Baja, FechaBaja
                FROM dbo.dist_dtDistribuidores 
                WHERE Id_Distribuidor = @IdParam";
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.QuerySingleOrDefaultAsync<Distribuidor>(sql, new { IdParam = id });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener Distribuidor (simple) por ID: {IdDistribuidor}", id);
                return null;
            }
        }

        public async Task<bool> ExistsByNroDocAsync(string nroDoc, int? excludeIdDistribuidor = null)
        {
            var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.dist_dtDistribuidores WHERE NroDoc = @NroDocParam");
            var parameters = new DynamicParameters();
            parameters.Add("NroDocParam", nroDoc);
            if (excludeIdDistribuidor.HasValue)
            {
                sqlBuilder.Append(" AND Id_Distribuidor != @ExcludeIdParam");
                parameters.Add("ExcludeIdParam", excludeIdDistribuidor.Value);
            }
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.ExecuteScalarAsync<bool>(sqlBuilder.ToString(), parameters);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error en ExistsByNroDocAsync. NroDoc: {NroDoc}", nroDoc);
                return true;
            }
        }
        public async Task<bool> ExistsByNameAsync(string nombre, int? excludeIdDistribuidor = null)
        {
            var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.dist_dtDistribuidores WHERE Nombre = @NombreParam");
            var parameters = new DynamicParameters();
            parameters.Add("NombreParam", nombre);
            if (excludeIdDistribuidor.HasValue)
            {
                sqlBuilder.Append(" AND Id_Distribuidor != @ExcludeIdParam");
                parameters.Add("ExcludeIdParam", excludeIdDistribuidor.Value);
            }
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.ExecuteScalarAsync<bool>(sqlBuilder.ToString(), parameters);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error en ExistsByNameAsync. Nombre: {Nombre}", nombre);
                return true;
            }
        }

        public async Task<bool> IsInUseAsync(int id)
        {
            using var connection = _connectionFactory.CreateConnection();
            string[] checkQueries = {
                "SELECT TOP 1 1 FROM dbo.dist_EntradasSalidas WHERE Id_Distribuidor = @IdParam",
                "SELECT TOP 1 1 FROM dbo.cue_PagosDistribuidor WHERE Id_Distribuidor = @IdParam",
                "SELECT TOP 1 1 FROM dbo.dist_PorcPago WHERE Id_Distribuidor = @IdParam"
            };
            try
            {
                foreach (var query in checkQueries)
                {
                    if (await connection.ExecuteScalarAsync<int?>(query, new { IdParam = id }) == 1) return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error en IsInUseAsync para Distribuidor ID: {IdDistribuidor}", id);
                return true;
            }
        }

        public async Task<Distribuidor?> CreateAsync(Distribuidor nuevoDistribuidor, int idUsuario, IDbTransaction transaction)
        {
            const string sqlInsert = @"
                INSERT INTO dbo.dist_dtDistribuidores (Nombre, Contacto, NroDoc, Id_Zona, Calle, Numero, Piso, Depto, Telefono, Email, Localidad, Baja, FechaBaja)
                OUTPUT INSERTED.Id_Distribuidor AS IdDistribuidor, INSERTED.Nombre, INSERTED.Contacto, INSERTED.NroDoc, INSERTED.Id_Zona AS IdZona,
                       INSERTED.Calle, INSERTED.Numero, INSERTED.Piso, INSERTED.Depto, INSERTED.Telefono, INSERTED.Email, INSERTED.Localidad, INSERTED.Baja, INSERTED.FechaBaja
                VALUES (@Nombre, @Contacto, @NroDoc, @IdZona, @Calle, @Numero, @Piso, @Depto, @Telefono, @Email, @Localidad, 0, NULL);";

            var connection = transaction.Connection!;
            var inserted = await connection.QuerySingleAsync<Distribuidor>(sqlInsert, nuevoDistribuidor, transaction);
            if (inserted == null || inserted.IdDistribuidor == 0) throw new DataException("Error al crear distribuidor o al obtener el ID generado.");

            const string sqlInsertHistorico = @"
                INSERT INTO dbo.dist_dtDistribuidores_H
                    (Id_Distribuidor, Nombre, Contacto, NroDoc, Id_Zona, Calle, Numero, Piso, Depto, Telefono, Email, Localidad, Baja, FechaBaja, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdDistribuidorParam, @NombreParam, @ContactoParam, @NroDocParam, @IdZonaParam, @CalleParam, @NumeroParam, @PisoParam, @DeptoParam, @TelefonoParam, @EmailParam, @LocalidadParam, @BajaParam, @FechaBajaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";

            await connection.ExecuteAsync(sqlInsertHistorico, new
            {
                IdDistribuidorParam = inserted.IdDistribuidor,
                NombreParam = inserted.Nombre,
                ContactoParam = inserted.Contacto,
                NroDocParam = inserted.NroDoc,
                IdZonaParam = inserted.IdZona,
                CalleParam = inserted.Calle,
                NumeroParam = inserted.Numero,
                PisoParam = inserted.Piso,
                DeptoParam = inserted.Depto,
                TelefonoParam = inserted.Telefono,
                EmailParam = inserted.Email,
                LocalidadParam = inserted.Localidad,
                BajaParam = inserted.Baja,
                FechaBajaParam = inserted.FechaBaja,
                IdUsuarioParam = idUsuario,
                FechaModParam = DateTime.Now,
                TipoModParam = "Creado"
            }, transaction);
            return inserted;
        }

        public async Task<bool> UpdateAsync(Distribuidor distribuidorAActualizar, int idUsuario, IDbTransaction transaction)
        {
            var connection = transaction.Connection!;
            var actual = await connection.QuerySingleOrDefaultAsync<Distribuidor>(
                @"SELECT Id_Distribuidor AS IdDistribuidor, Nombre, Contacto, NroDoc, Id_Zona AS IdZona,
                         Calle, Numero, Piso, Depto, Telefono, Email, Localidad, Baja, FechaBaja
                  FROM dbo.dist_dtDistribuidores WHERE Id_Distribuidor = @IdDistribuidorParam",
                new { IdDistribuidorParam = distribuidorAActualizar.IdDistribuidor }, transaction);
            if (actual == null) throw new KeyNotFoundException("Distribuidor no encontrado.");

            const string sqlUpdate = @"
                UPDATE dbo.dist_dtDistribuidores SET
                    Nombre = @Nombre, Contacto = @Contacto, NroDoc = @NroDoc, Id_Zona = @IdZona, Calle = @Calle, Numero = @Numero,
                    Piso = @Piso, Depto = @Depto, Telefono = @Telefono, Email = @Email, Localidad = @Localidad
                WHERE Id_Distribuidor = @IdDistribuidor;";
            const string sqlInsertHistorico = @"
                INSERT INTO dbo.dist_dtDistribuidores_H
                    (Id_Distribuidor, Nombre, Contacto, NroDoc, Id_Zona, Calle, Numero, Piso, Depto, Telefono, Email, Localidad, Baja, FechaBaja, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdDistribuidorParam, @NombreParam, @ContactoParam, @NroDocParam, @IdZonaParam, @CalleParam, @NumeroParam, @PisoParam, @DeptoParam, @TelefonoParam, @EmailParam, @LocalidadParam, @BajaParam, @FechaBajaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";

            await connection.ExecuteAsync(sqlInsertHistorico, new
            {
                IdDistribuidorParam = actual.IdDistribuidor,
                NombreParam = actual.Nombre,
                ContactoParam = actual.Contacto,
                NroDocParam = actual.NroDoc,
                IdZonaParam = actual.IdZona,
                CalleParam = actual.Calle,
                NumeroParam = actual.Numero,
                PisoParam = actual.Piso,
                DeptoParam = actual.Depto,
                TelefonoParam = actual.Telefono,
                EmailParam = actual.Email,
                LocalidadParam = actual.Localidad,
                BajaParam = actual.Baja,
                FechaBajaParam = actual.FechaBaja,
                IdUsuarioParam = idUsuario,
                FechaModParam = DateTime.Now,
                TipoModParam = "Actualizado"
            }, transaction);

            var rowsAffected = await connection.ExecuteAsync(sqlUpdate, distribuidorAActualizar, transaction);
            return rowsAffected == 1;
        }

        public async Task<bool> DeleteAsync(int id, int idUsuario, IDbTransaction transaction)
        {
            var connection = transaction.Connection!;
            var actual = await connection.QuerySingleOrDefaultAsync<Distribuidor>(
                @"SELECT Id_Distribuidor AS IdDistribuidor, Nombre, Contacto, NroDoc, Id_Zona AS IdZona,
                         Calle, Numero, Piso, Depto, Telefono, Email, Localidad, Baja, FechaBaja
                  FROM dbo.dist_dtDistribuidores WHERE Id_Distribuidor = @IdParam", new { IdParam = id }, transaction);
            if (actual == null) throw new KeyNotFoundException("Distribuidor no encontrado.");

            // ... (resto del método DeleteAsync)
            const string sqlDelete = "DELETE FROM dbo.dist_dtDistribuidores WHERE Id_Distribuidor = @IdParam";
            const string sqlInsertHistorico = @"
                 INSERT INTO dbo.dist_dtDistribuidores_H
                    (Id_Distribuidor, Nombre, Contacto, NroDoc, Id_Zona, Calle, Numero, Piso, Depto, Telefono, Email, Localidad, Baja, FechaBaja, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdDistribuidorParam, @NombreParam, @ContactoParam, @NroDocParam, @IdZonaParam, @CalleParam, @NumeroParam, @PisoParam, @DeptoParam, @TelefonoParam, @EmailParam, @LocalidadParam, @BajaParam, @FechaBajaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";

            await connection.ExecuteAsync(sqlInsertHistorico, new
            {
                IdDistribuidorParam = actual.IdDistribuidor,
                NombreParam = actual.Nombre,
                ContactoParam = actual.Contacto,
                NroDocParam = actual.NroDoc,
                IdZonaParam = actual.IdZona,
                CalleParam = actual.Calle,
                NumeroParam = actual.Numero,
                PisoParam = actual.Piso,
                DeptoParam = actual.Depto,
                TelefonoParam = actual.Telefono,
                EmailParam = actual.Email,
                LocalidadParam = actual.Localidad,
                BajaParam = actual.Baja,
                FechaBajaParam = actual.FechaBaja,
                IdUsuarioParam = idUsuario,
                FechaModParam = DateTime.Now,
                TipoModParam = "Eliminado"
            }, transaction);

            var rowsAffected = await connection.ExecuteAsync(sqlDelete, new { IdParam = id }, transaction);
            return rowsAffected == 1;
        }

        public async Task<bool> ToggleBajaAsync(int id, bool darDeBaja, DateTime? fechaBaja, int idUsuario, IDbTransaction transaction)
        {
            var connection = transaction.Connection!;
            var actual = await connection.QuerySingleOrDefaultAsync<Distribuidor>(
                @"SELECT Id_Distribuidor AS IdDistribuidor, Nombre, Contacto, NroDoc, Id_Zona AS IdZona,
                         Calle, Numero, Piso, Depto, Telefono, Email, Localidad, Baja, FechaBaja
                  FROM dbo.dist_dtDistribuidores WHERE Id_Distribuidor = @IdDistribuidorParam",
                new { IdDistribuidorParam = id }, transaction);
            if (actual == null) throw new KeyNotFoundException("Distribuidor no encontrado para dar de baja/alta.");

            const string sqlUpdate = "UPDATE dbo.dist_dtDistribuidores SET Baja = @BajaParam, FechaBaja = @FechaBajaParam WHERE Id_Distribuidor = @IdDistribuidorParam;";
            const string sqlInsertHistorico = @"
                 INSERT INTO dbo.dist_dtDistribuidores_H
                    (Id_Distribuidor, Nombre, Contacto, NroDoc, Id_Zona, Calle, Numero, Piso, Depto, Telefono, Email, Localidad, Baja, FechaBaja, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdDistribuidorParam, @NombreParam, @ContactoParam, @NroDocParam, @IdZonaParam, @CalleParam, @NumeroParam, @PisoParam, @DeptoParam, @TelefonoParam, @EmailParam, @LocalidadParam, @BajaNuevaParam, @FechaBajaNuevaParam, @IdUsuarioParam, @FechaModParam, @TipoModHistParam);";

            await connection.ExecuteAsync(sqlInsertHistorico, new
            {
                IdDistribuidorParam = actual.IdDistribuidor,
                NombreParam = actual.Nombre,
                ContactoParam = actual.Contacto,
                NroDocParam = actual.NroDoc,
                IdZonaParam = actual.IdZona,
                CalleParam = actual.Calle,
                NumeroParam = actual.Numero,
                PisoParam = actual.Piso,
                DeptoParam = actual.Depto,
                TelefonoParam = actual.Telefono,
                EmailParam = actual.Email,
                LocalidadParam = actual.Localidad,
                BajaNuevaParam = darDeBaja,
                FechaBajaNuevaParam = (darDeBaja ? fechaBaja : null),
                IdUsuarioParam = idUsuario,
                FechaModParam = DateTime.Now,
                TipoModHistParam = (darDeBaja ? "Baja" : "Alta")
            }, transaction);

            var rowsAffected = await connection.ExecuteAsync(sqlUpdate, new { BajaParam = darDeBaja, FechaBajaParam = (darDeBaja ? fechaBaja : null), IdDistribuidorParam = id }, transaction);
            return rowsAffected == 1;
        }

        public async Task<IEnumerable<(DistribuidorHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idDistribuidorOriginal)
        {
            using var connection = _connectionFactory.CreateConnection(); // Asumiendo _connectionFactory
            var sqlBuilder = new StringBuilder(@"
            SELECT 
                h.Id_Distribuidor, h.Nombre, h.Contacto, h.NroDoc, h.Id_Zona, h.Calle, h.Numero, 
                h.Piso, h.Depto, h.Telefono, h.Email, h.Localidad,
                h.Id_Usuario, h.FechaMod, h.TipoMod,
                u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico
            FROM dbo.dist_dtDistribuidores_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 (idDistribuidorOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Distribuidor = @IdDistribuidorOriginalParam"); parameters.Add("IdDistribuidorOriginalParam", idDistribuidorOriginal.Value); }

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

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