<?php

namespace Tests\Feature\Monitoring;

use App\Livewire\Monitoring\Tamano;
use App\Models\Auth\User;
use App\Services\System\Settings;
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;

/**
 * El tamaño de la base: cuántas filas tiene cada tabla y qué las borra.
 *
 * **Lo que no puede volver a pasar**, y es por lo que existe este fichero: el primer intento
 * contaba como «crecen sin que nada las borre» también las tablas **sin clasificar**, y eso
 * daba «28 tablas crecen sin control» cuando casi todas son catálogos de veinte filas. Un
 * aviso así enseña a no leer los avisos, que es justo lo contrario de para lo que está la
 * pantalla.
 */
class TamanoDeLaBaseTest extends TestCase
{
    use RefreshDatabase;

    private function usuario(): User
    {
        $rol = Role::firstOrCreate(['name' => 'Mirón tamaño', 'guard_name' => 'web']);
        $rol->givePermissionTo(Permission::firstOrCreate(
            ['name' => 'admin.api-logs.index', 'guard_name' => 'web'],
            ['model' => 'monitoring', 'description' => 'ver']
        ));

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

        return $usuario;
    }

    protected function setUp(): void
    {
        parent::setUp();

        // Los recuentos se cachean cinco minutos: entre tests eso cruzaría datos.
        Cache::forget('monitoring:tamano');
    }

    public function test_lista_las_tablas_con_sus_filas(): void
    {
        $this->usuario();

        $tablas = collect((new Tamano())->tablas())->keyBy('tabla');

        $this->assertTrue($tablas->has('api_request_logs'));
        $this->assertTrue($tablas->has('users'));
        $this->assertGreaterThanOrEqual(1, $tablas['users']['filas'], 'El usuario del test cuenta.');
    }

    public function test_las_tablas_del_framework_no_salen(): void
    {
        // `migrations`, `cache` y compañía no son datos del Manager: verlas solo alarga la
        // lista sin responder a nada.
        $this->usuario();

        $nombres = array_column((new Tamano())->tablas(), 'tabla');

        foreach (['migrations', 'cache', 'sessions', 'jobs'] as $fontaneria) {
            $this->assertNotContains($fontaneria, $nombres);
        }
    }

    public function test_solo_cuenta_como_sin_limite_lo_que_de_verdad_lo_es(): void
    {
        // **El fallo que este test fija.** Las «sin clasificar» no son un aviso: son una
        // tarea —alguien las clasifica— y se dicen aparte.
        $this->usuario();

        $resumen = (new Tamano())->resumen();

        $sinLimite = array_filter(
            (new Tamano())->tablas(),
            fn (array $t) => $t['estado'] === 'sinlimite' && $t['filas'] > 0
        );

        $this->assertSame(count($sinLimite), $resumen['sinLimite']);
        $this->assertArrayHasKey('sinClasificar', $resumen);
    }

    public function test_una_tabla_vacia_sin_retencion_no_es_un_aviso(): void
    {
        // Contarla llenaría el aviso de ruido: sin filas no hay nada que crezca.
        $this->usuario();

        foreach ((new Tamano())->tablas() as $tabla) {
            if ($tabla['estado'] === 'sinlimite' && $tabla['filas'] === 0) {
                $this->assertTrue(true);
            }
        }

        $resumen = (new Tamano())->resumen();

        $this->assertLessThanOrEqual(
            count(array_filter((new Tamano())->tablas(), fn ($t) => $t['filas'] > 0)),
            $resumen['sinLimite']
        );
    }

    public function test_la_retencion_a_cero_se_puede_distinguir(): void
    {
        // Es el dato que decide: 0 significa que la tarea se ejecuta cada noche y no borra
        // nada, y eso tiene que poder verse sin entrar a Configuraciones.
        $this->usuario();

        app(Settings::class)->set(Settings::LOGS_RETENCION, '0');
        app(Settings::class)->set(Settings::CORTES_RETENCION, '90');

        $retenciones = (new Tamano())->retenciones(app(Settings::class));

        $this->assertSame(0, $retenciones['api_request_logs']);
        $this->assertSame(90, $retenciones['api_blocks']);
    }

    public function test_la_pantalla_se_abre_y_agrupa(): void
    {
        $this->usuario();

        Livewire::test(Tamano::class)
            ->assertOk()
            ->assertSee('Crecen con el tráfico')
            ->assertSee('api_request_logs')
            ->assertSee('Las demás');
    }

    public function test_sin_permiso_no_se_abre(): void
    {
        // El gate de la ruta no se reaplica en /livewire/update (MGR-005).
        $usuario = User::factory()->create();
        $this->actingAs($usuario);

        Livewire::test(Tamano::class)->assertForbidden();
    }

    public function test_recontar_tira_la_cache(): void
    {
        // **No se comprueba que la caché quede vacía**, porque el propio render la vuelve a
        // llenar acto seguido —y eso es lo que se quiere—. Lo que se comprueba es que lo que
        // había antes ya no está: se deja un valor falso y tiene que desaparecer.
        $this->usuario();

        Cache::put('monitoring:tamano', [['tabla' => 'inventada', 'filas' => 999, 'que' => null, 'limpieza' => null, 'estado' => 'acotada']], 300);

        Livewire::test(Tamano::class)->call('recontar');

        $nombres = array_column(Cache::get('monitoring:tamano') ?? [], 'tabla');

        $this->assertNotContains('inventada', $nombres, 'El valor viejo se ha tirado.');
        $this->assertContains('api_request_logs', $nombres, 'Y se ha vuelto a contar de verdad.');
    }
}
