<?php

declare(strict_types=1);

namespace App\Services\Dashboard;

use App\Models\DashboardSnapshot;
use Carbon\Carbon;

/**
 * Calcula la tendencia semana-sobre-semana de las métricas de stock del dashboard
 * admin, comparando el valor vivo contra el snapshot más reciente con
 * snapshot_date <= hoy-7. Devuelve null durante la rampa (sin snapshot en ventana)
 * o si la base es 0.
 */
class DashboardTrendService
{
    /** Polaridad: true = "más alto es mejor" (verde al subir). */
    private const POLARITY = [
        'business_total' => true,
        'cash_base' => true,
        'por_cobrar' => true,
        'delinquency_rate' => false,
    ];

    /**
     * @param  array<string, float>  $live  claves: business_total, cash_base, por_cobrar, delinquency_rate
     * @return array<string, array{pct: float, good: bool}|null>
     */
    public function trendsFor(int $companyId, array $live): array
    {
        $snapshot = DashboardSnapshot::query()
            ->where('company_id', $companyId)
            ->where('snapshot_date', '<=', Carbon::today()->subDays(7)->toDateString())
            ->orderByDesc('snapshot_date')
            ->first();

        $out = [];
        foreach ($live as $key => $todayValue) {
            $past = $snapshot?->{$key};
            $out[$key] = $this->delta(
                $past === null ? null : (float) $past,
                (float) $todayValue,
                self::POLARITY[$key] ?? true,
            );
        }

        return $out;
    }

    /**
     * @return array{pct: float, good: bool}|null
     */
    private function delta(?float $past, float $today, bool $higherIsBetter): ?array
    {
        if ($past === null || $past == 0.0) {
            return null;
        }

        $pct = round((($today - $past) / $past) * 100, 1);
        $good = $higherIsBetter ? ($pct >= 0) : ($pct <= 0);

        return ['pct' => $pct, 'good' => $good];
    }

    /**
     * Polaridad conjunta.
     *
     * `por_cobrar` creciente se pintaba verde siempre. Pero una cartera que
     * crece mientras la caja baja o la mora sube no es un logro: es la firma de
     * estar colocando mas rapido de lo que entra. En ese caso el estado pasa a
     * `watch` (ambar) y deja de afirmar que va bien.
     *
     * Ver docs/superpowers/specs/2026-09-08-dashboard-salud-negocio-design.md §D.
     *
     * @param  array<string, array{trend_pct: float|null, good: bool|null}>  $trends
     * @return array<string, array{trend_pct: float|null, good: bool|null, state: string}>
     */
    public static function applyJointPolarity(array $trends): array
    {
        foreach ($trends as $key => $trend) {
            // Tri-estado: durante la rampa (sin snapshot de hace 7 dias) `good` es
            // null — desconocido. Colapsarlo a 'bad' seria inventar una mala
            // noticia, justo lo contrario del contrato anti-ceros.
            $trends[$key]['state'] = match ($trend['good'] ?? null) {
                null => 'no_data',
                true => 'good',
                false => 'bad',
            };
        }

        $carteraSube = ($trends['por_cobrar']['trend_pct'] ?? 0.0) > 0.0;
        $cajaBaja = ($trends['cash_base']['trend_pct'] ?? 0.0) < 0.0;
        $moraSube = ($trends['delinquency_rate']['trend_pct'] ?? 0.0) > 0.0;

        if ($carteraSube && ($cajaBaja || $moraSube)) {
            $trends['por_cobrar']['good'] = null;
            $trends['por_cobrar']['state'] = 'watch';
        }

        return $trends;
    }
}
