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

namespace SIGCM.Infrastructure.Repositories;

public class ClaimRepository : IClaimRepository
{
  private readonly IDbConnectionFactory _db;
  public ClaimRepository(IDbConnectionFactory db) => _db = db;

  public async Task<int> CreateAsync(Claim claim)
  {
    using var conn = _db.CreateConnection();
    var sql = @"
            INSERT INTO Claims (ListingId, CreatedByUserId, ClaimType, Description, Status)
            VALUES (@ListingId, @CreatedByUserId, @ClaimType, @Description, @Status);
            SELECT CAST(SCOPE_IDENTITY() as int);";
    return await conn.QuerySingleAsync<int>(sql, claim);
  }

  public async Task<IEnumerable<Claim>> GetByListingIdAsync(int listingId)
  {
    using var conn = _db.CreateConnection();
    var sql = @"
            SELECT c.*, u.Username as CreatedByUsername, r.Username as ResolvedByUsername
            FROM Claims c
            JOIN Users u ON c.CreatedByUserId = u.Id
            LEFT JOIN Users r ON c.ResolvedByUserId = r.Id
            WHERE c.ListingId = @ListingId
            ORDER BY c.CreatedAt DESC";
    return await conn.QueryAsync<Claim>(sql, new { ListingId = listingId });
  }

  public async Task<IEnumerable<Claim>> GetAllActiveAsync()
  {
    using var conn = _db.CreateConnection();
    var sql = @"
            SELECT c.*, u.Username as CreatedByUsername 
            FROM Claims c
            JOIN Users u ON c.CreatedByUserId = u.Id
            WHERE c.Status <> 'Resolved'
            ORDER BY c.CreatedAt ASC";
    return await conn.QueryAsync<Claim>(sql);
  }

  public async Task UpdateStatusAsync(int claimId, string status, ResolveRequest request, int resolvedByUserId)
  {
    using var conn = _db.CreateConnection();
    conn.Open();
    using var transaction = conn.BeginTransaction();

    try
    {
      // 1. Buscamos el aviso y sus valores ACTUALES antes de cambiarlos
      var currentDataSql = @"
            SELECT l.Id, l.PrintText, l.PrintStartDate, l.PrintDaysCount 
            FROM Listings l
            JOIN Claims c ON c.ListingId = l.Id
            WHERE c.Id = @claimId";

      var oldValues = await conn.QuerySingleOrDefaultAsync<dynamic>(currentDataSql, new { claimId }, transaction);

      if (oldValues == null)
        throw new InvalidOperationException("Reclamo o Aviso no encontrado.");

      // 2. Construimos un string descriptivo del estado anterior
      string originalValuesLog = $"Texto: {oldValues.PrintText} | " +
                                 $"Fecha: {oldValues.PrintStartDate:yyyy-MM-dd} | " +
                                 $"Días: {oldValues.PrintDaysCount}";

      // 3. Actualizar el Reclamo (Guardando los valores originales)
      var sqlClaim = @"
            UPDATE Claims 
            SET Status = @status, 
                SolutionDescription = @solution, 
                OriginalValues = @originalValuesLog,
                ResolvedAt = GETUTCDATE(), 
                ResolvedByUserId = @resolvedByUserId
            WHERE Id = @claimId";

      await conn.ExecuteAsync(sqlClaim, new
      {
        claimId,
        status,
        solution = request.Solution,
        originalValuesLog,
        resolvedByUserId
      }, transaction);

      // 4. Aplicar los nuevos ajustes técnicos al aviso
      if (!string.IsNullOrEmpty(request.NewPrintText) || request.NewStartDate.HasValue || request.NewDaysCount.HasValue)
      {
        var sqlListing = @"
                UPDATE Listings 
                SET PrintText = ISNULL(@NewPrintText, PrintText),
                    PrintStartDate = ISNULL(@NewStartDate, PrintStartDate),
                    PrintDaysCount = ISNULL(@NewDaysCount, PrintDaysCount)
                WHERE Id = @listingId";

        await conn.ExecuteAsync(sqlListing, new
        {
          listingId = (int)oldValues.Id,
          request.NewPrintText,
          request.NewStartDate,
          request.NewDaysCount
        }, transaction);
      }

      transaction.Commit();
    }
    catch
    {
      transaction.Rollback();
      throw;
    }
  }

  public async Task<Claim?> GetByIdAsync(int id)
  {
    using var conn = _db.CreateConnection();
    return await conn.QuerySingleOrDefaultAsync<Claim>("SELECT * FROM Claims WHERE Id = @Id", new { Id = id });
  }
}