<?php

namespace Tests\Feature\Environments;

use App\Livewire\Environments\Index as EnvironmentsIndex;
use App\Models\Auth\User;
use App\Models\Clients\Client;
use App\Models\Environments\Environment;
use App\Models\Environments\Type;
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 KPIs del listado de entornos: de qué está hecho el parque.
 *
 * **Es otra pregunta que los chips de triaje.** Aquellos dicen *qué hay que atender* —sin
 * señal, sin licencia— y estos dicen *qué tenemos*. Las dos se hacían igual de a menudo y
 * solo una tenía respuesta: para saber cuántos sitios de producción hay había que filtrar y
 * leer el «N de M».
 *
 * Lo que se fija aquí, y los dos primeros son fallos que llegaron a pantalla:
 *
 * 1. Que **el número lleve a lo que dice**. «Desincronizados» contaba los que nunca han
 *    sincronizado más los que llevan tres días callados, y al pulsarlo filtraba solo por los
 *    segundos: la tarjeta decía 19 y la lista enseñaba 2.
 * 2. Que **alterne**. Un filtro que solo sabe encenderse obliga a bajar a la barra a
 *    apagarlo, que es el mismo clic en otro sitio.
 * 3. Que **cuente sobre todo el parque y no sobre lo filtrado**: es la composición, y si se
 *    moviera al filtrar dejaría de servir para saber de qué se está hablando.
 */
class KpisDelParqueTest extends TestCase
{
    use RefreshDatabase;

    private function usuario(): User
    {
        $rol = Role::firstOrCreate(['name' => 'RolDeKpis', 'guard_name' => 'web']);
        $rol->givePermissionTo(Permission::firstOrCreate(
            ['name' => 'admin.environments.index', 'guard_name' => 'web'],
            ['model' => 'environments', 'description' => 'ver']
        ));

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

        Cache::flush();

        return $usuario;
    }

    private function entorno(string $nombre, array $atributos = []): Environment
    {
        $cliente = Client::firstOrCreate(['shortname' => 'kpi'], ['name' => 'Cliente', 'actived' => true]);

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

    /** La celda de un grupo, por su etiqueta. */
    private function celda(array $composicion, string $titulo, string $empiezaPor): array
    {
        $grupo = collect($composicion)->firstWhere('titulo', $titulo);

        $this->assertNotNull($grupo, "no hay grupo «{$titulo}»");

        $celda = collect($grupo['celdas'])->first(fn (array $c) => str_starts_with($c['etiqueta'], $empiezaPor));

        $this->assertNotNull($celda, "no hay celda «{$empiezaPor}» en «{$titulo}»");

        return $celda;
    }

    public function test_el_numero_de_desincronizados_lleva_a_esos_mismos_entornos(): void
    {
        // **El fallo que llegó a pantalla.** El KPI cuenta los que no tienen datos frescos
        // —nunca han hablado, o hace tres días o más— y al pulsarlo se activaba el filtro de
        // «sin señal», que es solo el segundo grupo. La tarjeta decía 19 y la lista, 2.
        $this->usuario();

        $this->entorno('Nunca ha hablado', ['refresh_at' => null]);
        $this->entorno('Callado hace tiempo', ['refresh_at' => now()->subDays(9)]);
        $this->entorno('Al día', ['refresh_at' => now()->subHour()]);

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

        $celda = $this->celda($componente->instance()->composicion(), 'Servicio y señal', 'Sin datos frescos');

        $this->assertSame(2, $celda['n']);

        // Y al filtrar salen exactamente esos dos, no uno.
        $nombres = $componente->set('onlyStale', true)->viewData('environments')->pluck('name')->all();

        sort($nombres);

        $this->assertSame(['Callado hace tiempo', 'Nunca ha hablado'], $nombres);
    }

    public function test_los_tres_cortes_de_la_senal_se_excluyen(): void
    {
        // Son tres cortes del mismo dato y combinarlos daría listas vacías: un entorno no
        // puede a la vez no haber sincronizado nunca y llevar tres días sin hacerlo.
        $this->usuario();
        $this->entorno('Uno');

        $componente = Livewire::test(EnvironmentsIndex::class)
            ->call('alternarSenal', 'frescos')
            ->assertSet('onlyStale', true);

        $componente->call('alternarSenal', 'nunca')
            ->assertSet('onlyNeverSynced', true)
            ->assertSet('onlyStale', false)
            ->assertSet('onlyWithoutSignal', false);
    }

    public function test_pulsar_dos_veces_quita_el_filtro(): void
    {
        // Un filtro que solo sabe encenderse obliga a bajar a la barra a apagarlo, que es el
        // mismo clic en otro sitio. Así se prueba una composición y se vuelve sin moverse.
        $this->usuario();
        $this->entorno('De pre', ['env' => 'pre']);

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

        // Sin filtro, la acción pone el valor.
        $this->assertStringContainsString("'pre'", $this->celda($componente->instance()->composicion(), 'Por entorno de trabajo', 'pre')['accion']);

        // Con el filtro puesto, la misma celda lo quita.
        $componente->set('env', 'pre');

        $this->assertStringContainsString("\$set('env', '')", $this->celda($componente->instance()->composicion(), 'Por entorno de trabajo', 'pre')['accion']);
    }

    public function test_los_numeros_no_se_mueven_al_filtrar(): void
    {
        // **Es la composición del parque, no del filtro.** Si se movieran, al pulsar
        // «producción» dirían «3 pro, 0 pre, 0 local» — o sea, dejarían de responder a la
        // pregunta para la que existen justo después de usarlos, y sin forma de volver.
        $this->usuario();

        $this->entorno('Uno de pro');
        $this->entorno('Uno de pre', ['env' => 'pre']);

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

        $antes = $this->celda($componente->instance()->composicion(), 'Por entorno de trabajo', 'pre')['n'];

        $componente->set('env', 'pro');

        Cache::forget('environments:composicion:v6');

        $despues = $this->celda($componente->instance()->composicion(), 'Por entorno de trabajo', 'pre')['n'];

        $this->assertSame($antes, $despues, 'el recuento por uso no depende del filtro puesto');
    }

    public function test_la_plataforma_sale_con_su_logo_y_solo_si_tiene_sitios(): void
    {
        // Ofrecer una plataforma sin ni un sitio lleva a una lista vacía sin decir por qué.
        $this->usuario();

        $conSitios = Type::create(['name' => 'Moodle Workplace', 'shortname' => 'wp', 'image' => 'images_static/environments/workplace.svg']);
        Type::create(['name' => 'Plataforma sin usar', 'shortname' => 'nada']);

        $this->entorno('Uno', ['type_id' => $conSitios->id]);

        $grupo = collect(Livewire::test(EnvironmentsIndex::class)->instance()->composicion())
            ->firstWhere('titulo', 'Por plataforma');

        $etiquetas = collect($grupo['celdas'])->pluck('etiqueta')->all();

        $this->assertSame(['Moodle Workplace'], $etiquetas);
        $this->assertStringContainsString('workplace.svg', $grupo['celdas'][0]['logo']);
    }
}
