using Dapper;
using GestionIntegral.Api.Models.Impresion;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Data.Repositories.Impresion
{
    public class TipoBobinaRepository : ITipoBobinaRepository
    {
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<TipoBobinaRepository> _logger;

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

        public async Task<IEnumerable<TipoBobina>> GetAllAsync(string? denominacionFilter)
        {
            var sqlBuilder = new StringBuilder("SELECT Id_TipoBobina AS IdTipoBobina, Denominacion FROM dbo.bob_dtBobinas WHERE 1=1");
            var parameters = new DynamicParameters();

            if (!string.IsNullOrWhiteSpace(denominacionFilter))
            {
                sqlBuilder.Append(" AND Denominacion LIKE @DenominacionFilter");
                parameters.Add("DenominacionFilter", $"%{denominacionFilter}%");
            }
            sqlBuilder.Append(" ORDER BY Denominacion;");

            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.QueryAsync<TipoBobina>(sqlBuilder.ToString(), parameters);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener todos los Tipos de Bobina. Filtro: {Denominacion}", denominacionFilter);
                return Enumerable.Empty<TipoBobina>();
            }
        }

        public async Task<IEnumerable<TipoBobina>> GetAllDropdownAsync()
        {
            var sqlBuilder = new StringBuilder("SELECT Id_TipoBobina AS IdTipoBobina, Denominacion FROM dbo.bob_dtBobinas WHERE 1=1");
            
            sqlBuilder.Append(" ORDER BY Denominacion;");

            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.QueryAsync<TipoBobina>(sqlBuilder.ToString());
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener todos los Tipos de Bobina.");
                return Enumerable.Empty<TipoBobina>();
            }
        }

        public async Task<TipoBobina?> GetByIdAsync(int id)
        {
            const string sql = "SELECT Id_TipoBobina AS IdTipoBobina, Denominacion FROM dbo.bob_dtBobinas WHERE Id_TipoBobina = @Id";
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                return await connection.QuerySingleOrDefaultAsync<TipoBobina>(sql, new { Id = id });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener Tipo de Bobina por ID: {IdTipoBobina}", id);
                return null;
            }
        }

        public async Task<bool> ExistsByDenominacionAsync(string denominacion, int? excludeId = null)
        {
            var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.bob_dtBobinas WHERE Denominacion = @Denominacion");
            var parameters = new DynamicParameters();
            parameters.Add("Denominacion", denominacion);

            if (excludeId.HasValue)
            {
                sqlBuilder.Append(" AND Id_TipoBobina != @ExcludeId");
                parameters.Add("ExcludeId", excludeId.Value);
            }

            try
            {
                using var connection = _connectionFactory.CreateConnection();
                var count = await connection.ExecuteScalarAsync<int>(sqlBuilder.ToString(), parameters);
                return count > 0;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error en ExistsByDenominacionAsync para Tipo de Bobina con denominación: {Denominacion}", denominacion);
                return true; // Asumir que existe en caso de error
            }
        }

        public async Task<bool> IsInUseAsync(int id)
        {
            // Verificar si el tipo de bobina está referenciado en bob_StockBobinas
            const string sqlCheckStock = "SELECT TOP 1 1 FROM dbo.bob_StockBobinas WHERE Id_TipoBobina = @IdTipoBobina";
            try
            {
                using var connection = _connectionFactory.CreateConnection();
                var inStock = await connection.ExecuteScalarAsync<int?>(sqlCheckStock, new { IdTipoBobina = id });
                return inStock.HasValue && inStock.Value == 1;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error en IsInUseAsync para Tipo de Bobina ID: {IdTipoBobina}", id);
                return true; // Asumir en uso si hay error
            }
        }

        public async Task<TipoBobina?> CreateAsync(TipoBobina nuevoTipoBobina, int idUsuario, IDbTransaction transaction)
        {
            const string sqlInsert = @"
                INSERT INTO dbo.bob_dtBobinas (Denominacion)
                OUTPUT INSERTED.Id_TipoBobina AS IdTipoBobina, INSERTED.Denominacion
                VALUES (@Denominacion);";
            const string sqlInsertHistorico = @"
                INSERT INTO dbo.bob_dtBobinas_H (Id_TipoBobina, Denominacion, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdTipoBobina, @Denominacion, @IdUsuario, @FechaMod, @TipoMod);";

            var connection = transaction.Connection!;

            var insertedTipoBobina = await connection.QuerySingleAsync<TipoBobina>(
                sqlInsert,
                new { nuevoTipoBobina.Denominacion },
                transaction: transaction
            );

            if (insertedTipoBobina == null || insertedTipoBobina.IdTipoBobina <= 0)
            {
                throw new DataException("No se pudo obtener el ID del tipo de bobina insertado.");
            }

            await connection.ExecuteAsync(sqlInsertHistorico, new
            {
                IdTipoBobina = insertedTipoBobina.IdTipoBobina,
                insertedTipoBobina.Denominacion,
                IdUsuario = idUsuario,
                FechaMod = DateTime.Now,
                TipoMod = "Insertada"
            }, transaction: transaction);

            return insertedTipoBobina;
        }

        public async Task<bool> UpdateAsync(TipoBobina tipoBobinaAActualizar, int idUsuario, IDbTransaction transaction)
        {
            var connection = transaction.Connection!;
            var tipoBobinaActual = await connection.QuerySingleOrDefaultAsync<TipoBobina>(
                "SELECT Id_TipoBobina AS IdTipoBobina, Denominacion FROM dbo.bob_dtBobinas WHERE Id_TipoBobina = @Id",
                new { Id = tipoBobinaAActualizar.IdTipoBobina },
                transaction);

            if (tipoBobinaActual == null) throw new KeyNotFoundException($"No se encontró el tipo de bobina con ID {tipoBobinaAActualizar.IdTipoBobina} para actualizar.");

            const string sqlUpdate = "UPDATE dbo.bob_dtBobinas SET Denominacion = @Denominacion WHERE Id_TipoBobina = @IdTipoBobina;";
            const string sqlInsertHistorico = @"
                INSERT INTO dbo.bob_dtBobinas_H (Id_TipoBobina, Denominacion, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdTipoBobina, @DenominacionActual, @IdUsuario, @FechaMod, @TipoMod);";

            await connection.ExecuteAsync(sqlInsertHistorico, new
            {
                IdTipoBobina = tipoBobinaActual.IdTipoBobina,
                DenominacionActual = tipoBobinaActual.Denominacion, // Valor ANTES de actualizar
                IdUsuario = idUsuario,
                FechaMod = DateTime.Now,
                TipoMod = "Modificada"
            }, transaction: transaction);

            var rowsAffected = await connection.ExecuteAsync(sqlUpdate, new
            {
                tipoBobinaAActualizar.Denominacion,
                tipoBobinaAActualizar.IdTipoBobina
            }, transaction: transaction);

            return rowsAffected == 1;
        }

        public async Task<bool> DeleteAsync(int id, int idUsuario, IDbTransaction transaction)
        {
            var connection = transaction.Connection!;
            var tipoBobinaActual = await connection.QuerySingleOrDefaultAsync<TipoBobina>(
                "SELECT Id_TipoBobina AS IdTipoBobina, Denominacion FROM dbo.bob_dtBobinas WHERE Id_TipoBobina = @Id",
                new { Id = id },
                transaction);

            if (tipoBobinaActual == null) throw new KeyNotFoundException($"No se encontró el tipo de bobina con ID {id} para eliminar.");

            const string sqlDelete = "DELETE FROM dbo.bob_dtBobinas WHERE Id_TipoBobina = @Id";
            const string sqlInsertHistorico = @"
                INSERT INTO dbo.bob_dtBobinas_H (Id_TipoBobina, Denominacion, Id_Usuario, FechaMod, TipoMod)
                VALUES (@IdTipoBobina, @Denominacion, @IdUsuario, @FechaMod, @TipoMod);";

            await connection.ExecuteAsync(sqlInsertHistorico, new
            {
                IdTipoBobina = tipoBobinaActual.IdTipoBobina,
                tipoBobinaActual.Denominacion, // Valor ANTES de borrar
                IdUsuario = idUsuario,
                FechaMod = DateTime.Now,
                TipoMod = "Eliminada"
            }, transaction: transaction);

            var rowsAffected = await connection.ExecuteAsync(sqlDelete, new { Id = id }, transaction: transaction);

            return rowsAffected == 1;
        }

        public async Task<IEnumerable<(TipoBobinaHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
            DateTime? fechaDesde, DateTime? fechaHasta,
            int? idUsuarioModifico, string? tipoModificacion,
            int? idTipoBobinaOriginal)
        {
            using var connection = _connectionFactory.CreateConnection();
            var sqlBuilder = new StringBuilder(@"
                SELECT 
                    h.Id_TipoBobina, h.Denominacion,
                    h.Id_Usuario, h.FechaMod, h.TipoMod,
                    u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico
                FROM dbo.bob_dtBobinas_H h  -- Nombre de tabla de historial correcto
                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 (idTipoBobinaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_TipoBobina = @IdTipoBobinaOriginalParam"); parameters.Add("IdTipoBobinaOriginalParam", idTipoBobinaOriginal.Value); }

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

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