using SIGCM.Application.DTOs;
using SIGCM.Domain.Entities;
using SIGCM.Infrastructure.Repositories;
using SIGCM.Domain.Interfaces;
using System.Text.RegularExpressions;

namespace SIGCM.Infrastructure.Services;

public class PricingService
{
  private readonly PricingRepository _repo;
  private readonly ICouponRepository _couponRepo;

  public PricingService(PricingRepository repo, ICouponRepository couponRepo)
  {
    _repo = repo;
    _couponRepo = couponRepo;
  }

  public async Task<CalculatePriceResponse> CalculateAsync(CalculatePriceRequest request)
  {
    // 1. Obtener Reglas
    var pricing = await _repo.GetByCategoryIdAsync(request.CategoryId);

    // Si no hay configuración para este rubro, devolvemos 0 o un default seguro
    if (pricing == null) return new CalculatePriceResponse
    {
      TotalPrice = 0,
      Details = "No hay tarifas configuradas para este rubro."
    };

    // 2. Análisis del Texto
    // A. Contar caracteres especiales PRIMERO (antes de borrarlos)
    string escapedSpecialChars = Regex.Escape(pricing.SpecialChars ?? "!");
    int specialCharCount = Regex.Matches(request.Text, $"[{escapedSpecialChars}]").Count;

    // B. Normalizar el texto para contar palabras
    // Reemplazamos los caracteres especiales por espacios para que no cuenten como palabras,
    // ni unan palabras (ej: "Hola!Chau" -> "Hola Chau")
    string cleanText = Regex.Replace(request.Text, $"[{escapedSpecialChars}]", " ");

    // C. Contar palabras reales (ignorando los signos que ahora son espacios)
    var words = cleanText.Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
    int realWordCount = words.Length;

    // 3. Costo Base y Excedente
    decimal currentCost = pricing.BasePrice; // Precio base incluye N palabras

    // ¿Cuántas palabras extra cobramos?
    // Nota: Los caracteres especiales se cobran aparte según tu requerimiento, 
    // o suman al conteo de palabras. Aquí implemento: Se cobran APARTE.

    int extraWords = Math.Max(0, realWordCount - pricing.BaseWordCount);
    decimal extraWordCost = extraWords * pricing.ExtraWordPrice;
    decimal specialCharCost = specialCharCount * pricing.SpecialCharPrice;

    currentCost += extraWordCost + specialCharCost;

    // 4. Estilos (Negrita / Recuadro / Destacado)
    if (request.IsBold) currentCost += pricing.BoldSurcharge;
    if (request.IsFrame) currentCost += pricing.FrameSurcharge;
    
    // Costo Destacado (Hardcoded por ahora o agregar a regla)
    decimal featuredSurcharge = 0;
    if (request.IsFeatured) 
    {
        featuredSurcharge = 500m; // Valor ejemplo por día
        currentCost += featuredSurcharge;
    }

    // 5. Multiplicar por Días
    decimal totalBeforeDiscount = currentCost * request.Days;

    // 6. Motor de Promociones y Cupones
    var promotions = await _repo.GetActivePromotionsAsync();
    decimal totalDiscount = 0;
    List<string> appliedPromos = new();

    // Lógica de promociones automáticas
    foreach (var promo in promotions)
    {
      if (promo.CategoryId.HasValue && promo.CategoryId != request.CategoryId) continue;
      if (promo.MinDays > 0 && request.Days < promo.MinDays) continue;
      
      // Verificar si aplica según el día de la semana
      if (!string.IsNullOrEmpty(promo.DaysOfWeek))
      {
        var targetDays = promo.DaysOfWeek.Split(',').Select(int.Parse).ToList();
        bool hitsDay = false;
        for (int i = 0; i < request.Days; i++)
        {
           var currentDay = (int)request.StartDate.AddDays(i).DayOfWeek;
           if (targetDays.Contains(currentDay)) hitsDay = true;
        }
        if (!hitsDay) continue;
      }

      if (promo.DiscountPercentage > 0)
      {
        decimal discountVal = totalBeforeDiscount * (promo.DiscountPercentage / 100m);
        totalDiscount += discountVal;
        appliedPromos.Add($"{promo.Name} (-{promo.DiscountPercentage}%)");
      }
      if (promo.DiscountFixedAmount > 0)
      {
        totalDiscount += promo.DiscountFixedAmount;
        appliedPromos.Add($"{promo.Name} (-${promo.DiscountFixedAmount})");
      }
    }

    // Lógica de Cupón
    if (!string.IsNullOrEmpty(request.CouponCode))
    {
        var coupon = await _couponRepo.GetByCodeAsync(request.CouponCode);
        if (coupon != null && (!coupon.ExpiryDate.HasValue || coupon.ExpiryDate > DateTime.UtcNow) 
            && (!coupon.MaxUsages.HasValue || coupon.UsageCount < coupon.MaxUsages))
        {
            if (coupon.DiscountType == "Percentage")
            {
                decimal val = totalBeforeDiscount * (coupon.DiscountValue / 100m);
                totalDiscount += val;
                appliedPromos.Add($"CUPÓN {coupon.Code} (-{coupon.DiscountValue}%)");
            }
            else if (coupon.DiscountType == "Fixed")
            {
                totalDiscount += coupon.DiscountValue;
                appliedPromos.Add($"CUPÓN {coupon.Code} (-${coupon.DiscountValue})");
            }
        }
    }

    return new CalculatePriceResponse
    {
      TotalPrice = Math.Max(0, totalBeforeDiscount - totalDiscount),
      BaseCost = pricing.BasePrice * request.Days,
      ExtraCost = (extraWordCost + specialCharCost) * request.Days,
      Surcharges = ((request.IsBold ? pricing.BoldSurcharge : 0) + (request.IsFrame ? pricing.FrameSurcharge : 0) + (request.IsFeatured ? featuredSurcharge : 0)) * request.Days,
      Discount = totalDiscount,
      WordCount = realWordCount,
      SpecialCharCount = specialCharCount,
      Details = $"Tarifa Diaria: ${currentCost} x {request.Days} días. (Extras diarios: ${extraWordCost + specialCharCost}). {string.Join(", ", appliedPromos)}",
      AppliedPromotion = string.Join(", ", appliedPromos)
    };
  }
}