<?php

namespace Tests\Feature\Dashboard;

use App\Livewire\Dashboard\Index as Dashboard;
use App\Livewire\Environments\Crecimiento;
use App\Models\Auth\User;
use App\Models\Clients\Client;
use App\Models\Environments\Data;
use App\Models\Environments\Environment;
use App\Models\Environments\EnvironmentStat;
use App\Support\CrecimientoDelParque;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Los que más han crecido: tres rankings más en el Dashboard y la tabla adonde llevan.
 *
 * Lo que se fija aquí:
 *
 * 1. Que el ranking sea **en número y no en porcentaje**: un sitio de pruebas que pasa de
 *    2 a 10 usuarios no le gana a uno que gana 500.
 * 2. Que **sin foto de referencia no haya incremento**, ni siquiera 0: no sabemos cuánto
 *    ha crecido. Y que una foto demasiado vieja no cuente como la de hace un mes.
 * 3. Que la tabla ordene por cualquier columna con lo que no tiene dato siempre al final.
 */
class CrecimientoDelParqueTest extends TestCase
{
    use RefreshDatabase;

    private function admin(): User
    {
        $rol = Role::firstOrCreate(['name' => 'RolDelCrecimiento', 'guard_name' => 'web']);

        foreach (['admin.dashboard.index', 'admin.environments.index'] as $nombre) {
            $rol->givePermissionTo(Permission::firstOrCreate(
                ['name' => $nombre, 'guard_name' => 'web'],
                ['model' => 'dashboard', 'description' => $nombre]
            ));
        }

        $usuario = User::factory()->create();
        $usuario->assignRole($rol);
        $this->actingAs($usuario);

        Cache::flush();

        return $usuario;
    }

    /**
     * Un sitio con su telemetría de hoy y, si se da, la de hace `$haceDias`.
     *
     * @param  array{0: int, 1: int, 2: int}  $ahora  usuarios, cursos, matriculaciones
     * @param  array{0: int, 1: int, 2: int}|null  $antes
     */
    private function sitio(string $nombre, array $ahora, ?array $antes = null, int $haceDias = 30, bool $activo = true): Environment
    {
        $cliente = Client::firstOrCreate(['shortname' => 'crece'], ['name' => 'Cliente que crece', 'actived' => true]);

        $entorno = Environment::create([
            'name' => $nombre,
            'version' => '5.1.1',
            'domain' => 'https://' . str()->slug($nombre) . '.test',
            'env' => 'pro',
            'active' => $activo,
            'client_id' => $cliente->id,
        ]);

        Data::create([
            'environment_id' => $entorno->id,
            'users' => $ahora[0],
            'courses' => $ahora[1],
            'enrolments' => $ahora[2],
        ]);

        if ($antes !== null) {
            EnvironmentStat::create([
                'environment_id' => $entorno->id,
                'date' => now()->subDays($haceDias)->toDateString(),
                'data' => ['users' => $antes[0], 'courses' => $antes[1], 'enrolments' => $antes[2]],
            ]);
        }

        return $entorno;
    }

    private function fila(string $nombre, int $dias = 30): array
    {
        return CrecimientoDelParque::filas($dias)->first(fn (array $f) => $f['entorno']->name === $nombre);
    }

    public function test_el_incremento_es_contra_la_foto_de_hace_el_periodo(): void
    {
        $this->sitio('Campus', [1500, 40, 3000], [1000, 30, 2000], 35);

        $usuarios = $this->fila('Campus')['metricas']['users'];

        $this->assertSame(1500, $usuarios['ahora']);
        $this->assertSame(1000, $usuarios['antes']);
        $this->assertSame(500, $usuarios['total']);
        $this->assertSame(50.0, $usuarios['pct']);
    }

    public function test_sin_foto_en_la_ventana_no_hay_incremento_ni_cero(): void
    {
        // Hace 400 días cae fuera de [30, 60]: compararla anunciaría como crecimiento de
        // un mes el de más de un año.
        $this->sitio('Viejo', [800, 10, 900], [100, 5, 100], 400);
        $this->sitio('Sin histórico', [300, 3, 300]);

        foreach (['Viejo', 'Sin histórico'] as $nombre) {
            $fila = $this->fila($nombre);

            $this->assertFalse($fila['conHistorico']);
            $this->assertNull($fila['metricas']['users']['total']);
            $this->assertNull($fila['metricas']['users']['pct']);
        }

        // Pero en la del año, [365, 730], sí cae dentro.
        $this->assertSame(700, $this->fila('Viejo', 365)['metricas']['users']['total']);
    }

    public function test_desde_cero_hay_incremento_pero_no_porcentaje(): void
    {
        $this->sitio('Nuevo', [50, 2, 60], [0, 0, 0]);

        $usuarios = $this->fila('Nuevo')['metricas']['users'];

        $this->assertSame(50, $usuarios['total']);
        $this->assertNull($usuarios['pct']);
    }

    public function test_el_dashboard_ordena_por_numero_y_no_por_porcentaje(): void
    {
        $this->admin();

        $this->sitio('Grande', [10500, 100, 20000], [10000, 90, 19000]);   // +500, +5 %
        $this->sitio('Pruebas', [10, 1, 10], [2, 1, 10]);                  // +8, +400 %
        $this->sitio('Menguante', [400, 10, 400], [600, 10, 400]);         // pierde

        $crecen = Livewire::test(Dashboard::class)->viewData('masCrecen');
        $dominios = array_column($crecen['users']['filas'], 'dominio');

        $this->assertSame(['grande.test', 'pruebas.test'], $dominios);
        $this->assertSame(500, $crecen['users']['filas'][0]['cuantos']);
        $this->assertStringContainsString('/environments/growth?sort=users_total', $crecen['users']['enlace']);
    }

    public function test_las_seis_tarjetas_llevan_a_la_tabla(): void
    {
        $this->admin();
        $this->sitio('Campus', [1500, 40, 3000], [1000, 30, 2000]);

        $componente = Livewire::test(Dashboard::class);

        foreach (['masGrandes', 'masCrecen'] as $bloque) {
            foreach ($componente->viewData($bloque) as $lista) {
                $this->assertStringContainsString('/environments/growth?sort=', $lista['enlace']);
            }
        }

        $componente->assertSee('Los que más han crecido en un mes');
    }

    public function test_la_tabla_ordena_y_deja_lo_que_no_tiene_dato_al_final(): void
    {
        $this->admin();

        $this->sitio('Alfa', [100, 1, 100], [90, 1, 100]);      // +10
        $this->sitio('Beta', [100, 1, 100], [50, 1, 100]);      // +50
        $this->sitio('Gamma', [5000, 1, 100]);                  // sin histórico

        $componente = Livewire::test(Crecimiento::class);
        $nombres = fn () => $componente->viewData('filas')->map(fn ($f) => $f['entorno']->name)->all();

        $this->assertSame(['Beta', 'Alfa', 'Gamma'], $nombres());

        $componente->call('ordenarPor', 'users_total');
        $this->assertSame(['Alfa', 'Beta', 'Gamma'], $nombres(), 'al dar la vuelta, el que no tiene dato sigue al final');

        // Por tamaño sí tiene dato: es el más grande aunque no se sepa cuánto ha crecido.
        $componente->call('ordenarPor', 'users');
        $this->assertSame('Gamma', $nombres()[0]);
    }

    public function test_los_filtros(): void
    {
        $this->admin();

        $this->sitio('Encendido', [100, 1, 100], [90, 1, 100]);
        $this->sitio('Apagado', [100, 1, 100], [90, 1, 100], activo: false);
        $this->sitio('Sin foto', [100, 1, 100]);

        $componente = Livewire::test(Crecimiento::class);
        $nombres = fn () => $componente->viewData('filas')->map(fn ($f) => $f['entorno']->name)->sort()->values()->all();

        $this->assertSame(['Encendido', 'Sin foto'], $nombres(), 'los apagados fuera por defecto');

        $componente->set('soloConHistorico', true);
        $this->assertSame(['Encendido'], $nombres());

        $componente->set('soloConHistorico', false)->set('incluirApagados', true)->set('busqueda', 'apag');
        $this->assertSame(['Apagado'], $nombres());
    }

    public function test_la_url_esta_en_ingles_y_lo_escrito_a_mano_se_corrige(): void
    {
        $this->admin();

        $this->get('/environments/growth?period=7&sort=courses_pct&dir=asc')->assertOk();

        Livewire::withQueryParams(['period' => 99, 'sort' => 'nada', 'dir' => 'x'])
            ->test(Crecimiento::class)
            ->assertSet('periodo', 30)
            ->assertSet('orden', 'users_total')
            ->assertSet('direccion', 'desc');
    }

    public function test_sin_permiso_de_entornos_no_hay_tabla(): void
    {
        $rol = Role::firstOrCreate(['name' => 'SoloDash', 'guard_name' => 'web']);
        $rol->givePermissionTo(Permission::firstOrCreate(
            ['name' => 'admin.dashboard.index', 'guard_name' => 'web'],
            ['model' => 'dashboard', 'description' => 'dash']
        ));
        $usuario = User::factory()->create();
        $usuario->assignRole($rol);

        $this->actingAs($usuario)->get('/environments/growth')->assertForbidden();
    }
}
