<?php

namespace App\Support;

use App\Models\Environments\Data;
use App\Models\Environments\EnvironmentStat;
use Illuminate\Support\Collection;

/**
 * Cuánto ha crecido cada sitio en usuarios, cursos y matriculaciones en un periodo.
 *
 * **El valor de ahora sale de `data`** —la última foto de cada entorno, la misma que usan
 * «Los sitios más grandes»— y **el de antes, de `environment_stats`**, que es la única
 * tabla con una fila por día.
 *
 * **La referencia es la foto más reciente de entre N y 2N días atrás**, la misma regla que
 * la pantalla de Uso de un entorno (`Uso::valorDeHace`), y por el mismo motivo: si el
 * Moodle no mandó justo ese día, la de un día antes vale; pero comparar «1 mes» contra
 * una foto de hace siete meses anunciaría como crecimiento de un mes el de medio año. Sin
 * foto en esa ventana el sitio no tiene incremento —`null`, no 0—: no sabemos cuánto ha
 * crecido, y un 0 diría que nada.
 *
 * Lo usan el Dashboard (los que más crecen) y `Environments\Crecimiento` (la tabla entera).
 */
class CrecimientoDelParque
{
    /** Columna de `data` y clave del JSON de `environment_stats` → nombre en pantalla. */
    public const METRICAS = [
        'users' => 'Usuarios',
        'courses' => 'Cursos',
        'enrolments' => 'Matriculaciones',
    ];

    /** Días → cómo se dice. El valor por defecto es el mes. */
    public const PERIODOS = [
        7 => '7 días',
        30 => '1 mes',
        365 => '1 año',
    ];

    /**
     * Una fila por entorno con telemetría.
     *
     * Por métrica: `ahora`, `antes`, `total` (ahora − antes) y `pct`. `pct` es `null`
     * también cuando antes era 0: crecer desde cero no es un porcentaje.
     *
     * @return Collection<int, array{entorno: \App\Models\Environments\Environment, dominio: string, conHistorico: bool, metricas: array<string, array{ahora: ?int, antes: ?int, total: ?int, pct: ?float}>}>
     */
    public static function filas(int $dias): Collection
    {
        $dias = array_key_exists($dias, self::PERIODOS) ? $dias : 30;

        $antes = self::fotosDeReferencia($dias);

        return Data::query()
            ->with('environment.client:id,name', 'environment.type:id,name')
            // El entorno tiene que existir: `data` sobrevive al borrado en blando de su
            // entorno, igual que en «Los sitios más grandes».
            ->whereHas('environment')
            ->get()
            ->map(function (Data $foto) use ($antes) {
                $referencia = $antes[$foto->environment_id] ?? null;
                $metricas = [];

                foreach (array_keys(self::METRICAS) as $metrica) {
                    $ahora = $foto->{$metrica};
                    $previo = is_array($referencia) && is_numeric($referencia[$metrica] ?? null)
                        ? (int) $referencia[$metrica]
                        : null;
                    $total = $ahora !== null && $previo !== null ? $ahora - $previo : null;

                    $metricas[$metrica] = [
                        'ahora' => $ahora,
                        'antes' => $previo,
                        'total' => $total,
                        'pct' => $total !== null && $previo > 0 ? round($total * 100 / $previo, 1) : null,
                    ];
                }

                return [
                    'entorno' => $foto->environment,
                    'dominio' => self::soloElHost($foto->environment->domain) ?: $foto->environment->name,
                    'conHistorico' => $referencia !== null,
                    'metricas' => $metricas,
                ];
            })
            ->values();
    }

    /**
     * El JSON de la foto de referencia de cada entorno, indexado por `environment_id`.
     *
     * Dos consultas y no una por entorno: la fecha elegida de cada uno con un `GROUP BY`, y
     * sus filas con un `JOIN` contra esa subconsulta. Se compara la fecha tal cual está
     * guardada, que es como la devuelve el `MAX`, así que casa en MySQL y en sqlite.
     *
     * @return array<int, array<string, mixed>>
     */
    private static function fotosDeReferencia(int $dias): array
    {
        $fechas = EnvironmentStat::query()
            ->selectRaw('environment_id, MAX(date) AS fecha')
            ->whereDate('date', '<=', now()->subDays($dias)->toDateString())
            ->whereDate('date', '>=', now()->subDays($dias * 2)->toDateString())
            ->groupBy('environment_id');

        return EnvironmentStat::query()
            ->joinSub($fechas, 'referencia', function ($join) {
                $join->on('environment_stats.environment_id', '=', 'referencia.environment_id')
                    ->on('environment_stats.date', '=', 'referencia.fecha');
            })
            ->get(['environment_stats.environment_id', 'environment_stats.data'])
            ->mapWithKeys(fn (EnvironmentStat $fila) => [$fila->environment_id => $fila->data ?? []])
            ->all();
    }

    /** El dominio sin `https://` ni barra final, como en el Dashboard. */
    public static function soloElHost(?string $dominio): string
    {
        if (! $dominio) {
            return '';
        }

        return rtrim(preg_replace('#^https?://#i', '', trim($dominio)), '/');
    }
}
