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

namespace SIGCM.Infrastructure.Repositories;

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

  // 1. Verificar si el usuario ya tiene una caja abierta
  public async Task<CashSession?> GetActiveSessionAsync(int userId)
  {
    using var conn = _db.CreateConnection();
    var sql = "SELECT * FROM CashSessions WHERE UserId = @userId AND Status = 'Open'";
    return await conn.QuerySingleOrDefaultAsync<CashSession>(sql, new { userId });
  }

  // 2. Abrir Caja
  public async Task<int> OpenSessionAsync(int userId, decimal openingBalance)
  {
    using var conn = _db.CreateConnection();
    var sql = @"
            INSERT INTO CashSessions (UserId, OpeningBalance, Status, OpeningDate)
            VALUES (@userId, @openingBalance, 'Open', GETUTCDATE());
            SELECT CAST(SCOPE_IDENTITY() as int);";
    return await conn.QuerySingleAsync<int>(sql, new { userId, openingBalance });
  }

  // 3. Obtener totales del sistema para un rango de tiempo (El turno)
  public async Task<dynamic> GetSystemTotalsAsync(int userId, DateTime start, DateTime end)
  {
    using var conn = _db.CreateConnection();
    var sql = @"
            SELECT 
                SUM(CASE WHEN PaymentMethod = 'Cash' THEN Amount + Surcharge ELSE 0 END) as Cash,
                SUM(CASE WHEN PaymentMethod IN ('Debit', 'Credit') THEN Amount + Surcharge ELSE 0 END) as Cards,
                SUM(CASE WHEN PaymentMethod = 'Transfer' THEN Amount + Surcharge ELSE 0 END) as Transfers
            FROM Payments p
            JOIN Listings l ON p.ListingId = l.Id
            WHERE l.UserId = @userId AND p.PaymentDate BETWEEN @start AND @end";

    return await conn.QuerySingleAsync<dynamic>(sql, new { userId, start, end });
  }

  // 4. Cerrar Caja (Arqueo Ciego)
  public async Task CloseSessionAsync(CashSession session)
  {
    using var conn = _db.CreateConnection();
    var sql = @"
            UPDATE CashSessions 
            SET Status = 'PendingValidation',
                ClosingDate = GETUTCDATE(),
                DeclaredCash = @DeclaredCash,
                DeclaredCards = @DeclaredCards,
                DeclaredTransfers = @DeclaredTransfers,
                SystemExpectedCash = @SystemExpectedCash,
                SystemExpectedCards = @SystemExpectedCards,
                SystemExpectedTransfers = @SystemExpectedTransfers,
                TotalDifference = @TotalDifference
            WHERE Id = @Id";
    await conn.ExecuteAsync(sql, session);
  }

  // Obtener todas las sesiones que requieren validación (para el Supervisor)
  public async Task<IEnumerable<CashSession>> GetPendingValidationAsync()
  {
    using var conn = _db.CreateConnection();
    var sql = @"
        SELECT cs.*, u.Username 
        FROM CashSessions cs
        JOIN Users u ON cs.UserId = u.Id
        WHERE cs.Status = 'PendingValidation'
        ORDER BY cs.ClosingDate DESC";
    return await conn.QueryAsync<CashSession>(sql);
  }

  // Validar / Liquidar una sesión
  public async Task ValidateSessionAsync(int sessionId, int adminId, string notes)
  {
    using var conn = _db.CreateConnection();
    var sql = @"
        UPDATE CashSessions 
        SET Status = 'Closed', 
            ValidatedByUserId = @adminId, 
            ValidationDate = GETUTCDATE(), 
            ValidationNotes = @notes
        WHERE Id = @sessionId";
    await conn.ExecuteAsync(sql, new { sessionId, adminId, notes });
  }

  public async Task<CashSession?> GetSessionDetailAsync(int sessionId)
  {
    using var conn = _db.CreateConnection();
    var sql = @"
        SELECT cs.*, u.Username, v.Username as ValidatorName
        FROM CashSessions cs
        JOIN Users u ON cs.UserId = u.Id
        LEFT JOIN Users v ON cs.ValidatedByUserId = v.Id
        WHERE cs.Id = @sessionId";
    return await conn.QuerySingleOrDefaultAsync<CashSession>(sql, new { sessionId });
  }
}