<?php

namespace Tests\Feature\Setups;

use App\Services\Setups\RevisionDelSetup;
use Tests\TestCase;

/**
 * La revisión estructural del YAML de un setup.
 *
 * **El techo de lo que se prueba aquí, y es lo importante**: esta revisión dice si el setup
 * está **bien escrito**, no si va a funcionar. Quien sabe lo segundo es `local_tresipunt`,
 * que es el que lo aplica en el Moodle del cliente. Un test que afirmara «esto funciona»
 * estaría probando algo que el Manager no puede saber.
 *
 * Lo que sí es demostrable leyendo el fichero es la coherencia interna, y ahí están los
 * errores que se cometen de verdad: una clave mal escrita que se acepta y no hace nada, un
 * usuario que apunta a un rol que no existe, dos `shortname` iguales.
 */
class RevisionDelSetupTest extends TestCase
{
    /** Un setup mínimo y coherente, del que partir en cada caso. */
    private function setupCorrecto(): array
    {
        return [
            'plugin' => 'block_tresipuntsepe',
            'version' => '2025112800',
            'roles' => [
                'tresipunt_sepe_ws' => [
                    'name' => 'Tresipunt SEPE WS',
                    'shortname' => 'tresipunt_sepe_ws',
                    'archetype' => 'coursecreator',
                ],
            ],
            'users' => [
                [
                    'username' => 'tresipunt_ws_sepe',
                    'firstname' => 'SEPE',
                    'lastname' => 'WS',
                    'email' => 'tresipunt_sepe_ws@tresipunt.comx',
                    'role' => 'tresipunt_sepe_ws',
                ],
            ],
        ];
    }

    public function test_un_setup_coherente_no_da_ningun_aviso(): void
    {
        // Si esto fallara, la revisión estaría avisando de lo normal — y una revisión que
        // avisa siempre no la lee nadie.
        $revision = RevisionDelSetup::revisar($this->setupCorrecto());

        $this->assertTrue($revision['limpio'], implode(' | ', $revision['avisos']));
        $this->assertSame([], $revision['avisos']);
    }

    public function test_avisa_del_usuario_con_un_rol_que_no_esta_declarado(): void
    {
        // **La comprobación que más vale.** Es la que falla más callada: la cuenta se crea
        // y se queda sin permisos, y en el YAML todo parece correcto.
        $setup = $this->setupCorrecto();
        $setup['users'][0]['role'] = 'un_rol_que_no_existe';

        $avisos = RevisionDelSetup::revisar($setup)['avisos'];

        $this->assertCount(1, $avisos);
        $this->assertStringContainsString('un_rol_que_no_existe', $avisos[0]);
        $this->assertStringContainsString('no está', $avisos[0]);
    }

    public function test_el_rol_se_puede_referenciar_por_su_clave_o_por_su_shortname(): void
    {
        // En los setups reales aparecen las dos formas, así que las dos valen y ninguna
        // debe dar aviso.
        $setup = $this->setupCorrecto();
        $setup['roles']['tresipunt_sepe_ws']['shortname'] = 'otro_shortname';

        // Por la clave del bloque.
        $this->assertTrue(RevisionDelSetup::revisar($setup)['limpio']);

        // Y por el shortname.
        $setup['users'][0]['role'] = 'otro_shortname';
        $this->assertTrue(RevisionDelSetup::revisar($setup)['limpio']);
    }

    public function test_avisa_de_una_clave_de_primer_nivel_desconocida(): void
    {
        // El caso del singular: `user_custom_field` se acepta hoy y **no hace nada**.
        $setup = $this->setupCorrecto();
        $setup['user_custom_field'] = ['category' => ['name' => 'SEPE']];

        $avisos = RevisionDelSetup::revisar($setup)['avisos'];

        $this->assertCount(1, $avisos);
        $this->assertStringContainsString('user_custom_field', $avisos[0]);
        $this->assertStringContainsString('no hace nada', $avisos[0]);
    }

    public function test_el_aviso_de_clave_desconocida_no_afirma_que_este_mal(): void
    {
        // **Puede ser un bloque nuevo del plugin** que aquí no se conozca todavía. Decir
        // «está mal» sería afirmar lo que no se sabe: se dice «no la reconocemos».
        $setup = $this->setupCorrecto();
        $setup['bloque_del_futuro'] = ['algo' => 1];

        $aviso = RevisionDelSetup::revisar($setup)['avisos'][0];

        $this->assertStringContainsString('no la reconocemos', $aviso);
    }

    public function test_avisa_de_un_arquetipo_que_no_es_de_moodle(): void
    {
        $setup = $this->setupCorrecto();
        $setup['roles']['tresipunt_sepe_ws']['archetype'] = 'profesor';

        $aviso = RevisionDelSetup::revisar($setup)['avisos'][0];

        $this->assertStringContainsString('profesor', $aviso);
        $this->assertStringContainsString('editingteacher', $aviso, 'hay que decir cuáles valen');
    }

    public function test_avisa_de_dos_shortname_iguales(): void
    {
        $setup = $this->setupCorrecto();
        $setup['roles']['otro'] = [
            'name' => 'Otro',
            'shortname' => 'tresipunt_sepe_ws',
            'archetype' => 'student',
        ];

        $avisos = RevisionDelSetup::revisar($setup)['avisos'];

        $this->assertStringContainsString('mismo «shortname»', implode(' ', $avisos));
    }

    public function test_avisa_de_la_cabecera_que_falta_o_esta_mal(): void
    {
        $setup = $this->setupCorrecto();
        unset($setup['plugin']);
        $setup['version'] = '2025-11-28';

        $avisos = implode(' | ', RevisionDelSetup::revisar($setup)['avisos']);

        $this->assertStringContainsString('plugin', $avisos);
        $this->assertStringContainsString('YYYYMMDDXX', $avisos);
    }

    public function test_avisa_del_token_emitido_a_un_usuario_que_no_esta(): void
    {
        // Mismo caso que el rol, y falla igual de callado.
        $setup = $this->setupCorrecto();
        $setup['webservice'] = [
            'protocols' => ['rest'],
            'service' => ['name' => 'Tresipunt SEPE WS'],
            'token' => ['user' => 'otro_usuario'],
        ];

        $avisos = implode(' | ', RevisionDelSetup::revisar($setup)['avisos']);

        $this->assertStringContainsString('otro_usuario', $avisos);
        $this->assertStringContainsString('no está en la lista de usuarios', $avisos);
    }

    public function test_los_campos_de_curso_y_de_usuario_usan_claves_de_tipo_distintas(): void
    {
        // `type` en los de curso, `datatype` en los de usuario. La asimetría es del plugin
        // y se respeta: pedir la clave equivocada haría avisar de setups correctos.
        $setup = $this->setupCorrecto();

        $setup['course_custom_fields'] = [
            'category' => ['name' => 'Acción Formativa'],
            'fields' => [['name' => 'Curso', 'shortname' => 'sepe', 'type' => 'checkbox']],
        ];
        $setup['user_custom_fields'] = [
            'category' => ['name' => 'SEPE'],
            'fields' => [['name' => 'Documento', 'shortname' => 'doc', 'datatype' => 'text']],
        ];

        $this->assertTrue(RevisionDelSetup::revisar($setup)['limpio']);

        // Y al revés, avisa: un campo de curso con `datatype` no declara su tipo.
        $setup['course_custom_fields']['fields'][0] = [
            'name' => 'Curso', 'shortname' => 'sepe', 'datatype' => 'checkbox',
        ];

        $avisos = implode(' | ', RevisionDelSetup::revisar($setup)['avisos']);
        $this->assertStringContainsString('«type»', $avisos);
    }

    public function test_avisa_de_campos_obligatorios_que_faltan(): void
    {
        $setup = $this->setupCorrecto();
        $setup['users'][0] = ['username' => 'solo_el_nombre', 'role' => 'tresipunt_sepe_ws'];

        $avisos = implode(' | ', RevisionDelSetup::revisar($setup)['avisos']);

        foreach (['firstname', 'lastname', 'email'] as $campo) {
            $this->assertStringContainsString($campo, $avisos);
        }
    }

    public function test_un_setup_vacio_no_revienta(): void
    {
        // Llega parseado de `Yaml::parse()`, así que puede ser cualquier cosa.
        $revision = RevisionDelSetup::revisar([]);

        $this->assertFalse($revision['limpio']);
        $this->assertNotEmpty($revision['avisos']);
    }

    public function test_los_bloques_mal_formados_no_revientan(): void
    {
        // Un `roles:` que sea una cadena en vez de un bloque, por ejemplo.
        $setup = $this->setupCorrecto();
        $setup['roles'] = 'esto no es un bloque';
        $setup['users'] = 'ni esto';

        $revision = RevisionDelSetup::revisar($setup);

        // No importa qué diga; importa que no lance.
        $this->assertIsArray($revision['avisos']);
    }
}
