<?php

namespace App\Livewire\Environments;

use App\Models\Environments\Type;
use App\Support\CrecimientoDelParque;
use Illuminate\Support\Collection;
use Livewire\Attributes\Url;
use Livewire\Component;

/**
 * Crecimiento del parque: tamaño de cada sitio y cuánto ha subido en un periodo.
 *
 * Es adonde llevan las seis tarjetas de ranking del Dashboard. Allí se ve el top 5 en
 * número; aquí está la tabla entera, con el porcentaje al lado, para comparar.
 *
 * **Los parámetros de la URL van en inglés**, como la ruta (`/environments/growth`), para
 * que un enlace pegado en un ticket lleve exactamente a la misma vista.
 */
class Crecimiento extends Component
{
    /** Los usos de un entorno, en el orden en que se piensan. */
    public const USOS = ['pro', 'pre', 'develop', 'local'];

    /** Días con que se compara: 7, 30 o 365. */
    #[Url(as: 'period', except: 30)]
    public int $periodo = 30;

    /** `site`, una métrica (`users`) o su incremento (`users_total`, `users_pct`). */
    #[Url(as: 'sort', except: 'users_total')]
    public string $orden = 'users_total';

    #[Url(as: 'dir', except: 'desc')]
    public string $direccion = 'desc';

    /** Dominio, nombre del entorno o cliente. */
    #[Url(as: 'q', except: '')]
    public string $busqueda = '';

    #[Url(as: 'env', except: '')]
    public string $uso = '';

    #[Url(as: 'type', except: '')]
    public string $tipo = '';

    /**
     * Los apagados fuera por defecto: un sitio sin servicio no crece por nada que hagamos,
     * y meterlo en un ranking de crecimiento es ruido.
     */
    #[Url(as: 'off', except: false)]
    public bool $incluirApagados = false;

    /** Solo los sitios con foto de referencia, para comparar sin huecos. */
    #[Url(as: 'history', except: false)]
    public bool $soloConHistorico = false;

    public function mount(): void
    {
        $this->sanear();
    }

    public function updated(): void
    {
        $this->sanear();
    }

    /**
     * Pulsar una cabecera ordena por ella; pulsarla otra vez da la vuelta.
     *
     * Las cifras empiezan de mayor a menor —lo que se viene a buscar es quién más tiene o
     * más ha crecido— y el sitio, de la A a la Z.
     */
    public function ordenarPor(string $columna): void
    {
        if (! in_array($columna, self::columnas(), true)) {
            return;
        }

        if ($this->orden === $columna) {
            $this->direccion = $this->direccion === 'desc' ? 'asc' : 'desc';

            return;
        }

        $this->orden = $columna;
        $this->direccion = $columna === 'site' ? 'asc' : 'desc';
    }

    public function limpiarFiltros(): void
    {
        $this->busqueda = '';
        $this->uso = '';
        $this->tipo = '';
        $this->incluirApagados = false;
        $this->soloConHistorico = false;
    }

    /** @return list<string> */
    public static function columnas(): array
    {
        $columnas = ['site'];

        foreach (array_keys(CrecimientoDelParque::METRICAS) as $metrica) {
            array_push($columnas, $metrica, $metrica . '_pct', $metrica . '_total');
        }

        return $columnas;
    }

    /** Lo que llega por URL se puede escribir a mano: se corrige en vez de reventar. */
    private function sanear(): void
    {
        if (! array_key_exists($this->periodo, CrecimientoDelParque::PERIODOS)) {
            $this->periodo = 30;
        }

        if (! in_array($this->orden, self::columnas(), true)) {
            $this->orden = 'users_total';
        }

        if (! in_array($this->direccion, ['asc', 'desc'], true)) {
            $this->direccion = 'desc';
        }

        if ($this->uso !== '' && ! in_array($this->uso, self::USOS, true)) {
            $this->uso = '';
        }
    }

    private function filtradas(Collection $filas): Collection
    {
        $termino = mb_strtolower(trim($this->busqueda));

        return $filas
            ->when(! $this->incluirApagados, fn (Collection $c) => $c->filter(fn (array $f) => $f['entorno']->active))
            ->when($this->uso !== '', fn (Collection $c) => $c->filter(fn (array $f) => $f['entorno']->env === $this->uso))
            ->when($this->tipo !== '', fn (Collection $c) => $c->filter(fn (array $f) => (string) $f['entorno']->type_id === $this->tipo))
            ->when($this->soloConHistorico, fn (Collection $c) => $c->filter(fn (array $f) => $f['conHistorico']))
            ->when($termino !== '', fn (Collection $c) => $c->filter(
                fn (array $f) => str_contains(mb_strtolower($f['dominio']), $termino)
                    || str_contains(mb_strtolower((string) $f['entorno']->name), $termino)
                    || str_contains(mb_strtolower((string) $f['entorno']->client?->name), $termino)
            ));
    }

    /**
     * **Lo que no tiene dato va siempre al final**, se ordene hacia donde se ordene. Un
     * sitio sin histórico no ha crecido 0: no sabemos cuánto, y ponerlo primero al ordenar
     * de menor a mayor lo presentaría como el que menos.
     */
    private function ordenadas(Collection $filas): Collection
    {
        if ($this->orden === 'site') {
            $ordenadas = $filas->sortBy(fn (array $f) => mb_strtolower($f['dominio']));

            return ($this->direccion === 'desc' ? $ordenadas->reverse() : $ordenadas)->values();
        }

        // `users` → el valor de ahora; `users_total` y `users_pct` → su incremento.
        [$metrica, $campo] = array_pad(explode('_', $this->orden, 2), 2, 'ahora');

        $valor = fn (array $f) => $f['metricas'][$metrica][$campo];

        [$conDato, $sinDato] = $filas->partition(fn (array $f) => $valor($f) !== null);

        $conDato = $this->direccion === 'desc' ? $conDato->sortByDesc($valor) : $conDato->sortBy($valor);

        return $conDato->concat($sinDato->sortBy(fn (array $f) => mb_strtolower($f['dominio'])))->values();
    }

    public function render()
    {
        $todas = CrecimientoDelParque::filas($this->periodo);
        $filas = $this->ordenadas($this->filtradas($todas));

        return view('livewire.environments.crecimiento', [
            'filas' => $filas,
            'total' => $todas->count(),
            'tipos' => Type::query()->orderBy('name')->get(['id', 'name']),
            'metricas' => CrecimientoDelParque::METRICAS,
            'periodos' => CrecimientoDelParque::PERIODOS,
            'hayFiltro' => $this->busqueda !== '' || $this->uso !== '' || $this->tipo !== ''
                || $this->incluirApagados || $this->soloConHistorico,
        ])->layout('layouts.app');
    }
}
