<?php

namespace Tests\Feature\Configurations;

use App\Livewire\Configurations\Version;
use App\Models\Auth\User;
use App\Support\Historial;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;

/**
 * «¿Está ya arriba lo de X?» — la pantalla y la norma que la sostiene.
 *
 * La pantalla es fácil; lo que de verdad protege este test es **la norma de versionado**
 * (`.tresipunt/…/manager/operations/versionado.md`): cada despliegue sube
 * `Historial::ACTUAL` y añade su entrada en `VERSIONES`. Si alguien sube el número y se
 * olvida de la entrada, la pantalla enseña un número pelado, sin qué trajo, que es
 * peor que no enseñar nada: parece que la información está y no está. Aquí se cae antes.
 */
class VersionYCambiosTest extends TestCase
{
    use RefreshDatabase;

    /** Sin un solo permiso: es lo que hace útil esta pantalla. */
    private function alguienSinPermisos(): User
    {
        $usuario = User::factory()->create();
        $this->actingAs($usuario);

        return $usuario;
    }

    public function test_se_ve_sin_ningun_permiso_de_configuracion(): void
    {
        // **A propósito.** Quien más necesita saber si algo está subido es quien atiende el
        // teléfono, que es justo quien menos permisos tiene. Pedir `admin.configs.index`
        // dejaría la pantalla solo para Admin, que es el único que ya sabía la respuesta.
        $this->alguienSinPermisos();

        $this->get(route('configurations.version'))->assertOk();
    }

    public function test_dice_que_version_corre_y_en_que_entorno(): void
    {
        // Sin el entorno, dos pestañas con esta misma pantalla son indistinguibles, que es
        // exactamente cuando uno se equivoca de instancia.
        $this->alguienSinPermisos();

        Livewire::test(Version::class)
            ->assertSee(Historial::ACTUAL)
            ->assertSee(Historial::donde());
    }

    public function test_la_norma_cada_version_publicada_tiene_su_numero_bien_puesto(): void
    {
        // `MAJOR.MINOR.PATCH` y nada más: ni `v2.1.0`, ni `2.1`, ni `2.1.0-rc1`. El número
        // se lee en el panel y se compara a ojo entre dos instancias.
        foreach (Historial::versiones() as $version) {
            $this->assertMatchesRegularExpression(
                '/^\d+\.\d+\.\d+$/',
                $version['version'],
                "«{$version['version']}» no sigue MAJOR.MINOR.PATCH; ver operations/versionado.md"
            );

        }

        $this->assertMatchesRegularExpression('/^\d+\.\d+\.\d+$/', Historial::ACTUAL);
    }

    public function test_la_norma_la_version_que_corre_tiene_su_entrada(): void
    {
        // **El olvido que este test existe para cazar**: subir `ACTUAL` y no añadir su
        // entrada en `VERSIONES`. La pantalla quedaría enseñando un número pelado, que
        // parece información y no lo es.
        $this->assertTrue(
            Historial::laActualEstaDocumentada(),
            'la versión ' . Historial::ACTUAL . ' no está en VERSIONES: al publicar hay que '
            . 'añadir su entrada con lo que trae. Ver operations/versionado.md'
        );
    }

    public function test_las_versiones_van_de_la_mas_nueva_a_la_mas_vieja_y_no_se_repiten(): void
    {
        $numeros = array_column(Historial::versiones(), 'version');

        $this->assertSame($numeros, array_values(array_unique($numeros)), 'hay una versión repetida');

        $ordenadas = $numeros;
        usort($ordenadas, fn (string $a, string $b) => version_compare($b, $a));

        $this->assertSame($ordenadas, $numeros, 'las versiones se pintan en orden, de la más nueva a la más vieja');
    }

    public function test_la_version_que_corre_sale_marcada_entre_las_demas(): void
    {
        // Con una sola versión no se nota, pero en cuanto haya tres la lista es idéntica
        // en pro y en develop salvo por esta marca: es lo único que dice cuál de ellas es
        // la que tienes delante.
        $this->alguienSinPermisos();

        Livewire::test(Version::class)->assertSee('la que corre aquí');
    }

    public function test_cada_linea_del_historial_es_una_linea(): void
    {
        // «Cortito y escueto»: esto se lee de un vistazo mientras se atiende a alguien. Si
        // una línea necesita un párrafo, el párrafo va al `CHANGELOG.md`, que es el que
        // guarda el porqué.
        $lineas = [];

        foreach (Historial::versiones() as $version) {
            $lineas = array_merge($lineas, $version['cambios'], [$version['titular']]);
        }

        foreach ($lineas as $linea) {
            $this->assertLessThanOrEqual(120, mb_strlen($linea), "demasiado larga para el panel: «{$linea}»");
            $this->assertStringNotContainsString("\n", $linea);
        }
    }
}
