<?php

use Illuminate\Database\Migrations\Migration;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Spatie\Permission\PermissionRegistrar;

/**
 * El permiso para borrar una versión de Funcionalidades (PRODSECU-198).
 *
 * **El hallazgo, y no es solo de Features.** El seeder crea `admin.features.version.create`,
 * `.edit` y `.show`, y **no `.destroy`**. Y lo mismo pasa en los otros cuatro tipos de
 * contenido versionado —Tutoriales, Recursos, SCSS y JS—: en los cinco se pueden borrar
 * **elementos de dentro** de una versión, y en ninguno se puede borrar la versión. Así que
 * esto no era un hueco de Features: **borrar una versión de contenido nunca ha existido**,
 * ni como permiso.
 *
 * Es la sexta vez que aparece este patrón en el release —MGR-039 el purgado de logs,
 * MGR-045, MGR-060 el borrado de productos, MGR-062 el de tipos de entorno, MGR-070 la
 * vista de plugins, y esto—: **funcionalidad que parecía existir y no tenía puerta**.
 *
 * **Se da a los mismos tres que ya pueden borrar una funcionalidad suelta** —Admin, Manager
 * y Developer, igual que `admin.features.destroy`—. No se restringe más a propósito: quien
 * puede vaciar una versión borrando sus elementos de uno en uno ya puede dejarla sin
 * contenido, así que negarle borrar la versión entera no protegería nada, solo lo haría más
 * incómodo. **Lo que protege de verdad es la modal**, que enseña a qué versión caería cada
 * entorno antes de confirmar.
 *
 * **Los cinco, y con su botón.** La primera versión de esta migración creaba solo el de
 * Funcionalidades, porque dar un permiso sin pantalla deja un permiso sin puerta —que es
 * justo el problema que arregla—. Al pedirlo el usuario para los cinco, la modal se
 * generalizó (`VersionBorrable` + `BorrarVersionModal`) y los cinco tienen su botón, así que
 * los cinco permisos tienen puerta.
 *
 * La correspondencia con el seeder está en `RoleSeeder::createPermissionAndAssign`, junto a
 * los otros de versión. Esta migración existe para los entornos ya desplegados, donde el
 * seeder no se vuelve a pasar.
 */
return new class extends Migration
{
    /**
     * Los cinco tipos de contenido versionado.
     *
     * **Ninguno tenía el permiso**: el seeder crea `.create`, `.edit` y `.show` de
     * versión en los cinco, y `.destroy` en ninguno. O sea que borrar una versión de
     * contenido nunca ha existido, ni como permiso.
     *
     * @var array<string, string>
     */
    private const PERMISOS = [
        'admin.features.version.destroy' => 'Features: Borrar versión',
        'admin.tutorials.version.destroy' => 'Tutoriales: Borrar versión',
        'admin.resources.version.destroy' => 'Recursos: Borrar versión',
        'admin.scss.version.destroy' => 'SCSS: Borrar versión',
        'admin.js.version.destroy' => 'JS: Borrar versión',
    ];

    /** Los mismos que ya pueden borrar una funcionalidad suelta. */
    private const ROLES = ['Admin', 'Manager', 'Developer'];

    public function up(): void
    {
        foreach (self::PERMISOS as $nombre => $descripcion) {
            $permiso = Permission::firstOrCreate(
                ['name' => $nombre, 'guard_name' => 'web'],
                ['model' => explode('.', $nombre)[1], 'description' => $descripcion]
            );

            foreach (self::ROLES as $rolNombre) {
                $rol = Role::where('name', $rolNombre)->where('guard_name', 'web')->first();

                // Sin el rol no se falla: en una instalación limpia esta migración corre
                // antes del seeder, y entonces es el seeder el que reparte.
                if ($rol !== null && ! $rol->hasPermissionTo($permiso)) {
                    $rol->givePermissionTo($permiso);
                }
            }
        }

        app(PermissionRegistrar::class)->forgetCachedPermissions();
    }

    public function down(): void
    {
        Permission::whereIn('name', array_keys(self::PERMISOS))->where('guard_name', 'web')->delete();

        app(PermissionRegistrar::class)->forgetCachedPermissions();
    }
};
