<?php
class Auth
{
    private static ?array $user = null;
    private static ?array $permissions = null;

    public static function login(string $username, string $password, Request $request): array
    {
        $db = Database::getInstance();

        // Rate limit check
        $maxAttempts = (int) Config::get('max_login_attempts', 5);
        $lockoutMinutes = (int) Config::get('lockout_duration_minutes', 30);

        $user = $db->fetchOne(
            "SELECT * FROM auth_users WHERE username = ? AND is_deleted = 0",
            [$username]
        );

        if (!$user) {
            self::recordAttempt($username, $request, false, 'User not found');
            return ['success' => false, 'message' => 'Invalid username or password'];
        }

        if ($user['is_locked']) {
            $lockedAt = strtotime($user['locked_at']);
            $unlockAt = $lockedAt + ($lockoutMinutes * 60);
            if (time() < $unlockAt) {
                self::recordAttempt($username, $request, false, 'Account locked');
                $remaining = ceil(($unlockAt - time()) / 60);
                return ['success' => false, 'message' => "Account locked. Try again in {$remaining} minutes."];
            }
            // Unlock
            $db->update('auth_users', [
                'is_locked' => 0,
                'failed_login_attempts' => 0,
                'locked_at' => null,
                'lock_reason' => null,
            ], 'id = ?', [$user['id']]);
            $user['is_locked'] = 0;
            $user['failed_login_attempts'] = 0;
        }

        if (!$user['is_active']) {
            self::recordAttempt($username, $request, false, 'Account inactive');
            return ['success' => false, 'message' => 'Account is inactive'];
        }

        if (!password_verify($password, $user['password_hash'])) {
            $attempts = $user['failed_login_attempts'] + 1;
            $updates = ['failed_login_attempts' => $attempts];
            if ($attempts >= $maxAttempts) {
                $updates['is_locked'] = 1;
                $updates['locked_at'] = date('Y-m-d H:i:s');
                $updates['lock_reason'] = 'Too many failed login attempts';
            }
            $db->update('auth_users', $updates, 'id = ?', [$user['id']]);
            self::recordAttempt($username, $request, false, 'Wrong password');
            self::logLogin($user['id'], $username, 'login_failed', $request, 'Wrong password');
            return ['success' => false, 'message' => 'Invalid username or password'];
        }

        // Success
        $db->update('auth_users', [
            'failed_login_attempts' => 0,
            'last_login_at' => date('Y-m-d H:i:s'),
            'last_login_ip' => $request->ip(),
        ], 'id = ?', [$user['id']]);

        // Load permissions
        $perms = self::loadPermissions($user['id']);

        // Create session record
        $sessionToken = bin2hex(random_bytes(32));
        $expiresAt = date('Y-m-d H:i:s', time() + 3600);

        $db->insert('auth_sessions', [
            'user_id' => $user['id'],
            'session_token' => $sessionToken,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'expires_at' => $expiresAt,
            'is_active' => 1,
        ]);

        // Set session data
        Session::regenerate();
        Session::set('auth_user_id', $user['id']);
        Session::set('auth_user', [
            'id' => $user['id'],
            'username' => $user['username'],
            'email' => $user['email'],
            'full_name_en' => $user['full_name_en'],
            'full_name_ar' => $user['full_name_ar'],
            'avatar_path' => $user['avatar_path'],
            'must_change_password' => $user['must_change_password'],
        ]);
        Session::set('auth_permissions', $perms);
        Session::set('auth_session_token', $sessionToken);
        Session::set('_session_timeout', (int) Config::get('session_timeout_minutes', 60) * 60);

        self::recordAttempt($username, $request, true, null);
        self::logLogin($user['id'], $username, 'login_success', $request);

        self::$user = null; // Reset static cache
        self::$permissions = null;

        return ['success' => true, 'user' => $user];
    }

    public static function logout(Request $request): void
    {
        $db = Database::getInstance();
        $token = Session::get('auth_session_token');
        $userId = Session::get('auth_user_id');
        $username = Session::get('auth_user')['username'] ?? '';

        if ($token) {
            $db->update('auth_sessions', [
                'is_active' => 0,
                'terminated_reason' => 'logout',
            ], 'session_token = ?', [$token]);
        }

        if ($userId) {
            self::logLogin($userId, $username, 'logout', $request);
        }

        Session::destroy();
        self::$user = null;
        self::$permissions = null;
    }

    public static function check(): bool
    {
        return Session::has('auth_user_id');
    }

    public static function id(): ?int
    {
        return Session::get('auth_user_id');
    }

    public static function user(?string $field = null)
    {
        if (self::$user === null) {
            $sessionUser = Session::get('auth_user');
            if (!$sessionUser) {
                return null;
            }
            // Load full user from DB on first access per request
            try {
                $db = Database::getInstance();
                self::$user = $db->fetchOne(
                    "SELECT id, username, email, full_name_en, full_name_ar, phone, avatar_path, is_active, must_change_password, timezone, language_id, preferences FROM auth_users WHERE id = ? AND is_deleted = 0",
                    [$sessionUser['id']]
                );
            } catch (\Exception $e) {
                self::$user = $sessionUser;
            }
        }

        if (self::$user === null) {
            return null;
        }

        if ($field !== null) {
            return self::$user[$field] ?? null;
        }

        return self::$user;
    }

    public static function permissions(): array
    {
        if (self::$permissions === null) {
            self::$permissions = Session::get('auth_permissions', []);
        }
        return self::$permissions;
    }

    private static function loadPermissions(int $userId): array
    {
        $db = Database::getInstance();

        // Check if user has super_admin role
        $isSuperAdmin = $db->fetchColumn(
            "SELECT COUNT(*) FROM auth_user_roles ur
             JOIN auth_roles r ON ur.role_id = r.id
             WHERE ur.user_id = ? AND r.role_code = 'super_admin' AND ur.is_active = 1 AND r.is_active = 1
             AND (ur.expires_at IS NULL OR ur.expires_at > NOW())",
            [$userId]
        );

        if ($isSuperAdmin > 0) {
            return ['*']; // Wildcard = all permissions
        }

        $perms = $db->fetchAll(
            "SELECT DISTINCT p.permission_code
             FROM auth_user_roles ur
             JOIN auth_role_permissions rp ON ur.role_id = rp.role_id
             JOIN auth_permissions p ON rp.permission_id = p.id
             WHERE ur.user_id = ? AND ur.is_active = 1 AND p.is_active = 1
             AND (ur.expires_at IS NULL OR ur.expires_at > NOW())",
            [$userId]
        );

        return array_column($perms, 'permission_code');
    }

    private static function recordAttempt(string $username, Request $request, bool $success, ?string $reason): void
    {
        try {
            $db = Database::getInstance();
            $db->insert('auth_login_attempts', [
                'username' => $username,
                'ip_address' => $request->ip(),
                'user_agent' => mb_substr($request->userAgent(), 0, 500),
                'success' => $success ? 1 : 0,
                'failure_reason' => $reason,
            ]);
        } catch (\Exception $e) {
            // Silently fail
        }
    }

    private static function logLogin(?int $userId, string $username, string $action, Request $request, ?string $failureReason = null): void
    {
        try {
            $db = Database::getInstance();
            $db->insert('login_log', [
                'user_id' => $userId,
                'username' => $username,
                'action' => $action,
                'ip_address' => $request->ip(),
                'user_agent' => mb_substr($request->userAgent(), 0, 500),
                'failure_reason' => $failureReason,
            ]);
        } catch (\Exception $e) {
            // Silently fail
        }
    }
}