<?php

namespace App\Support;

use App\Models\Features\Feature;
use App\Models\Features\FeatureVersion;
use App\Models\Js\JsFile;
use App\Models\Js\JsVersion;
use App\Models\Resources\Resource;
use App\Models\Resources\ResourceVersion;
use App\Models\Scss\ScssFile;
use App\Models\Scss\ScssVersion;
use App\Models\Tutorials\Tutorial;
use App\Models\Tutorials\TutorialVersion;
use InvalidArgumentException;

/**
 * Los cinco tipos de contenido cuya **versión** se puede borrar (PRODSECU-198).
 *
 * **Por qué un registro y no una modal por tipo.** Los cinco —Funcionalidades, Tutoriales,
 * Recursos, SCSS y JS— tienen exactamente la misma forma: un producto tiene versiones
 * numeradas `YYYYMMDDXX`, cada versión contiene elementos, y la API le sirve a cada Moodle
 * la versión más alta menor o igual a la que pide su plugin. Lo único que cambia es **el
 * modelo, la relación y las palabras**.
 *
 * Con una modal por tipo serían cinco copias del cálculo de impacto —el que dice a qué
 * versión cae cada entorno—, o sea MGR-024 otra vez, donde la regla de negocio central
 * estaba escrita siete veces con el mismo cuerpo palabra por palabra. Aquí hay **un
 * cálculo, una modal y cinco filas de tabla**.
 *
 * **Setups y SCSS-CDN no están, y no es un olvido.** `setups` no versiona por número:
 * tiene un `active` y la resolución es otra. Y `scss-cdn` son bundles con su propio ciclo.
 * Si algún día se les añade borrado de versión, tendrán que pasar por aquí y **cambiar la
 * documentación de este docblock**, no colarse con una fila.
 *
 * **Ninguno de los cinco usa `SoftDeletes`.** Comprobado en los cinco modelos de versión, y
 * es lo que obliga a que la modal diga «esto no se puede deshacer» y a que el rastro se
 * escriba **antes** de borrar: después no queda fila de la que sacar qué había.
 */
class VersionBorrable
{
    /**
     * @var array<string, array<string, mixed>>
     */
    public const TIPOS = [
        'features' => [
            'modelo' => FeatureVersion::class,
            'modeloElemento' => Feature::class,
            // La relación de la versión con lo que contiene.
            'relacion' => 'features',
            // La clave ajena del elemento, para acotar la búsqueda a su versión (MGR-006).
            'claveDeVersion' => 'feature_version_id',
            'nombre' => 'funcionalidades',
            'elemento' => 'funcionalidad',
            'elementos' => 'funcionalidades',
            'permisoVersion' => 'admin.features.version.destroy',
            'permisoElemento' => 'admin.features.destroy',
            'rutaListado' => 'products.features.index',
            // Los elementos con estado no se sirven todos: solo los `published`. En los que
            // no lo tienen, todo lo que hay en la versión se sirve.
            'conEstado' => true,
        ],
        'tutorials' => [
            'modelo' => TutorialVersion::class,
            'modeloElemento' => Tutorial::class,
            'relacion' => 'tutorials',
            'claveDeVersion' => 'tutorial_version_id',
            'nombre' => 'tutoriales',
            'elemento' => 'tutorial',
            'elementos' => 'tutoriales',
            'permisoVersion' => 'admin.tutorials.version.destroy',
            'permisoElemento' => 'admin.tutorials.destroy',
            'rutaListado' => 'products.tutorials.index',
            'conEstado' => true,
        ],
        'resources' => [
            'modelo' => ResourceVersion::class,
            'modeloElemento' => Resource::class,
            'relacion' => 'resources',
            'claveDeVersion' => 'resource_version_id',
            'nombre' => 'recursos',
            'elemento' => 'recurso',
            'elementos' => 'recursos',
            'permisoVersion' => 'admin.resources.version.destroy',
            'permisoElemento' => 'admin.resources.destroy',
            'rutaListado' => 'products.resources.index',
            'conEstado' => true,
        ],
        'scss' => [
            'modelo' => ScssVersion::class,
            'modeloElemento' => ScssFile::class,
            'relacion' => 'files',
            'claveDeVersion' => 'scss_version_id',
            'nombre' => 'SCSS',
            'elemento' => 'archivo',
            'elementos' => 'archivos',
            'permisoVersion' => 'admin.scss.version.destroy',
            'permisoElemento' => 'admin.scss.file.destroy',
            'rutaListado' => 'products.scss.index',
            // **Los archivos no tienen borrador**: todo lo que hay en la versión se sirve,
            // así que borrar uno lo quita de todos los sitios que reciben esa versión.
            'conEstado' => false,
        ],
        'js' => [
            'modelo' => JsVersion::class,
            'modeloElemento' => JsFile::class,
            'relacion' => 'files',
            'claveDeVersion' => 'js_version_id',
            'nombre' => 'JS',
            'elemento' => 'archivo',
            'elementos' => 'archivos',
            'permisoVersion' => 'admin.js.version.destroy',
            'permisoElemento' => 'admin.js.file.destroy',
            'rutaListado' => 'products.js.index',
            'conEstado' => false,
        ],
    ];

    /**
     * La ficha de un tipo.
     *
     * Revienta si no existe **a propósito**: el tipo llega como argumento de la modal, y
     * una clave mal escrita con un `?? []` silencioso daría una modal en blanco que borra
     * lo que no debe. Mejor un error del que se entera quien lo escribe.
     *
     * @return array<string, mixed>
     */
    public static function de(string $clave): array
    {
        if (! isset(self::TIPOS[$clave])) {
            throw new InvalidArgumentException(
                'El tipo de contenido «' . $clave . '» no tiene borrado de versión. '
                . 'Los que lo tienen: ' . implode(', ', array_keys(self::TIPOS)) . '.'
            );
        }

        return self::TIPOS[$clave];
    }

    /** @return list<string> */
    public static function claves(): array
    {
        return array_keys(self::TIPOS);
    }
}
