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

namespace SIGCM.Infrastructure.Services;

public class PricingService
{
  private readonly PricingRepository _repo;

  public PricingService(PricingRepository repo)
  {
    _repo = repo;
  }

  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) - Se suman al precio unitario diario
    if (request.IsBold) currentCost += pricing.BoldSurcharge;
    if (request.IsFrame) currentCost += pricing.FrameSurcharge;

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

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

    foreach (var promo in promotions)
    {
      // Filtro por Categoría
      if (promo.CategoryId.HasValue && promo.CategoryId != request.CategoryId) continue;

      // Filtro por Cantidad de Días
      if (promo.MinDays > 0 && request.Days < promo.MinDays) continue;

      // Filtro por Días de la Semana
      if (!string.IsNullOrEmpty(promo.DaysOfWeek))
      {
        var targetDays = promo.DaysOfWeek.Split(',').Select(int.Parse).ToList();
        bool hitsDay = false;
        // Revisamos cada día que durará el aviso
        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; // No cae en ningún día de promo
      }

      // Aplicar Descuento
      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})");
      }
    }

    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.Days,
      Discount = totalDiscount,
      WordCount = realWordCount,
      SpecialCharCount = specialCharCount,
      Details = $"Tarifa Diaria: ${currentCost} x {request.Days} días. (Extras diarios: ${extraWordCost + specialCharCost})"
    };
  }
}