<?php
declare(strict_types=1);

namespace App\Security;

use RuntimeException;

final class SqlSecurityPolicy
{
    /**
     * Refuse les commandes qui ne doivent jamais être exécutées par un package applicatif.
     */
    public static function assertMigrationSql(string $sql, string $label = 'migration'): void
    {
        $clean = self::stripComments($sql);
        $forbidden = [
            '/\b(?:CREATE|ALTER|DROP)\s+USER\b/i' => 'gestion des comptes SQL',
            '/\bGRANT\b|\bREVOKE\b/i' => 'modification des privilèges SQL',
            '/\bDROP\s+DATABASE\b|\bCREATE\s+DATABASE\b/i' => 'gestion de base de données',
            '/\bSET\s+(?:GLOBAL|PERSIST|PERSIST_ONLY)\b/i' => 'modification globale du serveur',
            '/\bSHUTDOWN\b/i' => 'arrêt du serveur SQL',
            '/\bINSTALL\s+(?:PLUGIN|COMPONENT)\b|\bUNINSTALL\s+(?:PLUGIN|COMPONENT)\b/i' => 'installation de composant serveur',
            '/\bLOAD\s+DATA\b/i' => 'chargement de fichier serveur',
            '/\bINTO\s+(?:OUTFILE|DUMPFILE)\b/i' => 'écriture de fichier serveur',
            '/\bLOAD_FILE\s*\(/i' => 'lecture de fichier serveur',
            '/\bMASTER\s+TO\b|\bCHANGE\s+REPLICATION\s+SOURCE\b|\bSTART\s+REPLICA\b|\bSTOP\s+REPLICA\b/i' => 'administration de réplication',
            '/\bFLUSH\s+(?:PRIVILEGES|TABLES\s+WITH\s+READ\s+LOCK)\b/i' => 'commande administrative globale',
        ];

        foreach ($forbidden as $pattern => $reason) {
            if (preg_match($pattern, $clean) === 1) {
                throw new RuntimeException('SQL interdit dans ' . $label . ' : ' . $reason . '.');
            }
        }

        if (preg_match('/\b(?:DROP|TRUNCATE)\s+TABLE\b/i', $clean) === 1
            && stripos($clean, 'SECURITY-ALLOW-DESTRUCTIVE-SCHEMA') === false) {
            throw new RuntimeException(
                'Opération destructive refusée dans ' . $label
                . '. Une migration exceptionnelle doit contenir le marqueur SECURITY-ALLOW-DESTRUCTIVE-SCHEMA et être auditée.'
            );
        }
    }

    private static function stripComments(string $sql): string
    {
        $sql = preg_replace('~/\*.*?\*/~s', ' ', $sql) ?? $sql;
        $sql = preg_replace('/--[^\r\n]*/', ' ', $sql) ?? $sql;
        return preg_replace('/#[^\r\n]*/', ' ', $sql) ?? $sql;
    }
}
