<?php

namespace Tests\Feature\Seeders;

use App\Models\Auth\User;
use Database\Seeders\RoleSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Tests\TestCase;

/**
 * Los usuarios iniciales del seeder.
 *
 * **Dos cosas que no pueden volver.** Una, las contraseñas escritas en el código:
 * estaban literales (`Pruebas@123`, `12345678`), o sea en el repositorio y en su
 * historial. Dos, los correos de personas concretas: había tres compañeros escritos
 * aquí, así que cualquier instalación sembrada en cualquier sitio creaba cuentas a
 * su nombre.
 *
 * Ahora todo sale de `config/seed.php` —que lee el `.env`— y **sin correo no se crea
 * el usuario**. Ver known-issues MGR-026.
 *
 * Los tests configuran `config('seed.usuarios')` y no variables de entorno porque es
 * de ahí de donde lee el seeder: `env()` dentro de un seeder devuelve null en
 * producción en cuanto la configuración está en caché.
 */
class RoleSeederPasswordsTest extends TestCase
{
    use RefreshDatabase;

    /** @param array<int, array<string, string|null>> $usuarios */
    private function configurar(array $usuarios): void
    {
        config()->set('seed.usuarios', $usuarios);
    }

    public function test_el_seeder_no_lleva_contrasenas_escritas_en_el_codigo(): void
    {
        $codigo = file_get_contents(database_path('seeders/RoleSeeder.php'));

        // Cualquier bcrypt('literal') sería una contraseña en el repositorio.
        $this->assertSame(
            0,
            preg_match("/bcrypt\('/", $codigo),
            'El seeder vuelve a tener una contraseña escrita en el código.'
        );
    }

    public function test_el_seeder_no_lleva_correos_de_personas_escritos_en_el_codigo(): void
    {
        // Solo debe aparecer en el comentario que cuenta por qué ya no se usan, no en
        // una llamada a env() como valor por defecto.
        $codigo = file_get_contents(database_path('seeders/RoleSeeder.php'));

        $this->assertSame(
            0,
            preg_match("/env\(\s*'SEED_\w+_EMAIL'\s*,/", $codigo),
            'El seeder vuelve a tener un correo por defecto escrito en el código.'
        );
    }

    public function test_sin_correo_no_se_crea_ningun_usuario(): void
    {
        // **Lo que decide el nuevo comportamiento.** El .env.example va con todas
        // vacías: una instalación nueva no crea cuentas a nombre de nadie.
        $this->configurar([
            ['email' => null, 'name' => null, 'password' => null, 'rol' => 'Admin'],
            ['email' => '', 'name' => 'Alguien', 'password' => 'da-igual', 'rol' => 'WebService'],
        ]);

        $this->seed(RoleSeeder::class);

        $this->assertSame(0, User::count());
    }

    public function test_los_roles_y_los_permisos_se_siembran_aunque_no_haya_usuarios(): void
    {
        // Es el trabajo de fondo del seeder y no depende de que haya nadie.
        $this->configurar([]);

        $this->seed(RoleSeeder::class);

        $this->assertDatabaseHas('roles', ['name' => 'Admin']);
        $this->assertDatabaseHas('roles', ['name' => 'WebService']);
        $this->assertGreaterThan(0, \Spatie\Permission\Models\Permission::count());
    }

    public function test_crea_el_usuario_del_entorno_con_su_contrasena_y_su_rol(): void
    {
        $this->configurar([
            [
                'email' => 'jefa@example.test',
                'name' => 'Jefa de Proyecto',
                'password' => 'una-contrasena-del-entorno',
                'rol' => 'Admin',
            ],
        ]);

        $this->seed(RoleSeeder::class);

        $usuario = User::where('email', 'jefa@example.test')->first();

        $this->assertNotNull($usuario, 'No se ha creado el usuario del entorno.');
        $this->assertSame('Jefa de Proyecto', $usuario->name);
        $this->assertTrue(Hash::check('una-contrasena-del-entorno', $usuario->password));
        $this->assertTrue($usuario->hasRole('Admin'));
    }

    public function test_sin_nombre_usa_la_parte_local_del_correo(): void
    {
        // La columna no admite nulos y no hay nada mejor que se sepa de esa persona.
        $this->configurar([
            ['email' => 'rmartinez@example.test', 'name' => null, 'password' => 'x1234567', 'rol' => 'Admin'],
        ]);

        $this->seed(RoleSeeder::class);

        $this->assertSame('rmartinez', User::where('email', 'rmartinez@example.test')->first()->name);
    }

    public function test_con_correo_pero_sin_contrasena_se_genera_una(): void
    {
        // Una instalación nueva tiene que ser usable sin que ninguna contraseña viaje
        // en git. La generada se muestra una vez por consola.
        $this->configurar([
            ['email' => 'jefa@example.test', 'name' => null, 'password' => null, 'rol' => 'Admin'],
        ]);

        $this->seed(RoleSeeder::class);

        $usuario = User::where('email', 'jefa@example.test')->first();

        $this->assertNotNull($usuario);
        $this->assertFalse(Hash::check('', $usuario->password));
        $this->assertFalse(Hash::check('Pruebas@123', $usuario->password));
        $this->assertFalse(Hash::check('12345678', $usuario->password));
    }

    public function test_volver_a_sembrar_no_cambia_la_contrasena_existente(): void
    {
        // firstOrCreate: sembrar de nuevo no debe dejar a nadie fuera.
        $this->configurar([
            ['email' => 'jefa@example.test', 'name' => null, 'password' => 'primera', 'rol' => 'Admin'],
        ]);

        $this->seed(RoleSeeder::class);

        $this->configurar([
            ['email' => 'jefa@example.test', 'name' => null, 'password' => 'segunda', 'rol' => 'Admin'],
        ]);

        $this->seed(RoleSeeder::class);

        $usuario = User::where('email', 'jefa@example.test')->first();

        $this->assertTrue(
            Hash::check('primera', $usuario->password),
            'Sembrar de nuevo ha cambiado la contraseña de un usuario existente.'
        );
    }

    public function test_un_rol_que_no_existe_no_revienta_el_seeder(): void
    {
        // Es una errata en el .env, no un fallo del seeder: se crea el usuario sin rol
        // y se avisa por consola.
        $this->configurar([
            ['email' => 'jefa@example.test', 'name' => null, 'password' => 'x1234567', 'rol' => 'RolInventado'],
        ]);

        $this->seed(RoleSeeder::class);

        $usuario = User::where('email', 'jefa@example.test')->first();

        $this->assertNotNull($usuario);
        $this->assertCount(0, $usuario->roles);
    }
}
