<?php

namespace App\Libraries;

use App\Entities\Promocion;
use App\Models\PromocionDiaModel;

/**
 * Lógica compartida para promociones: vigencia (fechas + día de la semana + stock)
 * y cálculo de precios (COMBO/PRODUCTO/GENERAL para el carrito, ENTRADA para boletería).
 *
 * Se centraliza aquí porque la misma regla de negocio se necesita en tres lugares:
 * la página pública de Promociones (para mostrar el precio ya calculado),
 * el carrito de alimentos (para guardar el precio real al agregar) y
 * la selección de asientos (para aplicar el descuento de "2x1 Martes" u otras
 * promociones de tipo ENTRADA).
 */
class PromoHelper
{
    /**
     * Días (en mayúsculas, ej. 'MARTES') en que aplica la promoción.
     * Array vacío = sin restricción, aplica todos los días.
     */
    public static function diasValidos(int $idPromocion): array
    {
        $model = new PromocionDiaModel();
        return $model->getDiasDePromocion($idPromocion);
    }

    /**
     * Vigencia completa de una promoción: activa, dentro de fecha_inicio/fecha_fin,
     * con stock disponible (si aplica) y, si tiene días configurados, que hoy sea uno de ellos.
     *
     * Devuelve ['vigente' => bool, 'motivo' => string|null] para poder mostrar
     * un mensaje claro al usuario cuando no se puede aplicar.
     */
    public static function verificarVigencia(Promocion $promocion, ?array $dias = null): array
    {
        if (!$promocion->estaActiva()) {
            return ['vigente' => false, 'motivo' => 'Esta promoción ya no está activa.'];
        }

        if (!$promocion->estaEnRangoDeFechas()) {
            return ['vigente' => false, 'motivo' => 'Esta promoción está fuera de su período de vigencia.'];
        }

        if (!$promocion->tieneStockDisponible()) {
            return ['vigente' => false, 'motivo' => 'Esta promoción agotó su stock disponible.'];
        }

        $dias = $dias ?? self::diasValidos($promocion->id_promocion);
        if (!empty($dias) && !in_array(PromocionDiaModel::diaDeHoy(), $dias, true)) {
            $diasTexto = implode(', ', array_map('ucfirst', array_map('strtolower', $dias)));
            return ['vigente' => false, 'motivo' => "Esta promoción solo aplica los días: {$diasTexto}."];
        }

        return ['vigente' => true, 'motivo' => null];
    }

    /**
     * ¿La promoción aplica al tipo de sala de la función indicada?
     * (ej. "Domingo 3D" sólo aplica si $tipoSalaFuncion === '3D').
     * Si la promoción no restringe tipo de sala, siempre es aplicable.
     */
    public static function verificarTipoSala(Promocion $promocion, ?string $tipoSalaFuncion): array
    {
        if ($promocion->aplicaATipoSala($tipoSalaFuncion)) {
            return ['vigente' => true, 'motivo' => null];
        }

        $requerido = $promocion->getTipoSalaRequerido();
        return [
            'vigente' => false,
            'motivo' => "Esta promoción solo aplica a funciones {$requerido}.",
        ];
    }

    /**
     * Calcula el precio final de UNA unidad dado el tipo/valor de descuento de la promoción,
     * a partir de su precio normal. Usado para promociones de tipo ENTRADA (precio de una
     * entrada) y como apoyo para PRODUCTO/GENERAL de un solo ítem.
     */
    public static function calcularPrecioUnidad(Promocion $promocion, float $precioNormal): float
    {
        if ($promocion->tipo_descuento === 'PRECIO_FIJO') {
            return round((float) $promocion->valor_descuento, 2);
        }

        // PORCENTAJE
        $pct = max(0, min(100, (float) $promocion->valor_descuento));
        return round($precioNormal * (1 - $pct / 100), 2);
    }

    /**
     * Calcula el bundle de productos de una promoción COMBO/PRODUCTO/GENERAL,
     * repartiendo el precio promocional entre los productos que lo componen.
     *
     * $productos: filas de PromocionProductoModel::getProductosDePromocion()
     * (cada una con: id_producto, nombre, precio, cantidad, imagen_url, etc.)
     *
     * Devuelve: [
     *   'items' => [ [...producto, 'precio_unitario_promo' => float], ... ],
     *   'precio_normal_total' => float,
     *   'precio_promo_total' => float,
     *   'ahorro' => float,
     * ]
     */
    public static function calcularBundle(Promocion $promocion, array $productos): array
    {
        $precioNormalTotal = 0.0;
        foreach ($productos as $p) {
            $precioNormalTotal += (float) $p['precio'] * max(1, (int) $p['cantidad']);
        }

        if ($promocion->tipo_descuento === 'PRECIO_FIJO') {
            $precioPromoTotal = round((float) $promocion->valor_descuento, 2);
        } else {
            $pct = max(0, min(100, (float) $promocion->valor_descuento));
            $precioPromoTotal = round($precioNormalTotal * (1 - $pct / 100), 2);
        }

        // Repartir el precio promocional entre los productos proporcionalmente a su precio normal,
        // para poder guardar un precio_unitario coherente por cada línea del carrito.
        $items = [];
        $restante = $precioPromoTotal;
        $count = count($productos);

        foreach ($productos as $index => $p) {
            $cantidad = max(1, (int) $p['cantidad']);
            $subtotalNormalItem = (float) $p['precio'] * $cantidad;

            if ($index === $count - 1) {
                $precioUnitarioPromo = $cantidad > 0 ? round($restante / $cantidad, 2) : 0.0;
            } else {
                $shareTotal = $precioNormalTotal > 0
                    ? ($subtotalNormalItem / $precioNormalTotal) * $precioPromoTotal
                    : 0.0;
                $precioUnitarioPromo = round($shareTotal / $cantidad, 2);
                $restante -= round($precioUnitarioPromo * $cantidad, 2);
            }

            $p['precio_unitario_promo'] = max(0, $precioUnitarioPromo);
            $items[] = $p;
        }

        return [
            'items' => $items,
            'precio_normal_total' => round($precioNormalTotal, 2),
            'precio_promo_total' => round($precioPromoTotal, 2),
            'ahorro' => round($precioNormalTotal - $precioPromoTotal, 2),
        ];
    }
}
