using Dapper;
using SIGCM.Domain.Entities;
using SIGCM.Infrastructure.Data;

namespace SIGCM.Infrastructure.Repositories;

public class CashClosingRepository
{
    private readonly IDbConnectionFactory _db;
    
    public CashClosingRepository(IDbConnectionFactory db) => _db = db;

    // Crear un nuevo cierre de caja
    public async Task<int> CreateAsync(CashClosing closing)
    {
        using var conn = _db.CreateConnection();
        var sql = @"
            INSERT INTO CashClosings (
                UserId, ClosingDate, 
                DeclaredCash, DeclaredDebit, DeclaredCredit, DeclaredTransfer,
                SystemCash, SystemDebit, SystemCredit, SystemTransfer,
                CashDifference, DebitDifference, CreditDifference, TransferDifference,
                TotalDeclared, TotalSystem, TotalDifference,
                Notes, HasDiscrepancies, IsApproved
            )
            VALUES (
                @UserId, @ClosingDate,
                @DeclaredCash, @DeclaredDebit, @DeclaredCredit, @DeclaredTransfer,
                @SystemCash, @SystemDebit, @SystemCredit, @SystemTransfer,
                @CashDifference, @DebitDifference, @CreditDifference, @TransferDifference,
                @TotalDeclared, @TotalSystem, @TotalDifference,
                @Notes, @HasDiscrepancies, @IsApproved
            );
            SELECT CAST(SCOPE_IDENTITY() as int);";
        
        return await conn.QuerySingleAsync<int>(sql, closing);
    }

    // Obtener cierres de un usuario en un rango de fechas
    public async Task<IEnumerable<CashClosing>> GetByUserAsync(int userId, DateTime startDate, DateTime endDate)
    {
        using var conn = _db.CreateConnection();
        var sql = @"
            SELECT * FROM CashClosings 
            WHERE UserId = @UserId 
              AND CAST(ClosingDate AS DATE) BETWEEN @StartDate AND @EndDate
            ORDER BY ClosingDate DESC";
        
        return await conn.QueryAsync<CashClosing>(sql, new { UserId = userId, StartDate = startDate.Date, EndDate = endDate.Date });
    }

    // Obtener todos los cierres con discrepancias (para administradores)
    public async Task<IEnumerable<CashClosing>> GetDiscrepanciesAsync()
    {
        using var conn = _db.CreateConnection();
        var sql = @"
            SELECT c.*, u.Username 
            FROM CashClosings c
            JOIN Users u ON c.UserId = u.Id
            WHERE c.HasDiscrepancies = 1 AND c.IsApproved = 0
            ORDER BY c.ClosingDate DESC";
        
        return await conn.QueryAsync<CashClosing>(sql);
    }

    // Aprobar un cierre de caja
    public async Task ApproveAsync(int closingId)
    {
        using var conn = _db.CreateConnection();
        await conn.ExecuteAsync("UPDATE CashClosings SET IsApproved = 1 WHERE Id = @Id", new { Id = closingId });
    }

    // Calcular totales de pagos por método del día actual para un usuario
    public async Task<Dictionary<string, decimal>> GetPaymentTotalsByMethodAsync(int userId, DateTime date)
    {
        using var conn = _db.CreateConnection();
        var sql = @"
            SELECT p.PaymentMethod, SUM(p.Amount + p.Surcharge) as Total
            FROM Payments p
            INNER JOIN Listings l ON p.ListingId = l.Id
            WHERE l.UserId = @UserId 
              AND CAST(l.CreatedAt AS DATE) = @Date
            GROUP BY p.PaymentMethod";
        
        var results = await conn.QueryAsync<dynamic>(sql, new { UserId = userId, Date = date.Date });
        
        var totals = new Dictionary<string, decimal>
        {
            ["Cash"] = 0,
            ["Debit"] = 0,
            ["Credit"] = 0,
            ["Transfer"] = 0
        };
        
        foreach (var row in results)
        {
            string method = row.PaymentMethod;
            decimal total = row.Total;
            if (totals.ContainsKey(method))
            {
                totals[method] = total;
            }
        }
        
        return totals;
    }
}
