<?php

namespace Tests\Feature\LicenseTokens;

use App\Livewire\Environments\Components\EnvironmentCard;
use App\Livewire\LicenseTokens\Index as LicenseTokensIndex;
use App\Models\Auth\User;
use App\Models\Clients\Client;
use App\Models\Environments\Environment;
use App\Models\Products\LicenseToken;
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;

/**
 * Cómo se llega a la licencia de un entorno, y al entorno desde la licencia.
 *
 * **El caso de uso que no estaba cubierto**: alguien quiere gestionar la licencia de un
 * sitio y no sabe dónde tocar. En el listado de entornos el token era **texto con un botón
 * de copiar**, y lo único de licencia en el menú de la fila era «Desasociar licencia» —la
 * destructiva—. Así que el camino era copiar el token, irse a Licencias y buscarlo… y el
 * buscador de Licencias **no buscaba por entorno**, así que tampoco valía para la pregunta
 * inversa: «¿qué licencia sirve a moodle51.dev?».
 */
class LlegarALaLicenciaTest extends TestCase
{
    use RefreshDatabase;

    private function usuario(array $permisos): User
    {
        $rol = Role::firstOrCreate(['name' => 'RolDeLicencias', 'guard_name' => 'web']);

        foreach ($permisos as $nombre) {
            $rol->givePermissionTo(Permission::firstOrCreate(
                ['name' => $nombre, 'guard_name' => 'web'],
                ['model' => 'license-tokens', 'description' => $nombre]
            ));
        }

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

        Cache::flush();

        return $user;
    }

    /** @return array{Environment, LicenseToken} */
    private function entornoConLicencia(): array
    {
        $cliente = Client::create([
            'name' => 'Cliente con licencia',
            'shortname' => 'conlic',
            'actived' => true,
        ]);

        $licencia = LicenseToken::create([
            'client_id' => $cliente->id,
            'name' => 'Licencia de producción',
            'token' => LicenseToken::generarToken(),
            'active' => true,
        ]);

        $entorno = Environment::create([
            'name' => 'Campus del cliente',
            'domain' => 'https://campus-del-cliente.test',
            'version' => '5.1.1',
            'env' => 'pro',
            'active' => true,
            'client_id' => $cliente->id,
            'license_token_id' => $licencia->id,
        ]);

        return [$entorno, $licencia];
    }

    public function test_desde_la_fila_del_entorno_se_llega_a_su_licencia(): void
    {
        // **El camino que faltaba.** El token es lo que se mira en la fila, así que es
        // donde se pulsa; y el menú ofrece «Gestionar su licencia» antes que «Desasociar».
        $this->usuario(['admin.environments.index', 'admin.license-tokens.show']);
        [$entorno, $licencia] = $this->entornoConLicencia();

        Livewire::test(EnvironmentCard::class, [
            'environment' => $entorno,
            'client' => $entorno->client,
        ])
            ->assertOk()
            ->assertSee(route('license_tokens.show', $licencia), escape: false)
            ->assertSee('Gestionar su licencia');
    }

    public function test_sin_permiso_de_ver_licencias_no_se_ofrece_el_enlace(): void
    {
        // `license_tokens.show` tiene su propio permiso: un enlace que va a dar 403 es
        // peor que no ofrecerlo.
        $this->usuario(['admin.environments.index']);
        [$entorno, $licencia] = $this->entornoConLicencia();

        Livewire::test(EnvironmentCard::class, [
            'environment' => $entorno,
            'client' => $entorno->client,
        ])
            ->assertOk()
            ->assertDontSee(route('license_tokens.show', $licencia), escape: false)
            ->assertDontSee('Gestionar su licencia')
            // Pero el token **cortado** se sigue viendo y se sigue pudiendo copiar: es el
            // dato, no el enlace, lo que hace falta para pegarlo en un Moodle.
            //
            // Se afirma contra el accessor y no contra un recorte escrito aquí: el recorte lo
            // define `LicenseToken::token_corto` —prefijo y cuatro últimos— y un número suelto
            // en el test lo deja divergiendo en el primer cambio.
            ->assertSee($licencia->token_corto)
            // Y el completo no está en la página, ni como texto ni en un tooltip: solo lo
            // lleva el botón de copiar.
            ->assertDontSee('title="' . $licencia->token . '"', escape: false);
    }

    public function test_un_entorno_sin_licencia_ofrece_asociarle_una(): void
    {
        // El camino contrario, que estaba solo en la celda y no en el menú.
        $this->usuario(['admin.environments.index', 'admin.license-tokens.manage-environments']);

        $cliente = Client::create([
            'name' => 'Cliente sin licencia',
            'shortname' => 'sinlic',
            'actived' => true,
        ]);

        $entorno = Environment::create([
            'name' => 'Campus sin licencia',
            'domain' => 'https://sin-licencia.test',
            'version' => '5.1.1',
            'env' => 'pre',
            'active' => true,
            'client_id' => $cliente->id,
        ]);

        Livewire::test(EnvironmentCard::class, [
            'environment' => $entorno,
            'client' => $cliente,
        ])
            ->assertOk()
            ->assertSee('Asociarle una licencia');
    }

    public function test_el_buscador_de_licencias_encuentra_por_dominio_del_entorno(): void
    {
        // **La pregunta real de soporte**: «¿qué licencia sirve a este sitio?». Antes se
        // buscaba por token, nombre y cliente, así que con el dominio delante no había
        // forma.
        $this->usuario(['admin.license-tokens.index']);
        [$entorno, $licencia] = $this->entornoConLicencia();

        $otra = LicenseToken::create([
            'client_id' => $entorno->client_id,
            'name' => 'Licencia sin entornos',
            'token' => LicenseToken::generarToken(),
            'active' => true,
        ]);

        $encontradas = Livewire::test(LicenseTokensIndex::class)
            ->set('search', 'campus-del-cliente')
            ->viewData('tokens');

        $ids = collect($encontradas->items())->pluck('id');

        $this->assertTrue($ids->contains($licencia->id), 'la licencia de ese sitio tiene que salir');
        $this->assertFalse($ids->contains($otra->id), 'y solo esa');
    }

    public function test_el_buscador_de_licencias_encuentra_por_nombre_del_entorno(): void
    {
        // Quien pregunta tiene el nombre o el dominio, no las dos cosas.
        $this->usuario(['admin.license-tokens.index']);
        [$entorno, $licencia] = $this->entornoConLicencia();

        $encontradas = Livewire::test(LicenseTokensIndex::class)
            ->set('search', 'Campus del cliente')
            ->viewData('tokens');

        $this->assertTrue(
            collect($encontradas->items())->pluck('id')->contains($licencia->id)
        );
    }

    public function test_buscar_por_entorno_no_rompe_el_filtro_de_estado(): void
    {
        // **La trampa de MGR-054**, que ya pasó en Clientes: un `orWhere` fuera de su
        // paréntesis se lleva por delante el `AND` de los demás filtros y la lista deja de
        // respetar la pestaña. La cláusula nueva va dentro del mismo grupo.
        $this->usuario(['admin.license-tokens.index']);
        [$entorno, $licencia] = $this->entornoConLicencia();

        $licencia->update(['active' => false]);

        $encontradas = Livewire::test(LicenseTokensIndex::class)
            ->set('estado', 'activas')
            ->set('search', 'campus-del-cliente')
            ->viewData('tokens');

        $this->assertCount(0, $encontradas->items(),
            'está apagada: el buscador no puede sacarla de la pestaña de activas');
    }
}
