<?php

namespace Tests\Feature\Tutorials;

use App\Livewire\Tutorials\Create as TutorialCreate;
use App\Models\Auth\User;
use App\Models\Products\Product;
use App\Models\Tutorials\Tutorial;
use App\Models\Tutorials\TutorialVersion;
use App\Support\IdentificadorDeVideo;
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 identificador de un vídeo de tutorial (MGR-091).
 *
 * **El fallo que arregla**: el campo se validaba como `required|string|max:255`, o sea nada.
 * Pegar la URL entera —que es lo que hace cualquiera— se guardaba tal cual, y como
 * `VideoUrlService` pone el valor detrás de `watch?v=`, salía
 * `watch?v=https://www.youtube.com/watch?v=abc123`. **El vídeo no cargaba en el panel del
 * cliente y no había error en ninguna parte.**
 *
 * Y la decisión de fondo: pegar la URL **no se rechaza, se extrae**. Es lo razonable que
 * hace el autor, y recortarla a mano es un trabajo que la máquina hace sin fallar.
 */
class IdentificadorDeVideoTest extends TestCase
{
    use RefreshDatabase;

    /**
     * @dataProvider formasDeYouTube
     */
    public function test_extrae_el_identificador_de_las_formas_de_youtube(string $escrito): void
    {
        $this->assertSame('dQw4w9WgXcQ', IdentificadorDeVideo::extraer($escrito, 'youtube'));
    }

    /** @return array<string, array{string}> */
    public static function formasDeYouTube(): array
    {
        return [
            'el identificador a secas' => ['dQw4w9WgXcQ'],
            'la URL de siempre' => ['https://www.youtube.com/watch?v=dQw4w9WgXcQ'],
            'sin protocolo' => ['youtube.com/watch?v=dQw4w9WgXcQ'],
            'el enlace corto' => ['https://youtu.be/dQw4w9WgXcQ'],
            'con un instante de inicio detrás' => ['https://youtu.be/dQw4w9WgXcQ?t=42'],
            'con la lista de reproducción' => ['https://www.youtube.com/watch?v=dQw4w9WgXcQ&list=PL123'],
            'con el parámetro después de otro' => ['https://www.youtube.com/watch?list=PL123&v=dQw4w9WgXcQ'],
            'la de incrustar' => ['https://www.youtube.com/embed/dQw4w9WgXcQ'],
            'un short' => ['https://www.youtube.com/shorts/dQw4w9WgXcQ'],
            'con espacios alrededor' => ['  https://youtu.be/dQw4w9WgXcQ  '],
        ];
    }

    /**
     * @dataProvider formasDeVimeo
     */
    public function test_extrae_el_identificador_de_las_formas_de_vimeo(string $escrito): void
    {
        $this->assertSame('123456789', IdentificadorDeVideo::extraer($escrito, 'vimeo'));
    }

    /** @return array<string, array{string}> */
    public static function formasDeVimeo(): array
    {
        return [
            'el número a secas' => ['123456789'],
            'la URL' => ['https://vimeo.com/123456789'],
            'sin protocolo' => ['vimeo.com/123456789'],
            'la del reproductor' => ['https://player.vimeo.com/video/123456789'],
            // **El caso de MGR-092**: un no listado lleva su código detrás. Se extrae el
            // número, que es lo único que el Manager sabe guardar — y por eso ese vídeo no
            // se podrá incrustar. Aquí solo se comprueba que la extracción no se rompe.
            'un no listado, con su código' => ['https://vimeo.com/123456789/a1b2c3d4e5'],
        ];
    }

    public function test_lo_que_no_se_reconoce_se_devuelve_tal_cual(): void
    {
        // Para que el mensaje de error hable de lo que el autor escribió, no de un valor
        // transformado que él no ha visto nunca.
        $this->assertSame(
            'https://dailymotion.com/video/x123',
            IdentificadorDeVideo::extraer('https://dailymotion.com/video/x123', 'youtube')
        );
    }

    public function test_valida_la_forma_segun_la_plataforma(): void
    {
        $this->assertTrue(IdentificadorDeVideo::esValido('dQw4w9WgXcQ', 'youtube'));
        $this->assertTrue(IdentificadorDeVideo::esValido('123456789', 'vimeo'));

        // En Vimeo el identificador **es un número**, así que ahí se puede ser estricto.
        $this->assertFalse(IdentificadorDeVideo::esValido('dQw4w9WgXcQ', 'vimeo'));

        $this->assertFalse(IdentificadorDeVideo::esValido('', 'youtube'));
        $this->assertFalse(IdentificadorDeVideo::esValido('con espacios', 'youtube'));
        $this->assertFalse(IdentificadorDeVideo::esValido('https://youtu.be/abc', 'youtube'));
    }

    public function test_no_se_exige_la_longitud_de_youtube(): void
    {
        // **Decisión deliberada.** Hoy todos tienen 11 caracteres y sería tentador
        // comprobarlo; si YouTube cambiara la longitud, un `size:11` empezaría a rechazar
        // identificadores válidos y nadie sabría por qué. Se valida el juego de caracteres.
        $this->assertTrue(IdentificadorDeVideo::esValido('abcdefghijklmno', 'youtube'));
        $this->assertTrue(IdentificadorDeVideo::esValido('abc123', 'youtube'));
    }

    public function test_el_mensaje_distingue_haber_pegado_una_url(): void
    {
        // Son dos equivocaciones distintas, y en la primera el autor hizo lo razonable.
        $conUrl = IdentificadorDeVideo::mensajeDeError('https://vimeo.com/canal/xyz', 'vimeo');
        $this->assertStringContainsString('dirección', $conUrl);

        $sinUrl = IdentificadorDeVideo::mensajeDeError('el vídeo del SEPE', 'vimeo');
        $this->assertStringContainsString('solo dígitos', $sinUrl);
    }

    /* ============ En el formulario ============ */

    private function autor(): void
    {
        $rol = Role::firstOrCreate(['name' => 'RolDeTutoriales', 'guard_name' => 'web']);
        $rol->givePermissionTo(Permission::firstOrCreate(
            ['name' => 'admin.tutorials.create', 'guard_name' => 'web'],
            ['model' => 'tutorials', 'description' => 'admin.tutorials.create']
        ));

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

        Cache::flush();
    }

    /** @return array{Product, TutorialVersion} */
    private function productoYVersion(): array
    {
        $producto = Product::create([
            'name' => 'Producto con tutoriales',
            'slug' => 'contutos',
            'actived' => true,
        ]);

        $version = TutorialVersion::create([
            'product_id' => $producto->id,
            'version' => '2026010100',
            'description' => 'Versión de prueba',
        ]);

        return [$producto, $version];
    }

    public function test_pegar_la_url_guarda_el_identificador(): void
    {
        // **El caso del ticket, de punta a punta.**
        $this->autor();
        [$producto, $version] = $this->productoYVersion();

        Livewire::test(TutorialCreate::class, ['product' => $producto, 'tutorialVersion' => $version])
            ->set('platform', 'youtube')
            ->set('videoid', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ')
            ->set('title', 'Cómo dar de alta una acción formativa')
            ->set('status', 'published')
            ->call('save')
            ->assertHasNoErrors('videoid');

        $this->assertSame('dQw4w9WgXcQ', Tutorial::first()->videoid);
    }

    public function test_la_url_guardada_ya_no_sale_doblada(): void
    {
        // Lo que de verdad rompía: la dirección que se le manda al plugin.
        $this->autor();
        [$producto, $version] = $this->productoYVersion();

        Livewire::test(TutorialCreate::class, ['product' => $producto, 'tutorialVersion' => $version])
            ->set('platform', 'youtube')
            ->set('videoid', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ')
            ->set('title', 'Un tutorial')
            ->set('status', 'published')
            ->call('save');

        $tutorial = Tutorial::first();

        $this->assertSame('https://www.youtube.com/watch?v=dQw4w9WgXcQ', $tutorial->video_url);
        $this->assertSame('https://www.youtube.com/embed/dQw4w9WgXcQ', $tutorial->embed_url);
    }

    public function test_lo_que_no_es_un_identificador_se_rechaza_al_guardar(): void
    {
        // Antes se guardaba y el vídeo no cargaba, sin error en ninguna parte.
        $this->autor();
        [$producto, $version] = $this->productoYVersion();

        Livewire::test(TutorialCreate::class, ['product' => $producto, 'tutorialVersion' => $version])
            ->set('platform' , 'vimeo')
            ->set('videoid', 'el vídeo que me pasó Marta')
            ->set('title', 'Un tutorial')
            ->set('status', 'published')
            ->call('save')
            ->assertHasErrors('videoid');

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

    public function test_un_identificador_de_youtube_no_vale_para_vimeo(): void
    {
        // El error de copiar un tutorial y cambiarle la plataforma sin cambiar el id.
        $this->autor();
        [$producto, $version] = $this->productoYVersion();

        Livewire::test(TutorialCreate::class, ['product' => $producto, 'tutorialVersion' => $version])
            ->set('platform', 'vimeo')
            ->set('videoid', 'dQw4w9WgXcQ')
            ->set('title', 'Un tutorial')
            ->set('status', 'published')
            ->call('save')
            ->assertHasErrors('videoid');
    }
}
