<?php
namespace App\Core;

use PDO;

class Auth
{
    private static ?array $resolvedPermissionsCache = null;

    private const LEGACY_ROLE_PERMISSIONS = [
        'admin' => ['*'],
        'agent' => ['dashboard.view', 'incidents.list', 'incidents.view', 'incidents.create', 'prospect_clients.list', 'prospect_clients.view', 'prospect_clients.create', 'prospect_clients.receipt_upload', 'prospect_clients.raccordement_create'],
        'manager' => ['dashboard.view', 'incidents.list', 'incidents.view', 'incidents.create', 'prospect_clients.list', 'prospect_clients.view', 'prospect_clients.create', 'prospect_clients.receipt_upload', 'prospect_clients.raccordement_create'],
        'superviseur' => ['dashboard.view', 'incidents.list', 'incidents.view', 'incidents.create', 'prospect_clients.list', 'prospect_clients.view', 'prospect_clients.create', 'prospect_clients.receipt_upload', 'prospect_clients.raccordement_create'],
        'supervisor' => ['dashboard.view', 'incidents.list', 'incidents.view', 'incidents.create', 'prospect_clients.list', 'prospect_clients.view', 'prospect_clients.create', 'prospect_clients.receipt_upload', 'prospect_clients.raccordement_create'],
        'technicien' => ['dashboard.view', 'incidents.list_own', 'incidents.view_own', 'incidents.create', 'planning.view_own', 'planning.edit_own', 'maintenance_ftth.list_own', 'maintenance_ftth.view_own'],
        'client' => ['dashboard.view', 'incidents.list_own', 'incidents.view_own'],
    ];

    public static function start(): void
    {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            session_start();
        }
    }

    public static function user(): ?array
    {
        self::start();
        return $_SESSION['user'] ?? null;
    }

    public static function check(): bool
    {
        return self::user() !== null;
    }

    public static function login(array $user): void
    {
        self::start();
        $_SESSION['user'] = $user;
    }

    public static function logout(): void
    {
        self::start();
        $_SESSION = [];
        session_destroy();
    }

    public static function requireRole(array $roles): void
    {
        $u = self::user();
        if (!$u || !in_array($u['role_key'] ?? '', $roles, true)) {
            http_response_code(403);
            echo 'Accès refusé';
            exit;
        }
    }

    public static function permissions(): array
    {
        self::start();

        $user = self::user();
        if (!$user) {
            return [];
        }

        $roleKey = (string)($user['role_key'] ?? '');
        if ($roleKey === '') {
            return [];
        }

        $cache = self::$resolvedPermissionsCache;
        if (is_array($cache) && ($cache['role_key'] ?? null) === $roleKey && isset($cache['permissions']) && is_array($cache['permissions'])) {
            return $cache['permissions'];
        }

        $permissions = self::loadRolePermissions($roleKey);
        self::$resolvedPermissionsCache = [
            'role_key' => $roleKey,
            'permissions' => $permissions,
        ];

        return $permissions;
    }

    public static function hasPermission(string $permission): bool
    {
        $permission = trim($permission);
        if ($permission === '') {
            return false;
        }

        $permissions = self::permissions();
        return in_array('*', $permissions, true) || in_array($permission, $permissions, true);
    }

    public static function permissionScope(string $module, string $action): string
    {
        $module = trim($module);
        $action = trim($action);

        if ($module === '' || $action === '') {
            return 'none';
        }

        if (self::hasPermission($module . '.' . $action)) {
            return 'all';
        }

        if (self::hasPermission($module . '.' . $action . '_own')) {
            return 'own';
        }

        return 'none';
    }

    private static function loadRolePermissions(string $roleKey): array
    {
        try {
            $pdo = Database::pdo();
            $storage = self::detectPermissionStorage($pdo);

            if ($storage === 'json') {
                $stmt = $pdo->prepare('SELECT permissions FROM roles WHERE key_name = ? LIMIT 1');
                $stmt->execute([$roleKey]);
                $rawPermissions = $stmt->fetchColumn();
                $decodedPermissions = self::decodePermissionKeys($rawPermissions);
                if (!empty($decodedPermissions)) {
                    return $decodedPermissions;
                }
            }

            if ($storage === 'relational') {
                $stmt = $pdo->prepare('SELECT p.key_name
                    FROM roles r
                    JOIN role_permissions rp ON rp.role_id = r.id
                    JOIN permissions p ON p.id = rp.permission_id
                    WHERE r.key_name = ?');
                $stmt->execute([$roleKey]);
                $permissions = array_values(array_unique($stmt->fetchAll(PDO::FETCH_COLUMN) ?: []));
                if (!empty($permissions)) {
                    return $permissions;
                }
            }
        } catch (\Throwable $e) {
        }

        return self::LEGACY_ROLE_PERMISSIONS[$roleKey] ?? [];
    }

    private static function detectPermissionStorage(PDO $pdo): string
    {
        if (self::tableExists($pdo, 'permissions') && self::tableExists($pdo, 'role_permissions')) {
            return 'relational';
        }

        return self::columnExists($pdo, 'roles', 'permissions') ? 'json' : 'none';
    }

    private static function tableExists(PDO $pdo, string $table): bool
    {
        $stmt = $pdo->prepare('SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? LIMIT 1');
        $stmt->execute([$table]);

        return (bool)$stmt->fetchColumn();
    }

    private static function columnExists(PDO $pdo, string $table, string $column): bool
    {
        $stmt = $pdo->prepare('SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ? LIMIT 1');
        $stmt->execute([$table, $column]);

        return (bool)$stmt->fetchColumn();
    }

    private static function decodePermissionKeys($rawPermissions): array
    {
        if ($rawPermissions === null || $rawPermissions === '') {
            return [];
        }

        if (is_string($rawPermissions)) {
            $decoded = json_decode($rawPermissions, true);
            if (!is_array($decoded)) {
                return [];
            }
        } elseif (is_array($rawPermissions)) {
            $decoded = $rawPermissions;
        } else {
            return [];
        }

        $normalized = [];
        foreach ($decoded as $permission) {
            $key = trim((string)$permission);
            if ($key !== '') {
                $normalized[$key] = $key;
            }
        }

        return array_values($normalized);
    }
}
