<?php

namespace App\Livewire\Monitoring;

use App\Models\ApiRequestLog;
use App\Models\Monitoring\ApiBlock;
use App\Models\Monitoring\ApiRateEvent;
use App\Models\Monitoring\Notice;
use App\Models\Products\LicenseToken;
use App\Services\Api\Severidad;
use Illuminate\Support\Facades\Cache;
use Livewire\Component;

/**
 * La portada de Monitorización.
 *
 * **Lo que había: la sección entraba por «Tráfico y cortes».** `/monitoring` era
 * directamente esa pantalla, así que la puerta de una sección de cinco herramientas era la
 * más densa de las cinco —una tabla de cortes paginada— y ninguna decía qué son las otras
 * cuatro. Es el mismo problema que tenía Configuración, que entraba por «Tipos de entornos»
 * (ver `briefs/configuracion.md`), y se arregla igual: una portada que **enruta y explica**.
 *
 * **No es un índice de enlaces.** Cada tarjeta dice *qué se contesta ahí* y trae **el número
 * que decide si hay que entrar**. En una sección que existe para vigilar, cinco nombres sin
 * número obligan a abrir las cinco para saber que no pasa nada.
 *
 * **Y los números dicen la verdad sobre lo que significan**, que en esta sección tiene
 * trampa:
 *
 * - Un corte **en observación no corta a nadie**: se apunta lo que se habría cortado y la
 *   petición sigue. Pintarlo en rojo sería mentir, así que van separados.
 * - Un error de la API **no es todo lo que responde `4xx`**: la mitad son respuestas
 *   correctas —«no hay contenido publicado», «no lo tiene contratado»—. Se cuenta solo
 *   `severity = error`, que es la norma de la casa (`norma-cero-errores.md`).
 * - Y se cuentan los errores **sin revisar**, no todos: los vistos siguen siendo errores,
 *   pero ya no son novedad para nadie.
 */
class Index extends Component
{
    /**
     * Los números de las cinco herramientas.
     *
     * **Cacheados 60 segundos** porque `render()` se ejecuta en cada interacción de
     * Livewire y esto son seis agregados, dos de ellos sobre `api_request_logs`, que es la
     * tabla que más crece del sistema. Sesenta segundos es lo mismo que usan las pestañas
     * de la sección: para decidir si entras a una pantalla, un minuto de retraso no cambia
     * ninguna decisión.
     *
     * @return array<string, int>
     */
    public function numeros(): array
    {
        return Cache::remember('monitoring:portada', 60, function () {
            $desde = now()->subDays(self::DIAS);

            return [
                // Cortando de verdad y en observación, separados: ver el docblock.
                'cortando' => ApiBlock::blocked()->where('enforced', true)->count(),
                'observando' => ApiBlock::blocked()->where('enforced', false)->count(),

                // Lo que hay guardado de los dos históricos, para que se vea que existen y
                // cuánto ocupan. `api_rate_events` no la lee ninguna pantalla todavía.
                'cortesGuardados' => ApiBlock::count(),
                'ventanasGuardadas' => ApiRateEvent::count(),

                'erroresSinRevisar' => ApiRequestLog::query()
                    ->where('started_at', '>=', $desde)
                    ->where('severity', Severidad::ERROR)
                    ->whereNull('reviewed_at')
                    ->count(),

                'peticiones' => ApiRequestLog::where('started_at', '>=', $desde)->count(),

                'avisosSinEntregar' => Notice::whereNull('sent_at')->count(),

                'caducaPronto' => LicenseToken::where('active', true)
                    ->whereNotNull('end_at')
                    ->whereDate('end_at', '>=', now()->format('Y-m-d'))
                    ->whereDate('end_at', '<=', now()->addDays(30)->format('Y-m-d'))
                    ->count(),
            ];
        });
    }

    /**
     * Los días que abarcan los números de peticiones.
     *
     * Siete y no treinta: la portada contesta «¿pasa algo *ahora*?». El rango largo es una
     * decisión que se toma ya dentro del visor, donde se puede cambiar.
     */
    public const DIAS = 7;

    public function render()
    {
        return view('livewire.monitoring.index', [
            'n' => $this->numeros(),
            'dias' => self::DIAS,
        ])->layout('layouts.app');
    }
}
