<?php
declare(strict_types=1);

namespace App\Security;

use App\Core\Permission;

final class RouteAuthorization
{
    private const WRITE_METHODS = ['POST', 'PUT', 'PATCH', 'DELETE'];

    /** @return array{public?:bool,spaces?:list<string>,permissions?:list<string>,roles?:list<string>,write?:bool} */
    public static function policy(string $method, string $path, string $controller = ''): array
    {
        $method = strtoupper(trim($method));
        $path = self::normalize($path);
        $write = in_array($method, self::WRITE_METHODS, true);

        if (in_array($path, ['/login','/mfa','/mfa/verifier','/mfa/activer','/consentement-public','/consentement-public/signature','/professeur-consentement-public','/professeur-consentement-public/signature','/portail/activation'], true)) {
            return ['public' => true];
        }
        if ($path === '/administration/comptes-portail') {
            return ['spaces'=>['axiom'], 'permissions'=>['portal.accounts.view'], 'zero_trust_sensitive'=>true];
        }
        if (in_array($path, ['/administration/comptes-portail/invitation','/administration/comptes-portail/statut'], true)) {
            return ['spaces'=>['axiom'], 'permissions'=>['portal.accounts.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/api/axiom') return ['public' => true];

        if ($path === '/logout') return ['write' => true];
        if ($path === '/' || $path === '/dashboard') return [];
        if (preg_match('#^/espaces(?:/|$)#', $path)) return $write ? ['write'=>true] : [];
        if (preg_match('#^/mon-espace(?:/|$)#', $path)) return $write
            ? ['spaces'=>['famille'], 'write'=>true]
            : ['spaces'=>['famille']];

        if (preg_match('#^/(direction|validations-affectations)(?:/|$)#', $path)) {
            return ['spaces'=>['direction'], 'permissions'=>[$write ? 'direction.manage' : 'direction.view', 'demandes_inscription.validate']];
        }
        // SECURITY-NATIVE-015 : la route de supervision est enregistrée
        // explicitement afin que toute évolution de routes/web.php soit couplée
        // à sa politique d'autorisation native.
        if ($path === '/administration/securite/supervision') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view']];
        }
        if ($path === '/administration/securite/zero-trust') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/mfa') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/mfa/desactiver') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/mfa/codes') return [];
        if ($path === '/administration/securite/ueba') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc' || $path === '/administration/securite/soc/export.csv') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.dashboard.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/incident') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.incidents.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/affecter') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.incidents.assign'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/action') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.incidents.respond'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/rouvrir') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.incidents.close'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/rapport') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.incidents.export'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/siem' || $path === '/administration/securite/siem/export.csv') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/incidents') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view']];
        }
        if ($path === '/administration/securite/incidents/traiter') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view'], 'write'=>true];
        }
        if ($path === '/administration/securite/sessions') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view']];
        }
        if ($path === '/administration/securite/soc/playbooks') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.playbooks.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/playbooks/demarrer' || $path === '/administration/securite/soc/playbooks/etape' || $path === '/administration/securite/soc/playbooks/statut') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.playbooks.execute'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/analytics') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.analytics.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/analytics/export.csv') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.analytics.export'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/zero-trust') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.zero_trust.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/zero-trust/simuler') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.zero_trust.simulate'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/zero-trust/enregistrer' || $path === '/administration/securite/soc/zero-trust/statut') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.zero_trust.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/assistant-ia') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.ai.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/assistant-ia/interroger') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.ai.use'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/assistant-ia/configuration' || $path === '/administration/securite/assistant-ia/historique/vider') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.ai.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/validation') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.validation.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/validation/simuler') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.validation.simulate'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/confiance-packages') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.trust.view']];
        }
        if ($path === '/administration/plateforme/confiance-packages/evaluer') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.trust.evaluate'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/compatibilite-packages') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.compatibility.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/compatibilite-packages/evaluer') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.compatibility.evaluate'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/certification-packages') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.certification.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/certification-packages/certifier') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.certification.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/certification-packages/revoquer') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.certification.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/politiques-packages') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.policy.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/politiques-packages/evaluer') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.policy.evaluate'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/politiques-packages/enregistrer' || $path === '/administration/plateforme/politiques-packages/statut') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.policy.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/synchroniser') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/export.csv') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.export'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/dependances') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.dependencies.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/dependances/analyser') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.dependencies.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/reparation') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.repair'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/reparation/diagnostiquer' || $path === '/administration/plateforme/packages/reparation/executer') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.repair'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/catalogue') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.catalog.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/catalogue/preference') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.catalog.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/catalogue/comparer') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.catalog.compare'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/packages/catalogue/export.csv') {
            return ['spaces'=>['axiom'], 'permissions'=>['packages.catalog.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/rapport-compatibilite') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.report.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/rapport-compatibilite/evaluer') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.report.evaluate'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/plateforme/rapport-compatibilite/export.csv' || $path === '/administration/plateforme/rapport-compatibilite/rapport') {
            return ['spaces'=>['axiom'], 'permissions'=>['updates.report.export'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/executive') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.executive.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/executive/export.csv' || $path === '/administration/securite/executive/rapport') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.executive.export'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/audit') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.audit.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/audit/executer') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.compliance.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/audit/export.csv') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.audit.export'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/threat-intelligence') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.threat.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/threat-intelligence/ajouter') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.threat.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/soc/threat-intelligence/detecter') {
            return ['spaces'=>['axiom'], 'permissions'=>['soc.detections.run'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/systeme/sante') {
            return ['spaces'=>['axiom'], 'permissions'=>['system.health.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/systeme/sante/analyser') {
            return ['spaces'=>['axiom'], 'permissions'=>['system.health.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/systeme/sante/acquitter') {
            return ['spaces'=>['axiom'], 'permissions'=>['system.health.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/systeme/sante/baseline') {
            return ['spaces'=>['axiom'], 'permissions'=>['system.health.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/integrite-runtime') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view'], 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/integrite-runtime/analyser' || $path === '/administration/securite/integrite-runtime/politique') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.manage'], 'write'=>true, 'zero_trust_sensitive'=>true];
        }
        if ($path === '/administration/securite/integrite') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.view']];
        }
        if ($path === '/administration/securite/sessions/revoquer' || $path === '/administration/securite/sessions/revoquer-autres') {
            return ['spaces'=>['axiom'], 'permissions'=>['administration.permissions.manage'], 'write'=>true];
        }
        if ($path === '/studio/sauvegardes/export-developpeur') {
            return ['spaces'=>['axiom'], 'permissions'=>['backups.download'], 'roles'=>['super_admin','superadmin'], 'write'=>true];
        }
        if (preg_match('#^/(administration|studio)(?:/|$)#', $path)) {
            return ['spaces'=>['axiom'], 'roles'=>['super_admin','superadmin','administration','admin']];
        }
        if (preg_match('#^/mises-a-jour(?:/|$)#', $path)) {
            return ['spaces'=>['axiom'], 'permissions'=>[$write ? 'updates.install' : 'updates.view']];
        }
        if (preg_match('#^/sauvegardes(?:/|$)#', $path)) {
            return ['spaces'=>['axiom'], 'permissions'=>[$write ? 'backups.create' : 'backups.view']];
        }
        if (preg_match('#^/professeurs-pro(?:/|$)#', $path)) {
            if (!$write) return ['spaces'=>['professeur','direction'], 'permissions'=>['professeurs.pro.view']];
            if (str_contains($path, '/disciplines')) return ['spaces'=>['direction'], 'permissions'=>['professeurs.disciplines.manage']];
            if (str_contains($path, '/teletravail')) return ['spaces'=>['professeur','direction'], 'permissions'=>['professeurs.edit']];
            return ['spaces'=>['professeur','direction'], 'permissions'=>['planning.manage']];
        }
        if (preg_match('#^/demandes-inscription(?:/|$)#', $path)) {
            return ['spaces'=>['direction'], 'permissions'=>[$write ? 'demandes_inscription.validate' : 'demandes_inscription.review']];
        }
        if (preg_match('#^/inscriptions(?:/|$)#', $path)) {
            return ['spaces'=>['direction'], 'permissions'=>[$write ? 'inscriptions.create' : 'demandes_inscription.review']];
        }
        if (preg_match('#^/ateliers(?:/|$)#', $path)) {
            return ['spaces'=>['direction'], 'permissions'=>[$write ? 'ateliers.edit' : 'ateliers.view']];
        }
        if (preg_match('#^/groupes(?:/|$)#', $path)) {
            return ['spaces'=>['direction'], 'permissions'=>[$write ? 'groupes.edit' : 'groupes.view']];
        }
        if (preg_match('#^/(professeurs|professeurs-rh)(?:/|$)#', $path)) {
            return ['spaces'=>['direction'], 'permissions'=>[$write ? 'professeurs.edit' : 'professeurs.view']];
        }
        if (preg_match('#^/clients(?:/|$)#', $path)) {
            return ['spaces'=>['direction','facturation'], 'permissions'=>[$write ? 'clients.edit' : 'clients.view']];
        }
        if (preg_match('#^/(adherents|familles|membres)(?:/|$)#', $path)) {
            return ['spaces'=>['direction','facturation'], 'permissions'=>[$write ? 'familles.edit' : 'familles.view']];
        }
        if (preg_match('#^/consentements(?:/|$)#', $path)) {
            return ['spaces'=>['direction','facturation'], 'permissions'=>[$write ? 'consentements.create' : 'clients.view']];
        }
        if (preg_match('#^/(facturation|factures|paiements|comptabilite|caisses|remises|avoirs)(?:/|$)#', $path)) {
            return ['spaces'=>['facturation'], 'permissions'=>[$write ? 'facturation.edit' : 'facturation.view']];
        }
        if (preg_match('#^/tarifs(?:/|$)#', $path)) {
            return ['spaces'=>['direction','axiom'], 'permissions'=>[$write ? 'tarifs.manage' : 'tarifs.view']];
        }
        if (preg_match('#^/parametres(?:/|$)#', $path)) {
            return ['spaces'=>['direction','axiom'], 'permissions'=>[$write ? 'parametres.disciplines.manage' : 'parametres.disciplines.view']];
        }
        if (preg_match('#^/presences(?:/|$)#', $path)) {
            return ['spaces'=>['professeur','direction'], 'permissions'=>[$write ? 'presences.edit' : 'presences.view']];
        }
        if (preg_match('#^/(agenda|reunions|pilotage-disciplines)(?:/|$)#', $path)) {
            return ['spaces'=>['professeur','direction'], 'permissions'=>[$write ? 'planning.manage' : 'planning.view']];
        }
        if (preg_match('#^/(utilisateurs|roles|permissions|audit)(?:/|$)#', $path)) {
            return ['spaces'=>['axiom'], 'roles'=>['super_admin','superadmin','administration','admin']];
        }
        if (preg_match('#^/communications(?:/|$)#', $path)) {
            return ['spaces'=>['direction'], 'permissions'=>[$write ? 'communications.create' : 'communications.view']];
        }
        if (preg_match('#^/recherche-globale(?:/|$)#', $path)) {
            return ['spaces'=>['direction','facturation'], 'permissions'=>['recherche.globale.use']];
        }
        if (preg_match('#^/api(?:/|$)#', $path)) {
            return ['spaces'=>['axiom'], 'roles'=>['super_admin','superadmin','administration','admin']];
        }

        return ['permissions' => ['__route_policy_missing__']];
    }

    public static function authorize(array $policy): void
    {
        if (($policy['public'] ?? false) === true) return;

        // Une simulation de portail ne remplace jamais l'identité réelle.
        // Le Super-admin authentifié peut donc traverser les espaces pour les
        // tester, tandis que les autres comptes restent strictement cloisonnés.
        $isSuperAdmin = Permission::isSuperAdmin();
        $spaces = $policy['spaces'] ?? [];
        if (!$isSuperAdmin && $spaces !== [] && !in_array(Permission::activeSpace(), $spaces, true)) {
            self::deny('Espace non autorisé.');
        }

        $roles = $policy['roles'] ?? [];
        if ($roles !== [] && count(array_intersect(Permission::roleCodes(), $roles)) === 0) self::deny('Rôle insuffisant.');

        $permissions = $policy['permissions'] ?? [];
        if ($permissions !== [] && !Permission::canAny(...$permissions)) {
            self::deny(in_array('__route_policy_missing__', $permissions, true)
                ? 'Route privée non enregistrée dans la politique de sécurité.'
                : 'Permission insuffisante.');
        }
    }

    private static function deny(string $message): never
    {
        \App\Services\SecurityEventMonitor::record('authorization.denied', 'warning', ['reason' => $message]);
        http_response_code(403);
        header('Content-Type: text/plain; charset=utf-8');
        exit('Accès refusé. ' . $message);
    }

    private static function normalize(string $path): string
    {
        $path = str_replace('/public', '', $path) ?: '/';
        $path = '/' . trim($path, '/');
        return $path === '/' ? '/' : rtrim($path, '/');
    }
}
