<?php

namespace App\Middleware;

use App\Core\Request;
use App\Core\Response;
use App\Core\Database;

/**
 * AuthMiddleware - Gestion de l'authentification ET des autorisations
 * 
 * USAGE DANS LES CONTRÔLEURS:
 * ---------------------------
 * 
 * // Vérifier une permission
 * if (AuthMiddleware::can('invoices.create')) { ... }
 * 
 * // Vérifier plusieurs permissions (au moins une)
 * if (AuthMiddleware::canAny(['invoices.view', 'invoices.edit'])) { ... }
 * 
 * // Exiger une permission (stoppe si non autorisé)
 * AuthMiddleware::require('invoices.delete');
 * 
 * // Vérifier un rôle
 * if (AuthMiddleware::hasRole('admin')) { ... }
 * if (AuthMiddleware::isAdmin()) { ... }
 * 
 * // Obtenir le scope d'une permission
 * $scope = AuthMiddleware::scope('invoices.view'); // 'own', 'team', 'all'
 * 
 * // Vérifier accès à une ressource spécifique
 * if (AuthMiddleware::canAccess('invoices.edit', $invoice['created_by'])) { ... }
 * 
 * // Filtrer une requête SQL selon le scope
 * $where = AuthMiddleware::scopeWhere('invoices.view', 'created_by');
 * $sql = "SELECT * FROM invoices WHERE $where";
 * 
 * // Informations utilisateur
 * $userId = AuthMiddleware::id();
 * $user = AuthMiddleware::user();
 * $roles = AuthMiddleware::roles();
 */
class AuthMiddleware
{
    private static ?array $permissions = null;
    private static ?array $roles = null;
    private static ?array $user = null;
    private static bool $initialized = false;
    
    // =========================================================================
    // MIDDLEWARE PRINCIPAL
    // =========================================================================
    
    /**
     * Middleware : Vérifier l'authentification
     */
    public static function handle(Request $request, callable $next)
    {
        // Vérifier si l'utilisateur est connecté
        if (!isset($_SESSION['user_id'])) {
            // Tenter la connexion automatique via remember_token
            if (self::attemptRememberLogin()) {
                return $next($request);
            }
            
            // Rediriger vers login
            return Response::redirect('/xproject/public/login');
        }
        
        // Initialiser les permissions
        self::init();
        
        return $next($request);
    }
    
    /**
     * Tentative de connexion automatique via remember_token
     */
    private static function attemptRememberLogin(): bool
    {
        if (!isset($_COOKIE['remember_token']) || !isset($_COOKIE['user_id'])) {
            return false;
        }
        
        $userId = (int) $_COOKIE['user_id'];
        $token = $_COOKIE['remember_token'];
        
        $db = Database::getInstance()->getConnection();
        $stmt = $db->prepare("SELECT * FROM users WHERE id = ? AND remember_token = ? AND status = 'active'");
        $stmt->execute([$userId, $token]);
        $user = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        if (!$user) {
            // Token invalide, supprimer les cookies
            setcookie('remember_token', '', time() - 3600, '/');
            setcookie('user_id', '', time() - 3600, '/');
            return false;
        }
        
        // Recréer la session
        $_SESSION['user_id'] = $user['id'];
        $_SESSION['user_email'] = $user['email'];
        $_SESSION['user_name'] = trim($user['first_name'] . ' ' . $user['last_name']);
        $_SESSION['tenant_id'] = $user['tenant_id'] ?? 1;
        
        session_regenerate_id(true);
        
        return true;
    }
    
    // =========================================================================
    // INITIALISATION
    // =========================================================================
    
    /**
     * Initialiser les permissions de l'utilisateur
     */
    public static function init(): void
    {
        if (self::$initialized) {
            return;
        }
        
        $userId = $_SESSION['user_id'] ?? null;
        
        if (!$userId) {
            self::$permissions = [];
            self::$roles = [];
            self::$initialized = true;
            return;
        }
        
        $db = Database::getInstance()->getConnection();
        
        // Charger l'utilisateur
        $stmt = $db->prepare("SELECT * FROM users WHERE id = ? AND status = 'active'");
        $stmt->execute([$userId]);
        self::$user = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        if (!self::$user) {
            self::$permissions = [];
            self::$roles = [];
            self::$initialized = true;
            return;
        }
        
        // Charger les rôles
        $stmt = $db->prepare("
            SELECT r.* 
            FROM roles r
            INNER JOIN user_roles ur ON r.id = ur.role_id
            WHERE ur.user_id = ? 
              AND r.is_active = 1
              AND (ur.valid_until IS NULL OR ur.valid_until > NOW())
            ORDER BY r.level DESC
        ");
        $stmt->execute([$userId]);
        self::$roles = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        
        // Charger les permissions via les rôles
        $roleIds = array_column(self::$roles, 'id');
        
        if (empty($roleIds)) {
            self::$permissions = [];
            self::$initialized = true;
            return;
        }
        
        $placeholders = implode(',', array_fill(0, count($roleIds), '?'));
        $stmt = $db->prepare("
            SELECT p.code, p.module, p.action, rp.scope, rp.conditions
            FROM permissions p
            INNER JOIN role_permissions rp ON p.id = rp.permission_id
            WHERE rp.role_id IN ($placeholders)
              AND p.is_active = 1
        ");
        $stmt->execute($roleIds);
        $perms = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        
        // Indexer les permissions par code
        self::$permissions = [];
        foreach ($perms as $p) {
            // Si permission déjà présente, garder le scope le plus large
            if (isset(self::$permissions[$p['code']])) {
                $existing = self::$permissions[$p['code']]['scope'];
                $new = $p['scope'];
                // all > team > own
                if ($new === 'all' || ($new === 'team' && $existing === 'own')) {
                    self::$permissions[$p['code']]['scope'] = $new;
                }
            } else {
                self::$permissions[$p['code']] = [
                    'module' => $p['module'],
                    'action' => $p['action'],
                    'scope' => $p['scope'],
                    'conditions' => $p['conditions'] ? json_decode($p['conditions'], true) : null
                ];
            }
        }
        
        self::$initialized = true;
    }
    
    // =========================================================================
    // VÉRIFICATION DES PERMISSIONS
    // =========================================================================
    
    /**
     * Vérifie si l'utilisateur a une permission
     * @param string $permission Code de permission (ex: 'invoices.create')
     */
    public static function can(string $permission): bool
    {
        self::init();
        
        // Super admin a toutes les permissions
        if (self::isSuperAdmin()) {
            return true;
        }
        
        return isset(self::$permissions[$permission]);
    }
    
    /**
     * Vérifie si l'utilisateur a AU MOINS UNE permission
     */
    public static function canAny(array $permissions): bool
    {
        foreach ($permissions as $p) {
            if (self::can($p)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Vérifie si l'utilisateur a TOUTES les permissions
     */
    public static function canAll(array $permissions): bool
    {
        foreach ($permissions as $p) {
            if (!self::can($p)) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * Exige une permission - stoppe l'exécution si non autorisé
     */
    public static function require(string $permission): void
    {
        if (!self::can($permission)) {
            self::denyAccess("Permission requise : $permission");
        }
    }
    
    /**
     * Exige au moins une permission
     */
    public static function requireAny(array $permissions): void
    {
        if (!self::canAny($permissions)) {
            self::denyAccess("Une des permissions requises : " . implode(', ', $permissions));
        }
    }
    
    /**
     * Exige d'être admin
     */
    public static function requireAdmin(): void
    {
        if (!self::isAdmin()) {
            self::denyAccess("Accès réservé aux administrateurs");
        }
    }
    
    // =========================================================================
    // GESTION DES SCOPES
    // =========================================================================
    
    /**
     * Retourne le scope d'une permission ('own', 'team', 'all')
     */
    public static function scope(string $permission): ?string
    {
        self::init();
        
        if (self::isSuperAdmin()) {
            return 'all';
        }
        
        return self::$permissions[$permission]['scope'] ?? null;
    }
    
    /**
     * Vérifie si l'utilisateur peut accéder à une ressource selon le scope
     * @param string $permission La permission à vérifier
     * @param int $ownerId ID du propriétaire de la ressource
     * @param int|null $teamId ID de l'équipe de la ressource (optionnel)
     */
    public static function canAccess(string $permission, int $ownerId, ?int $teamId = null): bool
    {
        if (!self::can($permission)) {
            return false;
        }
        
        $scope = self::scope($permission);
        $userId = self::id();
        $userTeamId = self::$user['team_id'] ?? null;
        
        switch ($scope) {
            case 'all':
                return true;
            case 'team':
                return $ownerId === $userId || ($teamId !== null && $teamId === $userTeamId);
            case 'own':
            default:
                return $ownerId === $userId;
        }
    }
    
    /**
     * Génère une clause WHERE SQL selon le scope
     * @param string $permission La permission
     * @param string $ownerColumn Nom de la colonne propriétaire (ex: 'created_by')
     * @param string $teamColumn Nom de la colonne équipe (ex: 'team_id')
     * @return string Clause SQL (sans WHERE)
     */
    public static function scopeWhere(string $permission, string $ownerColumn = 'created_by', string $teamColumn = 'team_id'): string
    {
        $scope = self::scope($permission);
        $userId = self::id();
        $teamId = self::$user['team_id'] ?? null;
        
        switch ($scope) {
            case 'all':
                return '1=1';
            case 'team':
                if ($teamId) {
                    return "($ownerColumn = $userId OR $teamColumn = $teamId)";
                }
                return "$ownerColumn = $userId";
            case 'own':
            default:
                return "$ownerColumn = $userId";
        }
    }
    
    // =========================================================================
    // VÉRIFICATION DES RÔLES
    // =========================================================================
    
    /**
     * Vérifie si l'utilisateur a un rôle spécifique
     */
    public static function hasRole(string $roleCode): bool
    {
        self::init();
        
        foreach (self::$roles as $role) {
            if ($role['code'] === $roleCode) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Vérifie si l'utilisateur a au moins un des rôles
     */
    public static function hasAnyRole(array $roleCodes): bool
    {
        foreach ($roleCodes as $code) {
            if (self::hasRole($code)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Vérifie si super admin
     */
    public static function isSuperAdmin(): bool
    {
        return self::hasRole('super_admin');
    }
    
    /**
     * Vérifie si admin (super_admin ou admin)
     */
    public static function isAdmin(): bool
    {
        return self::hasAnyRole(['super_admin', 'admin']);
    }
    
    /**
     * Vérifie si manager ou plus
     */
    public static function isManager(): bool
    {
        return self::hasAnyRole(['super_admin', 'admin', 'manager']);
    }
    
    /**
     * Retourne le niveau max des rôles de l'utilisateur
     */
    public static function level(): int
    {
        self::init();
        
        $maxLevel = 0;
        foreach (self::$roles as $role) {
            if ($role['level'] > $maxLevel) {
                $maxLevel = $role['level'];
            }
        }
        return $maxLevel;
    }
    
    // =========================================================================
    // INFORMATIONS UTILISATEUR
    // =========================================================================
    
    /**
     * Retourne l'utilisateur connecté
     */
    public static function user(): ?array
    {
        self::init();
        return self::$user;
    }
    
    /**
     * Retourne l'ID de l'utilisateur
     */
    public static function id(): ?int
    {
        return $_SESSION['user_id'] ?? null;
    }
    
    /**
     * Retourne le tenant_id
     */
    public static function tenantId(): int
    {
        return $_SESSION['tenant_id'] ?? 1;
    }
    
    /**
     * Retourne les rôles de l'utilisateur
     */
    public static function roles(): array
    {
        self::init();
        return self::$roles;
    }
    
    /**
     * Retourne toutes les permissions
     */
    public static function permissions(): array
    {
        self::init();
        return self::$permissions;
    }
    
    /**
     * Vérifie si connecté
     */
    public static function isLoggedIn(): bool
    {
        return isset($_SESSION['user_id']);
    }
    
    /**
     * Vérifie si l'utilisateur est connecté (alias)
     */
    public static function guest(): bool
    {
        return !self::isLoggedIn();
    }
    
    // =========================================================================
    // UTILITAIRES
    // =========================================================================
    
    /**
     * Réinitialise le cache (après modification des permissions)
     */
    public static function refresh(): void
    {
        self::$permissions = null;
        self::$roles = null;
        self::$user = null;
        self::$initialized = false;
        self::init();
    }
    
    /**
     * Refuse l'accès et retourne une erreur
     */
    private static function denyAccess(string $message = "Accès non autorisé"): void
    {
        $isAjax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) 
            && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
        
        if ($isAjax || self::isApiRequest()) {
            http_response_code(403);
            header('Content-Type: application/json');
            echo json_encode([
                'success' => false,
                'error' => $message,
                'code' => 403
            ]);
            exit;
        }
        
        // Stocker le message et rediriger
        $_SESSION['error'] = $message;
        header('Location: /xproject/public/dashboard');
        exit;
    }
    
    /**
     * Vérifie si c'est une requête API
     */
    private static function isApiRequest(): bool
    {
        $uri = $_SERVER['REQUEST_URI'] ?? '';
        return strpos($uri, '/api/') !== false;
    }
    
    // =========================================================================
    // HELPERS POUR VUES (Blade-like)
    // =========================================================================
    
    /**
     * Retourne les permissions pour le JS frontend
     */
    public static function toJson(): string
    {
        self::init();
        
        return json_encode([
            'user' => [
                'id' => self::id(),
                'name' => self::$user['first_name'] ?? '' . ' ' . self::$user['last_name'] ?? '',
                'email' => self::$user['email'] ?? ''
            ],
            'roles' => array_column(self::$roles, 'code'),
            'permissions' => array_keys(self::$permissions),
            'isAdmin' => self::isAdmin(),
            'isSuperAdmin' => self::isSuperAdmin()
        ]);
    }
    
    // =========================================================================
    // LEGACY - Compatibilité avec l'ancien système
    // =========================================================================
    
    /**
     * @deprecated Utiliser Auth::requireAdmin() ou Auth::can()
     */
    public static function requireRole(string $role)
    {
        $roles = [
            'super_admin' => 100,
            'admin' => 90,
            'manager' => 70,
            'user' => 10
        ];
        
        $requiredLevel = $roles[$role] ?? 0;
        
        if (self::level() < $requiredLevel) {
            self::denyAccess("Rôle requis : $role");
        }
        
        return null;
    }
}
