<?php

namespace Tests\Feature\Api;

use App\Models\Clients\Client;
use App\Models\Environments\Data;
use App\Models\Environments\Environment;
use App\Models\Environments\EnvironmentStat;
use App\Models\Products\LicenseToken;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Schema;
use Tests\TestCase;

/**
 * La portada del Moodle no cabía en la columna y el envío diario se iba con un 500.
 *
 * `data.description` guarda la descripción del sitio tal como la tiene escrita el cliente,
 * en HTML. Estaba declarada `varchar(255)` y dos clientes reales tenían ahí 382 y 664
 * caracteres —una bienvenida con párrafos y negritas, nada raro—. Con
 * `STRICT_TRANS_TABLES`, MariaDB no trunca: lanza «Data too long for column», que sale por
 * la API como «Error interno del servidor» y deja al sitio en rojo en el visor por un
 * fallo que era nuestro.
 *
 * **Lo caro no era la ficha, era el histórico.** `DataAction` escribe primero `data` y
 * después `environment_stats`, sin transacción, así que al reventar la primera la segunda
 * no llegaba a ejecutarse: esos sitios se quedaron sin la foto de ese día, y la foto de un
 * día no vuelve. La ficha la arregla el envío siguiente; la serie tiene un agujero para
 * siempre.
 *
 * **Y por qué no lo cogió nadie**: la suite corre en SQLite, que se apunta la longitud del
 * VARCHAR y luego la ignora, así que el insert pasaba aquí y fallaba solo en MariaDB. Por
 * eso el primer test de abajo no manda datos: mira **el tipo declarado de la columna**, que
 * es lo único de esto que los dos motores cuentan igual. Mismo truco que la regresión de
 * `only_full_group_by` en el listado de entornos.
 */
class DescripcionLargaDelSitioTest extends TestCase
{
    use RefreshDatabase;

    private const TOKEN = '3IP-TEST-DESCR-001';
    private const DOMAIN = 'https://descripcion.test';

    /** La portada real del cliente que destapó esto: 664 caracteres. */
    private const PORTADA_LARGA = '<p dir="ltr" style="text-align: left;"></p><p><b>Bienvenido/a a la Plataforma de '
        . 'Formación</b></p><p>Nos complace darte la bienvenida al entorno digital de aprendizaje '
        . 'diseñado para facilitar el cumplimiento de tus obligaciones formativas de manera '
        . 'eficiente, segura y profesional.</p><p>Esta plataforma ha sido desarrollada para '
        . 'ofrecerte acceso directo a los contenidos requeridos por la normativa vigente, con una '
        . 'experiencia intuitiva y adaptable a tu ritmo de trabajo.</p><p>Tu compromiso con la '
        . 'formación es clave para el desarrollo organizacional. Gracias por formar parte de este '
        . 'proceso de mejora continua.</p><p></p><br><br><p></p>';

    private Environment $entorno;

    protected function setUp(): void
    {
        parent::setUp();

        $cliente = Client::create([
            'name' => 'Cliente de la portada',
            'shortname' => 'portada',
            'actived' => true,
        ]);

        $token = LicenseToken::create([
            'client_id' => $cliente->id,
            'name' => 'Licencia de la portada',
            'token' => self::TOKEN,
            'active' => true,
        ]);

        $this->entorno = Environment::create([
            'name' => 'Entorno de la portada',
            'domain' => self::DOMAIN,
            'version' => '5.1.1',
            'env' => 'local',
            'client_id' => $cliente->id,
            'license_token_id' => $token->id,
            'active' => true,
        ]);
    }

    private function enviar(array $datos): \Illuminate\Testing\TestResponse
    {
        return $this->withHeaders([
            'Authorization' => 'Bearer ' . self::TOKEN,
            'Accept' => 'application/json',
        ])->postJson('/api/v1', [
            'action' => 'data',
            'host' => self::DOMAIN,
            'plugin' => 'local_tresipunt',
            'version' => '2026061100',
            'date' => '2026-09-16',
            'site' => [
                'version' => '2025100607',
                'release' => '5.1.7 (Build: 20260914)',
                'type' => 'moodle',
                'env' => 'pro',
            ],
            'data' => array_merge(['users' => 1172, 'courses' => 11], $datos),
        ]);
    }

    /**
     * @return list<array{string}>
     */
    public static function columnasDeTextoLibre(): array
    {
        // Las tres que reciben texto que no controlamos: la portada del sitio y dos URLs,
        // que con parámetros pasan de 255 sin ninguna dificultad. El resto de columnas de
        // la tabla —`language`, `countrycode`, `privacy`, `regioncode`— son códigos de
        // Moodle con forma fija y se quedan en varchar a propósito: ensanchar lo que tiene
        // forma conocida solo quita una comprobación que la base de datos hace gratis.
        return [
            'la portada del sitio' => ['description'],
            'la imagen del sitio' => ['imageurl'],
            'la url del sitio' => ['url'],
        ];
    }

    /**
     * @dataProvider columnasDeTextoLibre
     */
    public function test_las_columnas_de_texto_libre_no_tienen_tope_de_255(string $columna): void
    {
        // Se mira el tipo y no se prueba un insert largo porque **en SQLite el insert
        // largo pasa igual**: es el único motor de los dos donde este fallo no existe, y
        // es donde corre la suite.
        $this->assertSame(
            'text',
            Schema::getColumnType('data', $columna),
            "«data.{$columna}» recibe texto del sitio y no puede tener tope: un varchar(255) "
            . 'devuelve un 500 al Moodle en cuanto el cliente escribe un párrafo de más'
        );
    }

    public function test_una_portada_de_600_caracteres_se_guarda_entera(): void
    {
        $this->assertGreaterThan(255, mb_strlen(self::PORTADA_LARGA), 'la portada de prueba tiene que pasarse de 255');

        $this->enviar(['description' => self::PORTADA_LARGA])->assertOk();

        $this->assertSame(
            self::PORTADA_LARGA,
            Data::where('environment_id', $this->entorno->id)->firstOrFail()->description,
            'la portada se guarda tal cual, sin recortar'
        );
    }

    public function test_la_foto_del_dia_se_guarda_aunque_la_portada_sea_larga(): void
    {
        // **Es lo que de verdad se perdía.** La fila del histórico es la única de las dos
        // escrituras que no se puede repetir: la ficha la arregla el envío de mañana, la
        // serie del cliente se queda con el agujero.
        $this->enviar(['description' => self::PORTADA_LARGA, 'enrolments' => 2388])->assertOk();

        $foto = EnvironmentStat::where('environment_id', $this->entorno->id)->firstOrFail();

        $this->assertSame('2026-09-16', $foto->date->format('Y-m-d'));
        $this->assertSame(2388, $foto->data['enrolments']);
        $this->assertSame(self::PORTADA_LARGA, $foto->data['description']);
    }
}
