<?php
/**
 * Tenant Authentication & RBAC
 */
class Auth {

    public static function startSession(): void {
        if (session_status() === PHP_SESSION_NONE) {
            session_name(SESSION_NAME);
            session_set_cookie_params([
                'lifetime' => SESSION_LIFETIME,
                'path'     => '/',
                'secure'   => false,         // set true in production with HTTPS
                'httponly' => true,
                'samesite' => 'Lax',
            ]);
            session_start();
        }
    }

    // ── Login / Logout ──────────────────────────────────────
    public static function login(array $user, array $tenant): void {
        session_regenerate_id(true);
        $_SESSION['user']      = $user;
        $_SESSION['tenant']    = $tenant;
        $_SESSION['logged_in'] = true;
        // Update last login
        db()->prepare("UPDATE users SET last_login = NOW() WHERE id = ?")
             ->execute([$user['id']]);
    }

    public static function logout(): void {
        $_SESSION = [];
        if (ini_get("session.use_cookies")) {
            $params = session_get_cookie_params();
            setcookie(session_name(), '', time() - 42000,
                $params["path"], $params["domain"],
                $params["secure"], $params["httponly"]
            );
        }
        session_destroy();
    }

    public static function check(): bool {
        return !empty($_SESSION['logged_in']) && !empty($_SESSION['user']);
    }

    public static function user(): ?array {
        return $_SESSION['user'] ?? null;
    }

    public static function tenant(): ?array {
        return $_SESSION['tenant'] ?? null;
    }

    public static function tenantId(): ?int {
        return (int)($_SESSION['tenant']['id'] ?? null) ?: null;
    }

    public static function userId(): ?int {
        return (int)($_SESSION['user']['id'] ?? null) ?: null;
    }

    public static function role(): string {
        $uid = self::userId();
        if (!$uid) return $_SESSION['user']['role'] ?? '';

        // Check for new role-based system first
        if (!empty($_SESSION['user']['role_id']) && class_exists('Role')) {
            try {
                $role = Role::getUserRole($uid);
                if ($role) {
                    // Keep session matching the DB name for legacy consistency
                    if (isset($_SESSION['user'])) {
                        $_SESSION['user']['role'] = $role['name'];
                    }
                    return $role['name'];
                }
            } catch (Exception $e) {
                // Fallback to session role if Role model fails
            }
        }
        return $_SESSION['user']['role'] ?? '';
    }

    public static function roleId(): ?int {
        return $_SESSION['user']['role_id'] ?? null;
    }

    /**
     * Refreshes the user (and optionally tenant) session data from the database.
     * Use this when a user's role or details are updated to ensure immediate consistency.
     */
    public static function refreshSession(): void {
        if (!self::check()) return;

        $uid = self::userId();
        $tid = self::tenantId();

        // Refresh User Data
        $stmt = db()->prepare("SELECT * FROM users WHERE id = ? AND tenant_id = ?");
        $stmt->execute([$uid, $tid]);
        $user = $stmt->fetch();
        if ($user) {
            $_SESSION['user'] = $user;
        }

        // Refresh Tenant Data
        $stmt = db()->prepare("SELECT * FROM tenants WHERE id = ?");
        $stmt->execute([$tid]);
        $tenant = $stmt->fetch();
        if ($tenant) {
            $_SESSION['tenant'] = $tenant;
        }
    }

    // ── RBAC ────────────────────────────────────────────────
    public static function hasRole(string|array $roles): bool {
        $userRole = strtolower(self::role());
        $roles = array_map('strtolower', (array)$roles);
        return in_array($userRole, $roles, true);
    }

    public static function can(string $permission): bool {
        $user = self::user();
        if (!$user) return false;

        // Check legacy role-based permission (for backward compatibility)
        $legacyRole = strtolower($user['role'] ?? '');
        if ($legacyRole === 'admin' || $legacyRole === 'owner') return true;

        // Check new role-based permission system
        $roleId = self::roleId();
        if ($roleId && class_exists('Role')) {
            try {
                return Role::hasPermission($roleId, $permission);
            } catch (Exception $e) {
                // Fallback to legacy if Role model fails
            }
        }

        // Fallback to legacy JSON permissions
        $perms = is_string($user['permissions'])
            ? json_decode($user['permissions'], true)
            : ($user['permissions'] ?? []);
        return !empty($perms[$permission]) || !empty($perms['all']);
    }

    // ── Guards ──────────────────────────────────────────────
    public static function requireLogin(): void {
        if (!self::check()) {
            header('Location: ' . APP_URL . '/login');
            exit;
        }
    }

    public static function requireRole(string|array $roles): void {
        self::requireLogin();
        if (!self::hasRole($roles)) {
            http_response_code(403);
            include APP_ROOT . '/views/errors/403.php';
            exit;
        }
    }

    public static function requirePermission(string $permission): void {
        self::requireLogin();
        if (!self::can($permission)) {
            http_response_code(403);
            include APP_ROOT . '/views/errors/403.php';
            exit;
        }
    }

    /**
     * Verifies that the tenant in the current session still exists in the database
     * and is in an active state. Performs an automatic logout if the tenant is missing.
     */
    public static function requireActiveTenant(): void {
        $sessionTenant = self::tenant();
        if (!$sessionTenant) { self::logout(); header('Location: '.APP_URL.'/login'); exit; }

        // Fetch latest status from DB to ensure the tenant still exists and isn't suspended
        $stmt = db()->prepare("SELECT * FROM tenants WHERE id = ? LIMIT 1");
        $stmt->execute([$sessionTenant['id']]);
        $dbTenant = $stmt->fetch();

        // 1. Check if tenant still exists (deleted case)
        if (!$dbTenant) {
            self::logout();
            flash('error', 'Your business account no longer exists. Please contact support.');
            header('Location: ' . APP_URL . '/login');
            exit;
        }

        // 2. Check current status
        if ($dbTenant['status'] === 'suspended') {
            include APP_ROOT . '/views/errors/suspended.php';
            exit;
        }

        if ($dbTenant['status'] === 'cancelled') {
            header('Location: ' . APP_URL . '/billing');
            exit;
        }

        // Keep session data fresh
        $_SESSION['tenant'] = $dbTenant;
    }
}
