<?php

namespace Tests\Feature\Products;

use App\Livewire\Features\Index as FeaturesIndex;
use App\Livewire\Scss\Index as ScssIndex;
use App\Livewire\Products\CambioDeEstadoModal;
use App\Livewire\Setups\Index as SetupsIndex;
use App\Models\ApiRequestLog;
use App\Models\Auth\User;
use App\Models\Clients\Client;
use App\Models\Environments\Environment;
use App\Models\Environments\Plugin;
use App\Models\Features\FeatureVersion;
use App\Models\Products\LicenseToken;
use App\Models\Products\Product;
use App\Models\Scss\ScssVersion;
use App\Models\Setups\Setup;
use App\Support\RepartoDeVersiones;
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 listados de contenido versionado de un producto.
 *
 * Lo que se fija: que la pantalla diga **a quién le está llegando cada versión**. La API
 * no sirve la última: sirve la más alta que no supere la versión del plugin instalado en
 * cada sitio, así que se puede publicar contenido que no le llega a nadie sin que nada
 * falle ni avise. Y que un setup deprecado no cuente como servido, que es lo contrario de
 * lo que significa deprecarlo.
 */
class ContenidoVersionadoTest extends TestCase
{
    use RefreshDatabase;

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

        foreach ([
            'admin.products.index', 'admin.products.show',
            'admin.setups.index', 'admin.setups.show', 'admin.setups.edit', 'admin.setups.create',
            'admin.scss.index', 'admin.scss.version.create',
            'admin.features.index', 'admin.features.version.create',
            'admin.plugins.environment',
        ] as $nombre) {
            $rol->givePermissionTo(Permission::firstOrCreate(
                ['name' => $nombre, 'guard_name' => 'web'],
                ['model' => 'products', 'description' => $nombre]
            ));
        }

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

        Cache::flush();

        return $user;
    }

    private function producto(string $slug = 'block_tresipuntsepe'): Product
    {
        return Product::create([
            'slug' => $slug,
            'name' => 'Bloque SEPE',
            'tech' => 'moodle',
            'actived' => true,
            'enabled' => true,
        ]);
    }

    /**
     * Un entorno con licencia del producto, y con el plugin instalado en la versión dada.
     *
     * `$instalada` a null es el caso real más común: tiene licencia y **no** ha instalado
     * el plugin, así que no pide contenido y no recibe nada.
     */
    private function cliente(Product $producto, string $nombre, ?string $instalada): Environment
    {
        $cliente = Client::firstOrCreate(
            ['shortname' => 'cv'],
            ['name' => 'Cliente Contenido', 'actived' => true]
        );

        $token = LicenseToken::create([
            'token' => '3IP-CVCV-' . str_pad((string) (LicenseToken::count() + 1), 4, '0', STR_PAD_LEFT) . '-CVCV-CVCV',
            'client_id' => $cliente->id,
            'active' => true,
        ]);
        $token->products()->attach($producto->id);

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

        if ($instalada !== null) {
            Plugin::create([
                'environment_id' => $entorno->id,
                'name' => $producto->name,
                'component' => $producto->slug,
                'type' => explode('_', $producto->slug)[0],
                'versiondisk' => $instalada,
                'versiondb' => $instalada,
                'versionrequires' => '2024100700',
                'release' => '1.0.0',
                'has_updates' => false,
                'has_dependencies' => false,
            ]);
        }

        return $entorno;
    }

    /* ===================== La resolución de versión ===================== */

    public function test_un_cliente_recibe_la_version_mas_alta_que_no_supere_su_plugin(): void
    {
        // Es el algoritmo de las Actions de la API. Si el panel lo calcula de otra forma,
        // dirá una cosa y la API hará otra.
        $versiones = ['2026010100', '2026050100', '2026080100'];

        $this->assertSame('2026050100', RepartoDeVersiones::queRecibe($versiones, '2026060100'));
        // Exacta: se sirve esa.
        $this->assertSame('2026080100', RepartoDeVersiones::queRecibe($versiones, '2026080100'));
        // Su plugin va por delante de todo lo publicado: recibe lo más nuevo que haya.
        $this->assertSame('2026080100', RepartoDeVersiones::queRecibe($versiones, '2026120100'));
    }

    public function test_un_plugin_mas_antiguo_que_todo_no_recibe_nada(): void
    {
        // **No recibe contenido viejo: recibe un error.** La API devuelve 3002/4002/5002, y
        // desde el panel eso no se veía.
        $this->assertNull(RepartoDeVersiones::queRecibe(['2026050100'], '2025010100'));
    }

    public function test_las_versiones_se_comparan_como_cadenas_y_no_como_enteros(): void
    {
        // La marca `YYYYMMDDXX` son diez dígitos y como entero desborda en 32 bits: en un
        // servidor de 32 bits la comparación numérica daría cualquier cosa.
        $this->assertSame('2026080102', RepartoDeVersiones::queRecibe(
            ['2026080101', '2026080102', '2026080103'],
            '2026080102'
        ));
    }

    /* ===================== Quién recibe qué ===================== */

    public function test_la_pantalla_dice_cuantos_clientes_reciben_cada_version(): void
    {
        // **Es lo que el listado no decía.** Enseñaba «versión · archivos · creada por
        // quién» y con eso no se puede responder si le ha llegado a alguien.
        $this->admin();
        $producto = $this->producto();

        ScssVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);
        ScssVersion::create(['product_id' => $producto->id, 'version' => '2026080100', 'created_by' => 1, 'updated_by' => 1]);

        // Uno con el plugin al día y otro con uno viejo: reciben versiones distintas.
        $this->cliente($producto, 'Al dia', '2026090100');
        $this->cliente($producto, 'Atrasado', '2026050100');

        $reparto = Livewire::test(ScssIndex::class, ['product' => $producto])->viewData('reparto');

        $this->assertSame(1, $reparto['porVersion']['2026080100']);
        $this->assertSame(1, $reparto['porVersion']['2026010100']);
        $this->assertSame(2, $reparto['entornos']);
    }

    public function test_una_version_que_no_recibe_nadie_se_dice(): void
    {
        // Se publica contenido con una versión más alta que la del plugin de todos los
        // clientes y **no le llega a nadie**: para recibirla, cada cliente tiene que
        // actualizar su plugin. No hay operación inversa, así que se queda ahí sin que
        // nada falle.
        $this->admin();
        $producto = $this->producto();

        ScssVersion::create(['product_id' => $producto->id, 'version' => '2027010100', 'created_by' => 1, 'updated_by' => 1]);
        $this->cliente($producto, 'Con plugin viejo', '2026010100');

        $componente = Livewire::test(ScssIndex::class, ['product' => $producto]);

        $this->assertSame(0, $componente->viewData('reparto')['porVersion']['2027010100']);
        $componente->assertSee('Nadie');
    }

    public function test_un_cliente_con_licencia_y_sin_el_plugin_se_señala(): void
    {
        // **Es el caso real más común**: en el producto del SEPE, cuatro de cinco. No pide
        // contenido, así que no recibe nada y tampoco da error.
        $this->admin();
        $producto = $this->producto();

        ScssVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);
        $this->cliente($producto, 'Lo tiene', '2026050100');
        $this->cliente($producto, 'No lo instalo', null);
        $this->cliente($producto, 'Tampoco', null);

        $componente = Livewire::test(ScssIndex::class, ['product' => $producto]);
        $reparto = $componente->viewData('reparto');

        $this->assertSame(2, $reparto['sinPlugin']);
        $this->assertSame(3, $reparto['entornos']);
        // **Ya no hay tarjeta de aviso para esto.** Que un cliente no tenga el plugin
        // instalado no se arregla desde esta pantalla, así que va como una fila
        // agrupada de la tabla —el dato, sin recomendación— y no como una alarma.
        $componente->assertDontSee('Hay clientes a los que no les llega');
        $componente->assertSee('2 entornos más con licencia');
        $componente->assertSee('Nada — no piden y no da error');
    }

    public function test_un_cliente_al_que_la_api_le_devuelve_error_se_señala_aparte(): void
    {
        // No es lo mismo «no lo pide» que «lo pide y recibe un error»: el arreglo es
        // distinto —instalar el plugin, o actualizarlo—.
        $this->admin();
        $producto = $this->producto();

        ScssVersion::create(['product_id' => $producto->id, 'version' => '2026050100', 'created_by' => 1, 'updated_by' => 1]);
        $this->cliente($producto, 'Plugin prehistorico', '2024010100');

        $componente = Livewire::test(ScssIndex::class, ['product' => $producto]);
        $reparto = $componente->viewData('reparto');

        $this->assertSame(1, $reparto['conError']);
        $this->assertSame(0, $reparto['sinPlugin']);
        $componente->assertSee('Error 4002');
        $componente->assertSee('tiene que actualizarlo');
    }

    /* ===================== Las seis comparten una vista ===================== */

    public function test_cada_pantalla_dice_de_que_contenido_es_y_con_que_accion_se_sirve(): void
    {
        // La vista es una sola para los seis tipos: si la ficha de uno se queda a medias,
        // su pantalla se llama como otra. Y la acción de la API es lo que hay que buscar en
        // el visor de peticiones cuando un cliente dice que no le llega.
        $this->admin();
        $producto = $this->producto();

        Livewire::test(ScssIndex::class, ['product' => $producto])
            ->assertSee('SCSS')
            ->assertSee('scss');

        Livewire::test(FeaturesIndex::class, ['product' => $producto])
            ->assertSee('Funcionalidades')
            ->assertSee('features');
    }

    public function test_el_estado_vacio_explica_que_es_y_que_pasa_mientras_no_haya_nada(): void
    {
        // **Es el estado normal de cinco de los siete tipos.** Un «no hay versiones» a
        // secas deja a quien entra sin saber si eso está bien o falta algo.
        $this->admin();
        $producto = $this->producto();

        Livewire::test(ScssIndex::class, ['product' => $producto])
            ->assertSee('no publica scss todavía')
            ->assertSee('responde un error');
    }

    public function test_una_version_sin_contenido_dentro_se_marca(): void
    {
        // Una versión vacía se publica igual y el cliente que la reciba se lleva una
        // respuesta vacía: no da error y no se veía.
        $this->admin();
        $producto = $this->producto();

        FeatureVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);

        Livewire::test(FeaturesIndex::class, ['product' => $producto])->assertSee('Vacía');
    }

    /* ===================== Setups: el que tiene estado ===================== */

    public function test_un_setup_deprecado_no_cuenta_como_servido(): void
    {
        // **Es lo contrario de lo que significa deprecarlo.** Un `deprecated` no se sirve,
        // así que el cliente cuyo plugin resolvería a él recibe en realidad el activo
        // anterior.
        $this->admin();
        $producto = $this->producto();

        Setup::create(['product_id' => $producto->id, 'version' => '2026010100', 'yaml' => 'a: 1', 'status' => 'active', 'created_by' => 1, 'updated_by' => 1]);
        Setup::create(['product_id' => $producto->id, 'version' => '2026080100', 'yaml' => 'a: 2', 'status' => 'deprecated', 'created_by' => 1, 'updated_by' => 1]);

        $this->cliente($producto, 'Al dia', '2026090100');

        $componente = Livewire::test(SetupsIndex::class, ['product' => $producto]);
        $reparto = $componente->viewData('reparto');

        // Recibe el activo de enero, no el deprecado de agosto.
        $this->assertSame(1, $reparto['porVersion']['2026010100']);
        $this->assertArrayNotHasKey('2026080100', $reparto['porVersion']);
        $componente->assertSee('No se sirve');
    }

    public function test_si_ningun_setup_esta_activo_se_avisa(): void
    {
        // Un producto con setups pero ninguno publicado no sirve ninguno: quien pida la acción
        // recibe un error, y en la tabla eso solo se ve si te fijas en que todo está gris.
        $this->admin();
        $producto = $this->producto();

        Setup::create(['product_id' => $producto->id, 'version' => '2026010100', 'yaml' => 'a: 1', 'status' => 'deprecated', 'created_by' => 1, 'updated_by' => 1]);

        Livewire::test(SetupsIndex::class, ['product' => $producto])
            ->assertSee('No hay ningún setup publicado');
    }

    public function test_deprecar_un_setup_dice_a_cuantos_deja_sin_el(): void
    {
        // Deprecar deja de servirlo a quien lo esté recibiendo, y eso no se puede descubrir
        // después. Antes iba en un `confirm()` del navegador con el número metido a mano en
        // el atributo; ahora lo cuenta la modal, que además dice **a qué cae cada entorno**
        // y cuáles se quedan sin ninguno.
        $this->admin();
        $producto = $this->producto();

        $setup = Setup::create(['product_id' => $producto->id, 'version' => '2026010100', 'yaml' => 'a: 1', 'status' => 'active', 'created_by' => 1, 'updated_by' => 1]);
        $entorno = $this->cliente($producto, 'Lo recibe', '2026050100');

        Livewire::test(CambioDeEstadoModal::class, [
            'tipo' => 'setups',
            'productId' => $producto->id,
            'versionId' => $setup->id,
            'estado' => 'deprecated',
        ])
            ->assertSee('1 entorno la está recibiendo ahora mismo')
            // El que se queda sin ninguno, con su nombre: es el dato con el que se decide.
            ->assertSee('Lo recibe')
            ->assertSee('sin contenido')
            ->assertSee('se queda sin ninguna');
    }

    public function test_deprecar_el_ultimo_setup_avisa_de_que_la_api_se_queda_sin_contenido(): void
    {
        // **El caso silencioso.** Sin ningún setup publicado la API responde «sin
        // contenido», que es una respuesta correcta: no salta ninguna alarma en ninguna
        // pantalla. Si no se dice aquí, no se dice en ningún sitio.
        $this->admin();
        $producto = $this->producto();

        $setup = Setup::create(['product_id' => $producto->id, 'version' => '2026010100', 'yaml' => 'a: 1', 'status' => 'active', 'created_by' => 1, 'updated_by' => 1]);

        Livewire::test(CambioDeEstadoModal::class, [
            'tipo' => 'setups',
            'productId' => $producto->id,
            'versionId' => $setup->id,
            'estado' => 'deprecated',
        ])->assertSee('no quedará ninguna publicada');
    }

    public function test_la_modal_publica_y_deja_de_publicar(): void
    {
        $this->admin();
        $producto = $this->producto();

        $setup = Setup::create(['product_id' => $producto->id, 'version' => '2026010100', 'yaml' => 'a: 1', 'status' => 'active', 'created_by' => 1, 'updated_by' => 1]);

        Livewire::test(CambioDeEstadoModal::class, [
            'tipo' => 'setups',
            'productId' => $producto->id,
            'versionId' => $setup->id,
            'estado' => 'deprecated',
        ])->call('cambiar');

        $this->assertSame('deprecated', $setup->fresh()->status);

        // Y el histórico del setup sigue escribiéndose: es el único de los siete que lo
        // lleva, y el `toggleStatus` que esta modal sustituye lo escribía.
        $this->assertSame(1, $setup->history()->count());

        Livewire::test(CambioDeEstadoModal::class, [
            'tipo' => 'setups',
            'productId' => $producto->id,
            'versionId' => $setup->id,
            'estado' => 'active',
        ])->call('cambiar');

        $this->assertSame('active', $setup->fresh()->status);
    }

    /* ===================== Permisos ===================== */

    public function test_quien_solo_mira_no_ve_los_botones_de_escribir(): void
    {
        $rol = Role::firstOrCreate(['name' => 'Miron', 'guard_name' => 'web']);
        foreach (['admin.scss.index', 'admin.products.show'] as $nombre) {
            $rol->givePermissionTo(Permission::firstOrCreate(
                ['name' => $nombre, 'guard_name' => 'web'],
                ['model' => 'products', 'description' => $nombre]
            ));
        }

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

        $producto = $this->producto();
        ScssVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);

        Livewire::test(ScssIndex::class, ['product' => $producto])
            ->assertDontSee('Nueva versión')
            ->assertDontSee('Duplicar');
    }

    public function test_crear_una_version_exige_permiso(): void
    {
        // MGR-005: el permiso de la ruta no se reaplica en `/livewire/update`.
        $rol = Role::firstOrCreate(['name' => 'Miron', 'guard_name' => 'web']);
        $rol->givePermissionTo(Permission::firstOrCreate(
            ['name' => 'admin.scss.index', 'guard_name' => 'web'],
            ['model' => 'products', 'description' => 'ver']
        ));

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

        Livewire::test(ScssIndex::class, ['product' => $this->producto()])
            ->call('createNewVersion')
            ->assertForbidden();
    }

    /* ===================== Quién lo ha pedido de verdad ===================== */

    /** Una petición registrada a la API, como la que deja un cliente al pedir contenido. */
    private function peticion(Product $producto, string $accion, ?Environment $entorno, array $atributos = []): ApiRequestLog
    {
        return ApiRequestLog::create(array_merge([
            'request_uuid' => (string) \Illuminate\Support\Str::uuid(),
            'method' => 'POST',
            'path' => '/api/v1',
            'auth_type' => 'token',
            'action' => $accion,
            // El componente que declara el cliente en la petición: es lo que identifica de
            // qué producto se trata, porque la acción `features` la piden todos.
            'plugin' => $producto->slug,
            'environment_id' => $entorno?->id,
            'host' => $entorno?->domain,
            'version' => '2026080702',
            'http_status' => 200,
            'duration_ms' => 40,
            'started_at' => now()->subHours(2),
            'ended_at' => now()->subHours(2),
        ], $atributos));
    }

    public function test_se_ve_que_entornos_han_pedido_este_contenido(): void
    {
        // **Es otra pregunta que «quién debería recibirlo»**, y las dos hacen falta: un
        // cliente puede tener el plugin instalado y no haber llamado nunca, porque su tarea
        // programada no corre. Eso solo se ve en el registro de la API.
        $this->admin();
        $producto = $this->producto();
        $entorno = $this->cliente($producto, 'Llama', '2026080702');

        FeatureVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);
        $this->peticion($producto, 'features', $entorno);
        $this->peticion($producto, 'features', $entorno);

        $componente = Livewire::test(FeaturesIndex::class, ['product' => $producto]);
        $peticiones = $componente->viewData('peticiones');

        $this->assertSame(2, $peticiones['total']);
        $this->assertCount(1, $peticiones['porEntorno']);
        $this->assertSame(2, $peticiones['porEntorno']->first()['peticiones']);
        $componente->assertSee('Qué recibe cada entorno');
        $componente->assertSee('Llama');
    }

    public function test_un_cliente_que_pide_y_siempre_falla_se_destaca(): void
    {
        // **El caso que estaba invisible del todo**, y que pasó de verdad: `theme_fresk`
        // pidió `scss-cdn`, recibió un 3002 —«no hay versión compatible»— y nunca ha
        // recibido nada. Nadie lo sabía porque no hay ninguna pantalla que lo diga.
        $this->admin();
        $producto = $this->producto();
        $entorno = $this->cliente($producto, 'Pide y falla', '2026080702');

        // **Con una versión publicada**, que es donde el caso duele: hay contenido, el
        // cliente lo pide, y aun así no le llega. Sin versiones la pantalla está en el
        // estado vacío y este caso se cuenta allí, no en la tabla de clientes.
        FeatureVersion::create(['product_id' => $producto->id, 'version' => '2027010100', 'created_by' => 1, 'updated_by' => 1]);
        $this->peticion($producto, 'features', $entorno, ['http_status' => 404, 'error_code' => '3002']);

        $componente = Livewire::test(FeaturesIndex::class, ['product' => $producto]);
        $quien = $componente->viewData('peticiones')['porEntorno']->first();

        $this->assertTrue($quien['siempreFalla']);
        $this->assertSame('3002', $quien['ultimoError']);
        $componente->assertSee('nunca ha recibido nada');
    }

    public function test_un_fallo_ya_resuelto_no_se_presenta_como_roto(): void
    {
        // La historia real del producto del SEPE: pidió cuatro veces con error 3002, se
        // publicó la versión, y a partir de ahí recibió 200. Decir «nunca lo ha recibido»
        // ahí sería falso, y mandaría a arreglar algo que ya está arreglado.
        $this->admin();
        $producto = $this->producto();
        $entorno = $this->cliente($producto, 'Ya va bien', '2026080702');

        FeatureVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);
        $this->peticion($producto, 'features', $entorno, ['http_status' => 404, 'error_code' => '3002', 'started_at' => now()->subDays(2), 'ended_at' => now()->subDays(2)]);
        $this->peticion($producto, 'features', $entorno);

        $componente = Livewire::test(FeaturesIndex::class, ['product' => $producto]);
        $quien = $componente->viewData('peticiones')['porEntorno']->first();

        $this->assertFalse($quien['siempreFalla']);
        $this->assertSame(1, $quien['fallidas']);
        $componente->assertSee('con error antes');
        $componente->assertDontSee('nunca ha recibido nada');
    }

    public function test_contenido_publicado_que_nadie_pide_se_avisa(): void
    {
        // Hay versiones y en treinta días no ha llamado nadie: o los plugins no ejecutan su
        // tarea programada, o la versión instalada no pide este contenido. **No salta
        // ningún error**: simplemente se mantiene contenido para nadie.
        $this->admin();
        $producto = $this->producto();
        $this->cliente($producto, 'Instalado pero callado', '2026080702');

        FeatureVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);

        Livewire::test(FeaturesIndex::class, ['product' => $producto])
            ->assertSee('Nunca ha pedido')
            ->assertSee('revisar su tarea programada');
    }

    public function test_sin_versiones_y_sin_peticiones_no_se_avisa_de_nada(): void
    {
        // Un producto que no publica este tipo de contenido y a quien nadie se lo pide está
        // bien: avisar ahí sería ruido en cinco de las siete pantallas.
        $this->admin();

        Livewire::test(FeaturesIndex::class, ['product' => $this->producto()])
            ->assertDontSee('Qué recibe cada entorno');
    }

    public function test_las_peticiones_de_otro_producto_no_se_mezclan(): void
    {
        // La acción `features` la piden todos los productos: filtrar solo por `action`
        // metería las peticiones de un producto en la pantalla de otro.
        $this->admin();
        $sepe = $this->producto('block_tresipuntsepe');
        $otro = $this->producto('theme_fresk');

        $entorno = $this->cliente($sepe, 'Del sepe', '2026080702');
        $this->peticion($otro, 'features', $entorno);

        $this->assertSame(0, Livewire::test(FeaturesIndex::class, ['product' => $sepe])
            ->viewData('peticiones')['total']);
    }

    public function test_una_peticion_sin_entorno_no_desaparece(): void
    {
        // Una llamada con un token que no resuelve a ningún entorno **también es
        // información**: alguien está llamando. Agrupada como «null» se perdería.
        $this->admin();
        $producto = $this->producto();

        FeatureVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);
        $this->peticion($producto, 'features', null, ['host' => 'https://desconocido.test']);

        $componente = Livewire::test(FeaturesIndex::class, ['product' => $producto]);
        $quien = $componente->viewData('peticiones')['porEntorno']->first();

        $this->assertNull($quien['entorno']);
        $this->assertSame('https://desconocido.test', $quien['host']);
        // La tabla de abajo va **por cliente**, así que una petición cuyo token no
        // resuelve a ningún entorno no tiene fila propia; se cuenta y se conserva en
        // el registro, que es donde se investiga.
        $this->assertSame(1, $componente->viewData('peticiones')['total']);
    }

    public function test_dos_versiones_del_mismo_sitio_se_ensenan_las_dos(): void
    {
        // Ver dos versiones del mismo entorno es la señal de que ese sitio actualizó su
        // plugin por el medio, y eso explica un cambio de contenido que nadie pidió.
        $this->admin();
        $producto = $this->producto();
        $entorno = $this->cliente($producto, 'Se actualizo', '2026090100');

        FeatureVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);
        $this->peticion($producto, 'features', $entorno, ['version' => '2026080702', 'started_at' => now()->subDays(3), 'ended_at' => now()->subDays(3)]);
        $this->peticion($producto, 'features', $entorno, ['version' => '2026090100']);

        $versiones = Livewire::test(FeaturesIndex::class, ['product' => $producto])
            ->viewData('peticiones')['porEntorno']->first()['versiones'];

        $this->assertCount(2, $versiones);
    }

    public function test_solo_se_miran_los_ultimos_treinta_dias(): void
    {
        // `api_request_logs` es la tabla que más crece del sistema: sin acotar por fecha, la
        // consulta no puede usar el índice de `started_at`.
        $this->admin();
        $producto = $this->producto();
        $entorno = $this->cliente($producto, 'Llamo hace meses', '2026080702');

        FeatureVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);
        $this->peticion($producto, 'features', $entorno, ['started_at' => now()->subDays(60), 'ended_at' => now()->subDays(60)]);

        $this->assertSame(0, Livewire::test(FeaturesIndex::class, ['product' => $producto])
            ->viewData('peticiones')['total']);
    }

    public function test_si_el_registro_no_se_puede_leer_no_se_dice_que_nadie_lo_pidio(): void
    {
        // **No es lo mismo «nadie lo pidió» que «no se ha podido comprobar».** Con la
        // consulta cortada por tiempo, decir lo primero es afirmar algo que no se sabe.
        $this->admin();
        $producto = $this->producto();
        FeatureVersion::create(['product_id' => $producto->id, 'version' => '2026010100', 'created_by' => 1, 'updated_by' => 1]);

        \App\Support\ConsultaAcotada::fingirCorte();

        try {
            Livewire::test(FeaturesIndex::class, ['product' => $producto])
                ->assertSee('No se ha podido leer el registro de peticiones')
                ->assertDontSee('Nadie ha pedido este contenido');
        } finally {
            \App\Support\ConsultaAcotada::fingirCorte(false);
        }
    }
}
