<?php

namespace App\Support;

/**
 * El identificador de un vídeo de tutorial: se extrae de lo que pegue el autor, y se
 * comprueba (MGR-091).
 *
 * **El problema.** El campo se validaba como `required|string|max:255`, o sea nada: aceptaba
 * cualquier texto. Y `VideoUrlService` construye la dirección poniendo el valor detrás de
 * `watch?v=`, así que pegar la URL entera —que es lo natural— producí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**: ni al guardar, ni en el log de la API, ni en
 * el Moodle.
 *
 * **Y pegar la URL no es un error del autor**, es lo que cualquiera hace: copia la barra de
 * direcciones. Así que aquí no se rechaza, **se extrae**. Rechazarlo y pedirle que lo
 * recorte a mano sería trasladarle un trabajo que la máquina hace mejor y sin fallos.
 *
 * **Una decisión que conviene conocer: no se exige la longitud de YouTube.** Hoy todos sus
 * identificadores tienen 11 caracteres, y sería tentador comprobarlo. No se hace: si YouTube
 * cambia la longitud, un `size:11` empezaría a rechazar identificadores válidos y nadie
 * sabría por qué. Se valida **el juego de caracteres y un rango amplio**, que caza todos los
 * errores reales —URLs, espacios, texto pegado— sin apostar sobre lo que haga un tercero.
 */
class IdentificadorDeVideo
{
    /** Las dos plataformas que admite el modelo. */
    public const PLATAFORMAS = ['youtube', 'vimeo'];

    /**
     * Lo que el autor haya escrito, convertido en identificador.
     *
     * Acepta el identificador tal cual y las formas de URL que usa la gente. Devuelve el
     * texto **sin tocar** si no reconoce ninguna: quien valida es `esValido()`, y así un
     * valor raro llega a la validación con su forma original y el mensaje de error habla de
     * lo que el autor escribió.
     */
    public static function extraer(?string $escrito, string $plataforma): string
    {
        $valor = trim((string) $escrito);

        if ($valor === '') {
            return '';
        }

        // Si no parece una dirección, es el identificador y no hay nada que extraer.
        if (! str_contains($valor, '/') && ! str_contains($valor, '?')) {
            return $valor;
        }

        $patrones = $plataforma === 'vimeo'
            ? [
                // vimeo.com/123456789 y player.vimeo.com/video/123456789
                '~vimeo\.com/(?:video/)?(\d+)~i',
            ]
            : [
                // youtube.com/watch?v=ID  (el parámetro puede no ser el primero)
                '~[?&]v=([A-Za-z0-9_-]+)~',
                // youtu.be/ID
                '~youtu\.be/([A-Za-z0-9_-]+)~i',
                // youtube.com/embed/ID  y  /shorts/ID  y  /live/ID
                '~youtube\.com/(?:embed|shorts|live)/([A-Za-z0-9_-]+)~i',
            ];

        foreach ($patrones as $patron) {
            if (preg_match($patron, $valor, $coincidencia) === 1) {
                return $coincidencia[1];
            }
        }

        // Parecía una dirección y no se ha reconocido. Se devuelve tal cual para que la
        // validación lo rechace con lo que el autor escribió delante.
        return $valor;
    }

    /**
     * ¿Es un identificador con la forma que espera la plataforma?
     *
     * En Vimeo el identificador **es un número**, así que ahí sí se puede ser estricto. En
     * YouTube se comprueba el juego de caracteres y un rango de longitud amplio: ver la nota
     * del docblock de la clase sobre por qué no se fija en 11.
     */
    public static function esValido(?string $identificador, string $plataforma): bool
    {
        $valor = trim((string) $identificador);

        if ($valor === '') {
            return false;
        }

        return match ($plataforma) {
            'vimeo' => preg_match('/^\d{6,15}$/', $valor) === 1,
            'youtube' => preg_match('/^[A-Za-z0-9_-]{6,32}$/', $valor) === 1,
            // Una plataforma que no conocemos: no se puede afirmar que sea válido.
            default => false,
        };
    }

    /**
     * Qué decirle al autor cuando no vale.
     *
     * El mensaje distingue **haber pegado una dirección que no se ha reconocido** de haber
     * escrito cualquier otra cosa, porque son dos equivocaciones distintas: en la primera el
     * autor hizo lo razonable y le falló la herramienta.
     */
    public static function mensajeDeError(?string $escrito, string $plataforma): string
    {
        $valor = trim((string) $escrito);

        $pareceDireccion = str_contains($valor, '://')
            || str_contains($valor, 'youtu')
            || str_contains($valor, 'vimeo');

        if ($pareceDireccion) {
            return 'No se ha podido reconocer el identificador en esa dirección. '
                . ($plataforma === 'vimeo'
                    ? 'De «vimeo.com/123456789» el identificador es «123456789».'
                    : 'De «youtube.com/watch?v=abc123» el identificador es «abc123».')
                . ' Pega la dirección completa del vídeo o solo el identificador.';
        }

        return $plataforma === 'vimeo'
            ? 'El identificador de un vídeo de Vimeo son solo dígitos, como «123456789».'
            : 'El identificador de un vídeo de YouTube son letras, números, guiones y guiones '
                . 'bajos, como «dQw4w9WgXcQ».';
    }
}
