<?php

namespace App\Support;

use App\Models\Products\Product;
use Illuminate\Support\Facades\DB;

/**
 * Los siete tipos de contenido versionado de un producto, para el selector de la pantalla.
 *
 * **Antes no se podía ir de uno a otro.** Cada tipo era una URL suelta a la que solo se
 * llegaba desde la ficha del producto, y para pasar de los SCSS a las Funcionalidades había que
 * volver atrás. Con el selector, la sección se recorre como lo que es: siete caras de la
 * misma cosa.
 *
 * Y el contador dice **cuántas versiones tiene cada uno**, que es lo que permite ver de un
 * golpe que un producto publica SCSS y nada más —el caso real: de los siete tipos, cinco
 * están vacíos en los cinco productos—.
 */
class TiposDeContenido
{
    /**
     * La definición de cada tipo: cómo se llama, con qué se sirve y de qué tabla sale.
     *
     * `inicial` y `tono` son la identidad visual: en una sección de siete pantallas que
     * comparten estructura, un cuadro con la letra y un color de familia es lo que evita
     * confundirlas. Las familias son tres —el YAML del sitio, los archivos que se compilan
     * y el contenido que ve el usuario— y ahí es donde se equivoca uno, no entre familias.
     *
     * `relacion` y `contenido` dicen qué hay dentro de una versión y cómo se llama: los usa
     * la modal de duplicar para decir qué se va a copiar. En los setups no hay elementos
     * —el contenido es el propio YAML de la fila—, y por eso su relación es `null`.
     *
     * `modelo` y `permisoEscritura` los usa la modal del cambio de estado, que sirve a los
     * siete (MGR-023). Van aquí y no en un `match` dentro de la modal porque esto es **el
     * único sitio donde viven los siete juntos**: un `match` de siete ramas allí sería la
     * octava copia de esta misma tabla, que es el patrón que MGR-024 vino a quitar.
     *
     * @var array<string, array<string, mixed>>
     */
    public const TIPOS = [
        'setups' => [
            'relacion' => null,
            'contenido' => ['el YAML', 'el YAML'],
            'nombre' => 'Setups',
            'inicial' => 'S',
            'tono' => 'orange',
            'accion' => 'setup',
            'error' => '3002',
            'tabla' => 'setups',
            'ruta' => 'products.setups.index',
            'permiso' => 'admin.setups.index',
            'modelo' => \App\Models\Setups\Setup::class,
            'permisoEscritura' => 'admin.setups.edit',
        ],
        'scss' => [
            'relacion' => 'files',
            'contenido' => ['archivo', 'archivos'],
            'nombre' => 'SCSS',
            'inicial' => 'C',
            'tono' => 'teal',
            'accion' => 'scss',
            'error' => '4002',
            'tabla' => 'scss_versions',
            'ruta' => 'products.scss.index',
            'permiso' => 'admin.scss.index',
            'modelo' => \App\Models\Scss\ScssVersion::class,
            'permisoEscritura' => 'admin.scss.version.create',
        ],
        'js' => [
            'relacion' => 'files',
            'contenido' => ['archivo', 'archivos'],
            'nombre' => 'JS',
            'inicial' => 'J',
            'tono' => 'teal',
            'accion' => 'js',
            'error' => '4002',
            'tabla' => 'js_versions',
            'ruta' => 'products.js.index',
            'permiso' => 'admin.js.index',
            'modelo' => \App\Models\Js\JsVersion::class,
            'permisoEscritura' => 'admin.js.version.create',
        ],
        'scss-cdn' => [
            'relacion' => 'files',
            'contenido' => ['archivo', 'archivos'],
            'nombre' => 'SCSS CDN',
            'inicial' => 'C',
            'tono' => 'teal',
            'accion' => 'scss-cdn',
            'error' => '3002',
            'tabla' => 'scss_cdn_bundles',
            'ruta' => 'products.scss-cdn.index',
            'permiso' => 'admin.scss-cdn.index',
            'modelo' => \App\Models\ScssCdn\ScssCdnBundle::class,
            // El de su componente, que es la fuente buena: no sigue el patrón
            // `<tipo>.version.create` de los otros cinco.
            'permisoEscritura' => 'admin.scss-cdn.create',
        ],
        'features' => [
            'relacion' => 'features',
            'contenido' => ['funcionalidad', 'funcionalidades'],
            'nombre' => 'Funcionalidades',
            'inicial' => 'F',
            'tono' => 'info',
            'accion' => 'features',
            'error' => '3002',
            'tabla' => 'feature_versions',
            'ruta' => 'products.features.index',
            'permiso' => 'admin.features.index',
            'modelo' => \App\Models\Features\FeatureVersion::class,
            'permisoEscritura' => 'admin.features.version.create',
        ],
        'resources' => [
            'relacion' => 'resources',
            'contenido' => ['recurso', 'recursos'],
            'nombre' => 'Recursos',
            'inicial' => 'R',
            'tono' => 'info',
            'accion' => 'resources',
            'error' => '5002',
            'tabla' => 'resource_versions',
            'ruta' => 'products.resources.index',
            'permiso' => 'admin.resources.index',
            'modelo' => \App\Models\Resources\ResourceVersion::class,
            'permisoEscritura' => 'admin.resources.version.create',
        ],
        'tutorials' => [
            'relacion' => 'tutorials',
            'contenido' => ['tutorial', 'tutoriales'],
            'nombre' => 'Tutoriales',
            'inicial' => 'T',
            'tono' => 'info',
            'accion' => 'tutorials',
            'error' => '5002',
            'tabla' => 'tutorial_versions',
            'ruta' => 'products.tutorials.index',
            'permiso' => 'admin.tutorials.index',
            'modelo' => \App\Models\Tutorials\TutorialVersion::class,
            'permisoEscritura' => 'admin.tutorials.version.create',
        ],
    ];

    /** Los fondos y las tintas de cada familia, de los tokens del sistema de diseño. */
    private const TONOS = [
        'orange' => ['var(--orange-100)', 'var(--orange-600)'],
        'teal' => ['var(--teal-100)', 'var(--teal-700)'],
        'info' => ['var(--info-50)', 'var(--info-500)'],
    ];

    /**
     * Los siete tipos con su cuenta de versiones, para pintar el selector.
     *
     * **Una consulta para los siete**, con `UNION ALL`: siete `count()` sueltos por render
     * eran siete consultas en cada pulsación de una pantalla que ya hace varias.
     *
     * Se filtran por permiso: los permisos son **por tipo** —`admin.scss.index`,
     * `admin.setups.index`…— y ofrecer una pestaña que va a devolver un 403 es peor que no
     * ofrecerla.
     *
     * @return array<int, array<string, mixed>>
     */
    public static function paraElSelector(Product $producto, string $activo): array
    {
        $cuentas = self::cuentas($producto);
        $usuario = auth()->user();

        return collect(self::TIPOS)
            ->filter(fn (array $tipo) => $usuario?->can($tipo['permiso']) ?? false)
            ->map(fn (array $tipo, string $clave) => [
                'clave' => $clave,
                'nombre' => $tipo['nombre'],
                'cuantas' => $cuentas[$clave] ?? 0,
                'activo' => $clave === $activo,
                'ruta' => route($tipo['ruta'], $producto),
            ])
            ->values()
            ->all();
    }

    /**
     * Cuántas versiones tiene el producto de cada tipo.
     *
     * @return array<string, int>
     */
    public static function cuentas(Product $producto): array
    {
        $partes = [];
        $enlaces = [];

        foreach (self::TIPOS as $clave => $tipo) {
            // El nombre de la tabla sale de la constante de esta clase, nunca de la
            // petición: aquí se interpola en el SQL.
            $partes[] = 'select ? as clave, count(*) as cuantas from ' . $tipo['tabla']
                . ' where product_id = ?' . (self::tieneBorrado($tipo['tabla']) ? ' and deleted_at is null' : '');
            $enlaces[] = $clave;
            $enlaces[] = $producto->id;
        }

        $filas = DB::select(implode(' union all ', $partes), $enlaces);

        return collect($filas)->pluck('cuantas', 'clave')->map(fn ($n) => (int) $n)->all();
    }

    /** Si la tabla lleva borrado lógico: no todas las de contenido lo tienen. */
    private static function tieneBorrado(string $tabla): bool
    {
        static $cache = [];

        return $cache[$tabla] ??= \Illuminate\Support\Facades\Schema::hasColumn($tabla, 'deleted_at');
    }

    /**
     * El fondo y la tinta del tipo, para el cuadro de la inicial.
     *
     * @return array{0:string, 1:string}
     */
    public static function tonoDe(string $clave): array
    {
        $tono = self::TIPOS[$clave]['tono'] ?? 'orange';

        return self::TONOS[$tono] ?? self::TONOS['orange'];
    }
}
