using Dapper;
using GestionIntegral.Api.Models.Suscripciones;
using System.Data;
using System.Text;

namespace GestionIntegral.Api.Data.Repositories.Suscripciones
{
    public class AjusteRepository : IAjusteRepository
    {
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<AjusteRepository> _logger;

        public AjusteRepository(DbConnectionFactory factory, ILogger<AjusteRepository> logger)
        {
            _connectionFactory = factory;
            _logger = logger;
        }

        public async Task<bool> UpdateAsync(Ajuste ajuste, IDbTransaction transaction)
        {
            const string sql = @"
                UPDATE dbo.susc_Ajustes SET
                    IdEmpresa = @IdEmpresa,
                    FechaAjuste = @FechaAjuste,
                    TipoAjuste = @TipoAjuste,
                    Monto = @Monto,
                    Motivo = @Motivo
                WHERE IdAjuste = @IdAjuste AND Estado = 'Pendiente';";
            if (transaction?.Connection == null)
            {
                throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
            }
            var rows = await transaction.Connection.ExecuteAsync(sql, ajuste, transaction);
            return rows == 1;
        }

        public async Task<Ajuste?> CreateAsync(Ajuste nuevoAjuste, IDbTransaction transaction)
        {
            const string sql = @"
                INSERT INTO dbo.susc_Ajustes (IdSuscriptor, IdEmpresa, FechaAjuste, TipoAjuste, Monto, Motivo, Estado, IdUsuarioAlta, FechaAlta)
                OUTPUT INSERTED.*
                VALUES (@IdSuscriptor, @IdEmpresa, @FechaAjuste, @TipoAjuste, @Monto, @Motivo, 'Pendiente', @IdUsuarioAlta, GETDATE());";
            if (transaction?.Connection == null)
            {
                throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
            }
            return await transaction.Connection.QuerySingleOrDefaultAsync<Ajuste>(sql, nuevoAjuste, transaction);
        }

        public async Task<IEnumerable<Ajuste>> GetAjustesPorSuscriptorAsync(int idSuscriptor, DateTime? fechaDesde, DateTime? fechaHasta)
        {
            var sqlBuilder = new StringBuilder("SELECT * FROM dbo.susc_Ajustes WHERE IdSuscriptor = @IdSuscriptor");
            var parameters = new DynamicParameters();
            parameters.Add("IdSuscriptor", idSuscriptor);

            if (fechaDesde.HasValue)
            {
                sqlBuilder.Append(" AND FechaAjuste >= @FechaDesde");
                parameters.Add("FechaDesde", fechaDesde.Value.Date);
            }
            if (fechaHasta.HasValue)
            {
                sqlBuilder.Append(" AND FechaAjuste <= @FechaHasta");
                parameters.Add("FechaHasta", fechaHasta.Value.Date);
            }

            sqlBuilder.Append(" ORDER BY FechaAlta DESC;");

            using var connection = _connectionFactory.CreateConnection();
            return await connection.QueryAsync<Ajuste>(sqlBuilder.ToString(), parameters);
        }

        public async Task<IEnumerable<Ajuste>> GetAjustesPendientesHastaFechaAsync(int idSuscriptor, int idEmpresa, DateTime fechaHasta, IDbTransaction transaction)
        {
            const string sql = @"
                SELECT * FROM dbo.susc_Ajustes 
                WHERE IdSuscriptor = @IdSuscriptor 
                AND IdEmpresa = @IdEmpresa
                AND Estado = 'Pendiente'
                AND FechaAjuste <= @FechaHasta;";
            
            if (transaction?.Connection == null)
            {
                throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
            }
            return await transaction.Connection.QueryAsync<Ajuste>(sql, new { idSuscriptor, idEmpresa, FechaHasta = fechaHasta }, transaction);
        }

        public async Task<bool> MarcarAjustesComoAplicadosAsync(IEnumerable<int> idsAjustes, int idFactura, IDbTransaction transaction)
        {
            if (!idsAjustes.Any()) return true;

            const string sql = @"
                UPDATE dbo.susc_Ajustes SET
                    Estado = 'Aplicado',
                    IdFacturaAplicado = @IdFactura
                WHERE IdAjuste IN @IdsAjustes;";

            if (transaction?.Connection == null)
            {
                throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
            }
            var rowsAffected = await transaction.Connection.ExecuteAsync(sql, new { IdsAjustes = idsAjustes, IdFactura = idFactura }, transaction);
            return rowsAffected == idsAjustes.Count();
        }

        public async Task<Ajuste?> GetByIdAsync(int idAjuste)
        {
            const string sql = "SELECT * FROM dbo.susc_Ajustes WHERE IdAjuste = @IdAjuste;";
            using var connection = _connectionFactory.CreateConnection();
            return await connection.QuerySingleOrDefaultAsync<Ajuste>(sql, new { idAjuste });
        }

        public async Task<bool> AnularAjusteAsync(int idAjuste, int idUsuario, IDbTransaction transaction)
        {
            const string sql = @"
                UPDATE dbo.susc_Ajustes SET
                    Estado = 'Anulado',
                    IdUsuarioAnulo = @IdUsuario,
                    FechaAnulacion = GETDATE()
                WHERE IdAjuste = @IdAjuste AND Estado = 'Pendiente';";

            if (transaction?.Connection == null)
            {
                throw new ArgumentNullException(nameof(transaction.Connection), "La conexión de la transacción no puede ser nula.");
            }

            var rows = await transaction.Connection.ExecuteAsync(sql, new { IdAjuste = idAjuste, IdUsuario = idUsuario }, transaction);
            return rows == 1;
        }

        public async Task<IEnumerable<Ajuste>> GetAjustesPorIdFacturaAsync(int idFactura)
        {
            const string sql = "SELECT * FROM dbo.susc_Ajustes WHERE IdFacturaAplicado = @IdFactura;";
            using var connection = _connectionFactory.CreateConnection();
            return await connection.QueryAsync<Ajuste>(sql, new { IdFactura = idFactura });
        }
    }
}