<?php

namespace App\Services\System;

use App\Models\ApiRequestLog;
use App\Services\Api\Severidad;

/**
 * Los fallos de la API repartidos por lo que significan, no por su número.
 *
 * **La pantalla los contaba juntos y no son lo mismo.** Cuatro familias, cuatro
 * conversaciones distintas, y una de ellas ni siquiera es un fallo:
 *
 * | Familia | Qué es | Qué se hace |
 * |---|---|---|
 * | **401 / 403** | Token que no vale, producto fuera de la licencia | Vigilancia: puede ser un tercero probando |
 * | **5xx** | Avería nuestra | Lo más urgente, y hoy se perdía entre lo demás |
 * | **200 en volumen** | Todo correcto y llamando de más | Configuración o código del cliente |
 * | **404 / *002** | «No hay contenido publicado» | **No es un error** y no debe contarse como avería |
 *
 * La última es la que más daño hace mezclada: `norma-cero-errores.md` la clasifica como
 * respuesta correcta, y sumada al montón infla el porcentaje de fallos de cualquier sitio que
 * simplemente no tiene contenido publicado todavía.
 *
 * **Cada familia lleva el filtro con el que se abre el visor**, para que el número no sea un
 * callejón sin salida: se pulsa y se ven las peticiones que lo forman.
 */
final class FamiliasDeFallo
{
    /**
     * Las cuatro, con su recuento en la ventana pedida.
     *
     * @return array<int, array<string, mixed>>
     */
    public static function de(int $dias): array
    {
        $desde = now()->subDays($dias);

        $base = fn () => ApiRequestLog::query()->where('started_at', '>=', $desde);

        // **Una sola pasada y no cuatro consultas.** Son cuatro recuentos sobre la misma
        // ventana de la tabla más grande: agrupar una vez por estado y repartir en PHP sale
        // por una consulta en vez de por cuatro escaneos.
        $porEstado = $base()
            ->selectRaw('http_status, severity, count(*) as cuantas')
            ->groupBy('http_status', 'severity')
            ->get();

        $suma = function (callable $encaja) use ($porEstado): int {
            return (int) $porEstado
                ->filter(fn ($f) => $encaja((int) $f->http_status, (string) $f->severity))
                ->sum('cuantas');
        };

        return [
            [
                'clave' => 'auth',
                'etiqueta' => '401 / 403',
                'titulo' => 'Autenticación',
                'cuantas' => $suma(fn (int $e) => $e === 401 || $e === 403),
                'nota' => 'Token que no vale o producto fuera de la licencia. Es lo que hay que '
                    . 'mirar para controlar peticiones no autorizadas.',
                'tono' => 'warning',
                'filtro' => ['httpStatuses' => [401, 403]],
            ],
            [
                'clave' => 'averia',
                'etiqueta' => '5xx',
                'titulo' => 'Avería nuestra',
                'cuantas' => $suma(fn (int $e) => $e >= 500),
                'nota' => 'Lo único de esta pantalla que es culpa del Manager. Si hay alguna, '
                    . 'va antes que todo lo demás.',
                'tono' => 'danger',
                'filtro' => ['severity' => Severidad::ERROR, 'httpStatuses' => [500, 502, 503]],
            ],
            [
                'clave' => 'ok',
                'etiqueta' => '200',
                'titulo' => 'Correctas',
                'cuantas' => $suma(fn (int $e) => $e >= 200 && $e < 300),
                'nota' => 'Todo correcto. Un volumen anómalo aquí suele ser configuración o '
                    . 'código del cliente: ni corta ni falla, y es de lo que más cuesta ver.',
                'tono' => 'ok',
                'filtro' => ['severity' => Severidad::OK],
            ],
            [
                'clave' => 'sincontenido',
                'etiqueta' => '404 / *002',
                'titulo' => 'Sin contenido',
                // Por severidad y no por el código: un 404 puede ser otra cosa, y lo que
                // define esta familia es la clasificación, no el número.
                'cuantas' => $suma(fn (int $e, string $s) => $s === Severidad::SIN_CONTENIDO),
                'nota' => 'No es un error: no hay contenido publicado para lo que piden. '
                    . 'No cuenta como avería.',
                'tono' => 'neutro',
                'filtro' => ['severity' => Severidad::SIN_CONTENIDO],
            ],
        ];
    }
}
