<?php

namespace VulnTitan\Services;

use WP_Error;
use WP_User;

class LoginSecurityService
{
    protected const META_2FA_ENABLED = 'vulntitan_2fa_enabled';
    protected const META_2FA_SECRET = 'vulntitan_2fa_secret';
    protected const META_2FA_RECOVERY_CODES = 'vulntitan_2fa_recovery_codes';
    protected const META_2FA_TRUSTED_DEVICES = 'vulntitan_2fa_trusted_devices';
    protected const PROFILE_NONCE = 'vulntitan_2fa_profile_nonce';
    protected const PROFILE_ACTION = 'vulntitan_2fa_profile';
    protected const PROFILE_SETUP_QUERY_ARG = 'vulntitan_2fa_setup';
    protected const CHALLENGE_NONCE = 'vulntitan_2fa_challenge_nonce';
    protected const CHALLENGE_ACTION = 'vulntitan_2fa_challenge';
    protected const CHALLENGE_PREFIX = 'vulntitan_2fa_challenge_';
    protected const RECOVERY_PREVIEW_PREFIX = 'vulntitan_2fa_recovery_preview_';
    protected const TRUSTED_COOKIE = 'vulntitan_2fa_trusted';
    protected const CHALLENGE_TTL = 10 * MINUTE_IN_SECONDS;
    protected const RECOVERY_PREVIEW_TTL = 15 * MINUTE_IN_SECONDS;
    protected const ATTEMPT_PREFIX = 'vulntitan_2fa_attempt_';
    protected const BLOCK_PREFIX = 'vulntitan_2fa_block_';
    protected const MAX_CHALLENGE_ATTEMPTS = 5;
    protected const ATTEMPT_WINDOW = 10 * MINUTE_IN_SECONDS;
    protected const BLOCK_TTL = 10 * MINUTE_IN_SECONDS;
    protected static bool $booted = false;

    public static function boot(): void
    {
        if (self::$booted) {
            return;
        }

        self::$booted = true;

        add_filter('authenticate', [__CLASS__, 'maybeStartTwoFactorChallenge'], 60, 3);
        add_action('login_form_vulntitan_2fa', [__CLASS__, 'renderTwoFactorChallenge']);
        add_action('admin_enqueue_scripts', [__CLASS__, 'enqueueProfileAssets']);
        add_action('admin_init', [__CLASS__, 'enforceTwoFactorEnrollment']);
        add_action('admin_notices', [__CLASS__, 'renderEnrollmentNotice']);
        add_action('show_user_profile', [__CLASS__, 'renderProfileSection']);
        add_action('personal_options_update', [__CLASS__, 'handlePendingProfileEnrollment']);
        add_action('profile_update', [__CLASS__, 'saveProfileSection'], 10, 1);
        add_action('user_profile_update_errors', [__CLASS__, 'validateProfilePassword'], 10, 3);
        add_action('validate_password_reset', [__CLASS__, 'validatePasswordReset'], 10, 2);
        add_filter('registration_errors', [__CLASS__, 'validateRegistrationPassword'], 20, 3);
    }

    public static function enqueueProfileAssets(string $hookSuffix): void
    {
        if ($hookSuffix !== 'profile.php' && $hookSuffix !== 'user-edit.php') {
            return;
        }

        $profileScriptPath = 'assets/js/login-security-profile.js';
        $profileScriptMinPath = 'assets/js/login-security-profile.min.js';
        $profileScriptRelativePath = (defined('VULNTITAN_DEVELOPMENT') && VULNTITAN_DEVELOPMENT === true)
            ? $profileScriptPath
            : $profileScriptMinPath;

        wp_enqueue_script(
            'vulntitan-jquery-qrcode',
            VULNTITAN_PLUGIN_URL . '/assets/js/jquery.qrcode.min.js',
            ['jquery'],
            self::assetVersion('assets/js/jquery.qrcode.min.js'),
            true
        );

        wp_enqueue_script(
            'vulntitan-login-security-profile',
            VULNTITAN_PLUGIN_URL . '/' . $profileScriptRelativePath,
            ['jquery', 'vulntitan-jquery-qrcode'],
            self::assetVersion($profileScriptRelativePath),
            true
        );

        wp_localize_script('vulntitan-login-security-profile', 'VulnTitanLoginSecurityProfile', [
            'step_enable_first' => __('Turn on Step 2 first, then enter the current code from your authenticator app here.', 'vulntitan'),
            'step_enter_code' => __('Enter the current 6-digit code from your authenticator app, then click Update Profile.', 'vulntitan'),
            'step_ready' => __('Ready. Click Update Profile to finish setup and generate recovery codes.', 'vulntitan'),
        ]);

        wp_add_inline_style('common', self::getProfileInlineCss());
    }

    public static function maybeStartTwoFactorChallenge($user, $username, $password)
    {
        if (!$user instanceof WP_User) {
            return $user;
        }

        if (!self::isTwoFactorGloballyEnabled()) {
            return $user;
        }

        if ((defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) || (defined('REST_REQUEST') && REST_REQUEST)) {
            return $user;
        }

        if (defined('DOING_AJAX') && DOING_AJAX) {
            return $user;
        }

        if (sanitize_key((string) ($_REQUEST['action'] ?? '')) === 'vulntitan_2fa') {
            return $user;
        }

        if (!self::requiresTwoFactor($user)) {
            return $user;
        }

        $rateLimit = self::getTwoFactorRateLimitState($user->ID, FirewallService::detectClientIp());
        if (!empty($rateLimit['blocked'])) {
            return new WP_Error(
                'vulntitan_2fa_rate_limited',
                self::getTwoFactorRateLimitMessage((int) ($rateLimit['remaining_seconds'] ?? self::BLOCK_TTL))
            );
        }

        $challengeId = self::createChallenge($user);
        $redirectUrl = add_query_arg([
            'action' => 'vulntitan_2fa',
            'vt_challenge' => $challengeId,
        ], wp_login_url());

        wp_safe_redirect($redirectUrl);
        exit;
    }

    public static function renderTwoFactorChallenge(): void
    {
        $challengeId = sanitize_text_field((string) ($_REQUEST['vt_challenge'] ?? ''));
        $challenge = self::getChallenge($challengeId);
        $errorMessage = '';

        if (!$challenge || empty($challenge['user_id'])) {
            self::renderChallengePage(
                __('Two-Factor Authentication', 'vulntitan'),
                __('Your verification session expired. Please sign in again.', 'vulntitan'),
                [],
                true
            );
        }

        $user = get_user_by('id', (int) $challenge['user_id']);
        if (!$user instanceof WP_User) {
            self::deleteChallenge($challengeId);
            self::renderChallengePage(
                __('Two-Factor Authentication', 'vulntitan'),
                __('Unable to load the user account for this verification session.', 'vulntitan'),
                [],
                true
            );
        }

        $ipAddress = FirewallService::detectClientIp();
        $rateLimit = self::getTwoFactorRateLimitState($user->ID, $ipAddress);
        if (!empty($rateLimit['blocked'])) {
            self::deleteChallenge($challengeId);
            self::renderChallengePage(
                __('Two-Factor Authentication', 'vulntitan'),
                self::getTwoFactorRateLimitMessage((int) ($rateLimit['remaining_seconds'] ?? self::BLOCK_TTL)),
                [],
                true
            );
        }

        if (strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')) === 'POST') {
            $nonce = isset($_POST[self::CHALLENGE_NONCE]) ? (string) wp_unslash($_POST[self::CHALLENGE_NONCE]) : '';
            if ($nonce === '' || !wp_verify_nonce($nonce, self::CHALLENGE_ACTION)) {
                $errorMessage = __('Security validation failed. Please try again.', 'vulntitan');
            } else {
                $totpCode = self::sanitizeCodeInput((string) ($_POST['vulntitan_2fa_code'] ?? ''));
                $recoveryCode = self::sanitizeCodeInput((string) ($_POST['vulntitan_recovery_code'] ?? ''));

                $isValid = false;
                if ($totpCode !== '') {
                    $isValid = self::verifyTotpCode($user->ID, $totpCode);
                } elseif ($recoveryCode !== '') {
                    $isValid = self::consumeRecoveryCode($user->ID, $recoveryCode);
                }

                if ($isValid) {
                    self::deleteChallenge($challengeId);
                    self::clearTwoFactorRateLimitState($user->ID, $ipAddress);
                    self::finalizeAuthenticatedLogin(
                        $user,
                        !empty($challenge['remember']),
                        !empty($_POST['vulntitan_remember_device'])
                    );
                    $redirectTo = is_string($challenge['redirect_to'] ?? '') ? (string) $challenge['redirect_to'] : '';
                    $redirectTo = $redirectTo !== '' ? $redirectTo : admin_url();
                    wp_safe_redirect($redirectTo);
                    exit;
                }

                FirewallService::logEvent('login_failed', [
                    'ip_address' => FirewallService::detectClientIp(),
                    'username' => $user->user_login,
                    'user_id' => $user->ID,
                    'event_source' => 'login_security',
                    'rule_group' => 'two_factor',
                    'rule_id' => 'totp_verification_failed',
                    'reason' => 'Invalid two-factor verification code.',
                ]);

                $rateLimit = self::recordFailedTwoFactorAttempt($user, $ipAddress);
                if (!empty($rateLimit['blocked'])) {
                    self::deleteChallenge($challengeId);
                    self::renderChallengePage(
                        __('Two-Factor Authentication', 'vulntitan'),
                        self::getTwoFactorRateLimitMessage((int) ($rateLimit['remaining_seconds'] ?? self::BLOCK_TTL)),
                        [],
                        true
                    );
                }

                $errorMessage = __('The verification code was invalid. Please try again.', 'vulntitan');
            }
        }

        $recoveryCount = count(self::getRecoveryCodeHashes($user->ID));
        self::renderChallengePage(
            __('Two-Factor Authentication', 'vulntitan'),
            $errorMessage,
            [
                'challenge_id' => $challengeId,
                'user' => $user,
                'recovery_count' => $recoveryCount,
                'trusted_days' => self::getTrustedDeviceDays(),
            ]
        );
    }

    public static function enforceTwoFactorEnrollment(): void
    {
        if (!is_user_logged_in()) {
            return;
        }

        if (defined('DOING_CRON') && DOING_CRON) {
            return;
        }

        if (defined('WP_CLI') && WP_CLI) {
            return;
        }

        $user = wp_get_current_user();
        if (!$user instanceof WP_User) {
            return;
        }

        if (!self::requiresTwoFactorEnrollment($user)) {
            return;
        }

        if (self::isEnrollmentRequestAllowed($user)) {
            return;
        }

        if (self::isAjaxRequest()) {
            wp_send_json_error([
                'message' => esc_html__('Two-factor authentication is required before you can use the admin dashboard.', 'vulntitan'),
            ], 403);
        }

        wp_safe_redirect(self_admin_url('profile.php#vulntitan-login-security'));
        exit;
    }

    public static function renderEnrollmentNotice(): void
    {
        if (!is_user_logged_in()) {
            return;
        }

        $user = wp_get_current_user();
        if (!$user instanceof WP_User) {
            return;
        }

        if (!self::requiresTwoFactorEnrollment($user)) {
            return;
        }

        $profileUrl = self_admin_url('profile.php#vulntitan-login-security');

        echo '<div class="notice notice-warning"><p>'
            . esc_html__('Two-factor authentication is required for your role. Please enable 2FA on your profile to continue using the admin dashboard.', 'vulntitan')
            . ' <a class="button button-primary" href="' . esc_url($profileUrl) . '">'
            . esc_html__('Set up 2FA now', 'vulntitan')
            . '</a></p></div>';
    }

    public static function renderProfileSection($user): void
    {
        if (!$user instanceof WP_User) {
            return;
        }

        if ((int) get_current_user_id() !== (int) $user->ID) {
            return;
        }

        if (!current_user_can('edit_user', $user->ID)) {
            return;
        }

        $isEligible = self::isEligibleRole($user);
        $enabled = self::isUserTwoFactorEnabled($user->ID);
        $globalEnabled = self::isTwoFactorGloballyEnabled();

        echo '<div id="vulntitan-login-security">';
        echo '<h2>' . esc_html__('VulnTitan Login Security', 'vulntitan') . '</h2>';
        echo '<table class="form-table" role="presentation">';
        echo '<tbody>';
        echo '<tr>';
        echo '<th scope="row">' . esc_html__('Two-Factor Authentication', 'vulntitan') . '</th>';
        echo '<td>';
        wp_nonce_field(self::PROFILE_ACTION, self::PROFILE_NONCE);

        if (!$isEligible) {
            echo '<p>' . esc_html__('Two-factor authentication is currently available only for roles selected in VulnTitan Firewall settings.', 'vulntitan') . '</p>';
            echo '</td></tr></tbody></table>';
            return;
        }

        $secret = self::getOrProvisionSecret($user->ID);
        $recoveryPreview = get_transient(self::RECOVERY_PREVIEW_PREFIX . $user->ID);
        $recoveryCount = count(self::getRecoveryCodeHashes($user->ID));
        $otpAuthUri = self::buildOtpAuthUri($user, $secret);
        $trustedDays = self::getTrustedDeviceDays();
        $profileSetupNotice = self::getProfileSetupNotice();
        $isPendingSetupRetry = !empty($profileSetupNotice['message']);
        $trustedDeviceLabel = $trustedDays > 0
            ? sprintf(__('Trusted device window: %d day(s)', 'vulntitan'), $trustedDays)
            : __('Trusted device window: disabled (2FA required on every sign-in)', 'vulntitan');
        $statusLabel = '';
        $statusClass = '';
        $statusMessage = '';

        if ($enabled && $globalEnabled) {
            $statusLabel = __('Active', 'vulntitan');
            $statusClass = 'is-active';
            $statusMessage = __('Two-factor authentication is active for this account.', 'vulntitan');
        } elseif ($enabled) {
            $statusLabel = __('Provisioned', 'vulntitan');
            $statusClass = 'is-waiting';
            $statusMessage = __('This account is enrolled, but global 2FA enforcement is currently disabled in VulnTitan Firewall settings.', 'vulntitan');
        } else {
            $statusLabel = __('Not enabled', 'vulntitan');
            $statusClass = 'is-pending';
            $statusMessage = __('Follow the quick setup below, then click Update Profile to finish activation.', 'vulntitan');
        }

        echo '<div class="vulntitan-2fa-panel">';
        echo '<div class="vulntitan-2fa-status ' . esc_attr($statusClass) . '">';
        echo '<span class="vulntitan-2fa-status-badge">' . esc_html($statusLabel) . '</span>';
        echo '<div class="vulntitan-2fa-status-copy">';
        echo '<strong>' . esc_html($statusMessage) . '</strong>';
        if (!$globalEnabled) {
            echo '<p>' . esc_html__('Global enforcement is off, so sign-in challenges will not be required until you enable 2FA in VulnTitan Firewall > Identity Security.', 'vulntitan') . '</p>';
        } else {
            echo '<p>' . esc_html__('When active, this account will require a 6-digit code after the password step.', 'vulntitan') . '</p>';
        }
        echo '</div>';
        echo '</div>';

        if (!$enabled) {
            echo '<div class="vulntitan-2fa-wizard vulntitan-2fa-profile-setup" data-vulntitan-2fa-wizard data-vulntitan-otp-uri="' . esc_attr($otpAuthUri) . '" data-vulntitan-2fa-enabled="0" data-vulntitan-2fa-error="' . esc_attr($isPendingSetupRetry ? '1' : '0') . '">';
            echo '<div class="vulntitan-2fa-wizard-head">';
            echo '<h3>' . esc_html__('Quick setup', 'vulntitan') . '</h3>';
            echo '<p>' . esc_html__('Use these four steps in order. The only action that finishes setup is clicking Update Profile at the bottom of this page.', 'vulntitan') . '</p>';
            echo '</div>';

            echo '<div class="vulntitan-2fa-steps">';
            echo '<section class="vulntitan-2fa-step-card" data-vulntitan-step="scan">';
            echo '<span class="vulntitan-2fa-step-kicker">' . esc_html__('Step 1', 'vulntitan') . '</span>';
            echo '<h4>' . esc_html__('Scan the QR code', 'vulntitan') . '</h4>';
            echo '<p>' . esc_html__('Open your authenticator app and scan this QR code.', 'vulntitan') . '</p>';
            echo '<div class="vulntitan-2fa-qr-shell"><div class="vulntitan-2fa-qr" data-vulntitan-2fa-qr aria-label="' . esc_attr__('Authenticator QR code', 'vulntitan') . '"></div></div>';
            echo '</section>';

            echo '<section class="vulntitan-2fa-step-card" data-vulntitan-step="toggle">';
            echo '<span class="vulntitan-2fa-step-kicker">' . esc_html__('Step 2', 'vulntitan') . '</span>';
            echo '<h4>' . esc_html__('Turn on 2FA for this account', 'vulntitan') . '</h4>';
            echo '<label class="vulntitan-2fa-toggle" for="vulntitan-2fa-enabled">';
            echo '<input type="checkbox" id="vulntitan-2fa-enabled" name="vulntitan_2fa_enabled" value="1" ' . checked($isPendingSetupRetry, true, false) . '>';
            echo '<span>' . esc_html__('Enable 2FA for this account', 'vulntitan') . '</span>';
            echo '</label>';
            echo '<p class="description">' . esc_html__('Check this box after you scan the QR code.', 'vulntitan') . '</p>';
            echo '</section>';

            echo '<section id="vulntitan-2fa-step-verify" class="vulntitan-2fa-step-card" data-vulntitan-step="verify">';
            echo '<span class="vulntitan-2fa-step-kicker">' . esc_html__('Step 3', 'vulntitan') . '</span>';
            echo '<h4>' . esc_html__('Enter the current code from your app', 'vulntitan') . '</h4>';
            echo '<p>' . esc_html__('This confirms that the QR code was scanned correctly.', 'vulntitan') . '</p>';
            if (!empty($profileSetupNotice['message'])) {
                echo '<div id="vulntitan-2fa-inline-notice" class="notice inline notice-error vulntitan-2fa-inline-notice" data-vulntitan-2fa-inline-notice><p>' . esc_html($profileSetupNotice['message']) . '</p></div>';
            }
            echo '<p><label for="vulntitan-2fa-verification-code"><strong>' . esc_html__('6-digit confirmation code', 'vulntitan') . '</strong></label><br>';
            echo '<input type="text" id="vulntitan-2fa-verification-code" name="vulntitan_2fa_verification_code" class="regular-text code" value="" inputmode="numeric" autocomplete="one-time-code" placeholder="123456" maxlength="8"></p>';
            echo '<p class="description" data-vulntitan-2fa-verify-help>' . esc_html__('Turn on the checkbox in Step 2, then enter the current code from your authenticator app here.', 'vulntitan') . '</p>';
            echo '</section>';

            echo '<section class="vulntitan-2fa-step-card" data-vulntitan-step="save">';
            echo '<span class="vulntitan-2fa-step-kicker">' . esc_html__('Step 4', 'vulntitan') . '</span>';
            echo '<h4>' . esc_html__('Finish setup', 'vulntitan') . '</h4>';
            echo '<p>' . esc_html__('Click Update Profile to activate 2FA and generate your recovery codes.', 'vulntitan') . '</p>';
            echo '<p class="description">' . esc_html__('Recovery codes appear only after the profile is saved successfully.', 'vulntitan') . '</p>';
            echo '</section>';
            echo '</div>';

            echo '<details class="vulntitan-2fa-advanced">';
            echo '<summary>' . esc_html__('Need manual setup instead?', 'vulntitan') . '</summary>';
            echo '<div class="vulntitan-2fa-advanced-body">';
            echo '<p><strong>' . esc_html__('Setup secret', 'vulntitan') . '</strong><br>';
            echo '<code>' . esc_html(self::formatSecretForDisplay($secret)) . '</code></p>';
            echo '<p><strong>' . esc_html__('Authenticator URI', 'vulntitan') . '</strong><br>';
            echo '<code class="vulntitan-2fa-code-block">' . esc_html($otpAuthUri) . '</code></p>';
            echo '<p class="description">' . esc_html__('Use manual setup only if your authenticator app cannot scan the QR code.', 'vulntitan') . '</p>';
            echo '<ul class="vulntitan-2fa-meta-list">';
            echo '<li>' . esc_html__('Code type: Time-based (TOTP)', 'vulntitan') . '</li>';
            echo '<li>' . esc_html__('Code length: 6 digits', 'vulntitan') . '</li>';
            echo '<li>' . esc_html__('Refresh period: 30 seconds', 'vulntitan') . '</li>';
            echo '<li>' . esc_html($trustedDeviceLabel) . '</li>';
            echo '</ul>';
            echo '</div>';
            echo '</details>';
            echo '</div>';
        } else {
            echo '<div class="vulntitan-2fa-manage">';
            echo '<div class="vulntitan-2fa-manage-card">';
            echo '<h3>' . esc_html__('2FA is active', 'vulntitan') . '</h3>';
            echo '<label class="vulntitan-2fa-toggle" for="vulntitan-2fa-enabled">';
            echo '<input type="checkbox" id="vulntitan-2fa-enabled" name="vulntitan_2fa_enabled" value="1" ' . checked($enabled, true, false) . '>';
            echo '<span>' . esc_html__('Keep 2FA enabled for this account', 'vulntitan') . '</span>';
            echo '</label>';
            echo '<p class="description">' . esc_html__('Clear this checkbox and click Update Profile if you want to disable 2FA for this account.', 'vulntitan') . '</p>';
            echo '<p class="description">' . esc_html($trustedDeviceLabel) . '</p>';
            echo '</div>';

            echo '<div class="vulntitan-2fa-manage-card">';
            echo '<h3>' . esc_html__('Recovery codes', 'vulntitan') . '</h3>';
            echo '<p><strong>' . esc_html(sprintf(__('Active recovery codes: %d', 'vulntitan'), $recoveryCount)) . '</strong></p>';

            if (is_array($recoveryPreview) && !empty($recoveryPreview)) {
                echo '<div class="notice inline notice-success"><p><strong>' . esc_html__('Save these new recovery codes now.', 'vulntitan') . '</strong> ' . esc_html__('They are shown only once and each code can be used a single time.', 'vulntitan') . '</p></div>';
                echo '<ul style="margin:8px 0 0 18px;">';
                foreach ($recoveryPreview as $code) {
                    echo '<li><code>' . esc_html((string) $code) . '</code></li>';
                }
                echo '</ul>';
                delete_transient(self::RECOVERY_PREVIEW_PREFIX . $user->ID);
            } else {
                echo '<p class="description">' . esc_html__('Use a recovery code only when you cannot access your authenticator app.', 'vulntitan') . '</p>';
            }

            echo '<p><label for="vulntitan-2fa-regenerate-recovery">';
            echo '<input type="checkbox" id="vulntitan-2fa-regenerate-recovery" name="vulntitan_2fa_regenerate_recovery" value="1"> ';
            echo esc_html__('Generate a fresh set of recovery codes on the next profile save', 'vulntitan');
            echo '</label></p>';
            echo '<p class="description">' . esc_html__('Generating a new set invalidates all previously issued recovery codes for this account.', 'vulntitan') . '</p>';
            echo '</div>';

            echo '<details class="vulntitan-2fa-advanced vulntitan-2fa-advanced--manage">';
            echo '<summary>' . esc_html__('Show setup details for a new device', 'vulntitan') . '</summary>';
            echo '<div class="vulntitan-2fa-advanced-body vulntitan-2fa-profile-setup" data-vulntitan-otp-uri="' . esc_attr($otpAuthUri) . '" data-vulntitan-2fa-enabled="1">';
            echo '<div class="vulntitan-2fa-qr-shell"><div class="vulntitan-2fa-qr" data-vulntitan-2fa-qr aria-label="' . esc_attr__('Authenticator QR code', 'vulntitan') . '"></div></div>';
            echo '<p><strong>' . esc_html__('Setup secret', 'vulntitan') . '</strong><br>';
            echo '<code>' . esc_html(self::formatSecretForDisplay($secret)) . '</code></p>';
            echo '<p><strong>' . esc_html__('Authenticator URI', 'vulntitan') . '</strong><br>';
            echo '<code class="vulntitan-2fa-code-block">' . esc_html($otpAuthUri) . '</code></p>';
            echo '</div>';
            echo '</details>';
            echo '</div>';
        }

        echo '</div>';

        echo '</td>';
        echo '</tr>';
        echo '</tbody>';
        echo '</table>';
        echo '</div>';
    }

    public static function handlePendingProfileEnrollment(int $userId): void
    {
        if ((int) get_current_user_id() !== $userId) {
            return;
        }

        if (!current_user_can('edit_user', $userId)) {
            return;
        }

        $nonce = isset($_POST[self::PROFILE_NONCE]) ? (string) wp_unslash($_POST[self::PROFILE_NONCE]) : '';
        if ($nonce === '' || !wp_verify_nonce($nonce, self::PROFILE_ACTION)) {
            return;
        }

        $user = get_user_by('id', $userId);
        if (!$user instanceof WP_User || !self::isEligibleRole($user)) {
            return;
        }

        $requestedEnable = !empty($_POST['vulntitan_2fa_enabled']);
        $alreadyEnabled = self::isUserTwoFactorEnabled($userId);

        if (!$requestedEnable || $alreadyEnabled) {
            return;
        }

        $verificationCode = self::sanitizeCodeInput((string) ($_POST['vulntitan_2fa_verification_code'] ?? ''));
        if ($verificationCode === '') {
            self::redirectWithProfileSetupState('missing_code');
        }

        if (!self::verifyTotpCode($userId, $verificationCode)) {
            self::redirectWithProfileSetupState('invalid_code');
        }
    }

    public static function saveProfileSection(int $userId): void
    {
        if ((int) get_current_user_id() !== $userId) {
            return;
        }

        if (!current_user_can('edit_user', $userId)) {
            return;
        }

        $nonce = isset($_POST[self::PROFILE_NONCE]) ? (string) wp_unslash($_POST[self::PROFILE_NONCE]) : '';
        if ($nonce === '' || !wp_verify_nonce($nonce, self::PROFILE_ACTION)) {
            return;
        }

        $user = get_user_by('id', $userId);
        if (!$user instanceof WP_User || !self::isEligibleRole($user)) {
            return;
        }

        $enabled = !empty($_POST['vulntitan_2fa_enabled']) ? 1 : 0;
        $regenerateRecovery = !empty($_POST['vulntitan_2fa_regenerate_recovery']);

        if ($enabled) {
            self::getOrProvisionSecret($userId);
        }

        update_user_meta($userId, self::META_2FA_ENABLED, $enabled);

        if ($enabled && ($regenerateRecovery || !self::getRecoveryCodeHashes($userId))) {
            self::provisionRecoveryCodes($userId, true);
        }

        if (!$enabled) {
            delete_user_meta($userId, self::META_2FA_TRUSTED_DEVICES);
            self::clearTrustedDeviceCookie();
        }
    }

    /**
     * @param bool $update
     * @param object $user
     */
    public static function validateProfilePassword(WP_Error $errors, bool $update, $user): void
    {
        if (!self::isWeakPasswordBlockingEnabled()) {
            return;
        }

        $password = isset($_POST['pass1']) ? (string) wp_unslash($_POST['pass1']) : '';
        if ($password === '') {
            return;
        }

        if (self::isStrongPassword($password)) {
            return;
        }

        $errors->add(
            'vulntitan_weak_password',
            self::getWeakPasswordMessage()
        );
    }

    public static function validatePasswordReset(WP_Error $errors, $user): void
    {
        if (!self::isWeakPasswordBlockingEnabled()) {
            return;
        }

        $password = isset($_POST['pass1']) ? (string) wp_unslash($_POST['pass1']) : '';
        if ($password === '' || self::isStrongPassword($password)) {
            return;
        }

        $errors->add(
            'vulntitan_weak_password',
            self::getWeakPasswordMessage()
        );
    }

    public static function validateRegistrationPassword(WP_Error $errors, $sanitizedUserLogin, $userEmail): WP_Error
    {
        if (!self::isWeakPasswordBlockingEnabled()) {
            return $errors;
        }

        $password = isset($_POST['pass1']) ? (string) wp_unslash($_POST['pass1']) : '';
        if ($password === '' || self::isStrongPassword($password)) {
            return $errors;
        }

        $errors->add(
            'vulntitan_weak_password',
            self::getWeakPasswordMessage()
        );

        return $errors;
    }

    protected static function createChallenge(WP_User $user): string
    {
        $challengeId = wp_generate_password(32, false, false);
        $remember = !empty($_POST['rememberme']);
        $redirectTo = isset($_REQUEST['redirect_to']) ? (string) wp_unslash($_REQUEST['redirect_to']) : '';

        set_transient(self::CHALLENGE_PREFIX . $challengeId, [
            'user_id' => $user->ID,
            'remember' => $remember ? 1 : 0,
            'redirect_to' => $redirectTo,
        ], self::CHALLENGE_TTL);

        return $challengeId;
    }

    protected static function getChallenge(string $challengeId): ?array
    {
        if ($challengeId === '') {
            return null;
        }

        $challenge = get_transient(self::CHALLENGE_PREFIX . $challengeId);

        return is_array($challenge) ? $challenge : null;
    }

    protected static function deleteChallenge(string $challengeId): void
    {
        if ($challengeId === '') {
            return;
        }

        delete_transient(self::CHALLENGE_PREFIX . $challengeId);
    }

    protected static function getTwoFactorRateLimitState(int $userId, string $ipAddress): array
    {
        $blockedUntil = (int) get_transient(self::getTwoFactorBlockKey($userId, $ipAddress));
        if ($blockedUntil > time()) {
            return [
                'blocked' => true,
                'remaining_seconds' => max(1, $blockedUntil - time()),
            ];
        }

        if ($blockedUntil > 0) {
            delete_transient(self::getTwoFactorBlockKey($userId, $ipAddress));
        }

        return [
            'blocked' => false,
            'remaining_seconds' => 0,
        ];
    }

    protected static function recordFailedTwoFactorAttempt(WP_User $user, string $ipAddress): array
    {
        $attemptKey = self::getTwoFactorAttemptKey($user->ID, $ipAddress);
        $data = get_transient($attemptKey);

        if (!is_array($data)) {
            $data = [
                'count' => 0,
                'started_at' => time(),
            ];
        }

        $data['count'] = max(0, (int) ($data['count'] ?? 0)) + 1;
        $data['started_at'] = (int) ($data['started_at'] ?? time());
        set_transient($attemptKey, $data, self::ATTEMPT_WINDOW);

        if ((int) $data['count'] < self::MAX_CHALLENGE_ATTEMPTS) {
            return [
                'blocked' => false,
                'remaining_seconds' => 0,
            ];
        }

        $blockedUntil = time() + self::BLOCK_TTL;
        set_transient(self::getTwoFactorBlockKey($user->ID, $ipAddress), $blockedUntil, self::BLOCK_TTL);
        delete_transient($attemptKey);

        FirewallService::logEvent('login_blocked', [
            'ip_address' => $ipAddress,
            'username' => $user->user_login,
            'user_id' => $user->ID,
            'event_source' => 'login_security',
            'rule_group' => 'two_factor',
            'rule_id' => 'totp_rate_limited',
            'reason' => 'Too many invalid two-factor verification attempts.',
            'details' => [
                'attempts' => (int) $data['count'],
                'retry_after_seconds' => self::BLOCK_TTL,
            ],
        ]);

        return [
            'blocked' => true,
            'remaining_seconds' => self::BLOCK_TTL,
        ];
    }

    protected static function clearTwoFactorRateLimitState(int $userId, string $ipAddress): void
    {
        delete_transient(self::getTwoFactorAttemptKey($userId, $ipAddress));
        delete_transient(self::getTwoFactorBlockKey($userId, $ipAddress));
    }

    protected static function getTwoFactorAttemptKey(int $userId, string $ipAddress): string
    {
        return self::ATTEMPT_PREFIX . md5($userId . '|' . ($ipAddress !== '' ? $ipAddress : 'unknown'));
    }

    protected static function getTwoFactorBlockKey(int $userId, string $ipAddress): string
    {
        return self::BLOCK_PREFIX . md5($userId . '|' . ($ipAddress !== '' ? $ipAddress : 'unknown'));
    }

    protected static function getTwoFactorRateLimitMessage(int $remainingSeconds): string
    {
        return sprintf(
            __('Too many two-factor verification attempts. Try again in %d minute(s).', 'vulntitan'),
            max(1, (int) ceil($remainingSeconds / MINUTE_IN_SECONDS))
        );
    }

    protected static function renderChallengePage(string $title, string $message = '', array $context = [], bool $isFatal = false): void
    {
        login_header($title, '<p class="message">' . esc_html__('Enter the code from your authenticator app or use one of your recovery codes.', 'vulntitan') . '</p>');

        if ($message !== '') {
            $containerId = $isFatal ? 'login_error' : 'message';
            echo '<div id="' . esc_attr($containerId) . '" class="message">' . esc_html($message) . '</div>';
        }

        if ($isFatal) {
            echo '<p><a href="' . esc_url(wp_login_url()) . '">' . esc_html__('Return to sign in', 'vulntitan') . '</a></p>';
            login_footer();
            exit;
        }

        $challengeId = (string) ($context['challenge_id'] ?? '');
        $recoveryCount = (int) ($context['recovery_count'] ?? 0);
        $trustedDays = (int) ($context['trusted_days'] ?? 0);

        echo '<form name="vulntitan_2faform" id="loginform" action="' . esc_url(add_query_arg([
            'action' => 'vulntitan_2fa',
            'vt_challenge' => $challengeId,
        ], wp_login_url())) . '" method="post">';
        wp_nonce_field(self::CHALLENGE_ACTION, self::CHALLENGE_NONCE);
        echo '<p>';
        echo '<label for="vulntitan_2fa_code">' . esc_html__('Authenticator code', 'vulntitan') . '</label>';
        echo '<input type="text" name="vulntitan_2fa_code" id="vulntitan_2fa_code" class="input" value="" size="20" inputmode="numeric" autocomplete="one-time-code">';
        echo '</p>';
        echo '<p class="description" style="margin-top:-8px;">' . esc_html__('Use the current 6-digit code from your authenticator app.', 'vulntitan') . '</p>';
        echo '<p>';
        echo '<label for="vulntitan_recovery_code">' . esc_html__('Recovery code', 'vulntitan') . '</label>';
        echo '<input type="text" name="vulntitan_recovery_code" id="vulntitan_recovery_code" class="input" value="" size="20" autocomplete="off">';
        echo '</p>';
        echo '<p class="description" style="margin-top:-8px;">' . esc_html(sprintf(__('You currently have %d recovery codes remaining. Use one only if you cannot access your authenticator app.', 'vulntitan'), $recoveryCount)) . '</p>';

        if ($trustedDays > 0) {
            echo '<p><label for="vulntitan_remember_device">';
            echo '<input type="checkbox" name="vulntitan_remember_device" id="vulntitan_remember_device" value="1"> ';
            echo esc_html(sprintf(__('Remember this device for %d day(s)', 'vulntitan'), $trustedDays));
            echo '</label></p>';
        }

        echo '<p class="submit">';
        echo '<input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="' . esc_attr__('Verify and sign in', 'vulntitan') . '">';
        echo '</p>';
        echo '<p style="margin-top:16px;"><a href="' . esc_url(wp_login_url()) . '">' . esc_html__('Start over', 'vulntitan') . '</a></p>';
        echo '</form>';
        login_footer();
        exit;
    }

    protected static function finalizeAuthenticatedLogin(WP_User $user, bool $remember, bool $rememberDevice): void
    {
        wp_set_current_user($user->ID, $user->user_login);
        wp_set_auth_cookie($user->ID, $remember);
        do_action('wp_login', $user->user_login, $user);

        if ($rememberDevice && self::getTrustedDeviceDays() > 0) {
            self::rememberTrustedDevice($user->ID);
        }
    }

    protected static function requiresTwoFactorEnrollment(WP_User $user): bool
    {
        if (!self::isTwoFactorGloballyEnabled()) {
            return false;
        }

        if (!self::isEligibleRole($user)) {
            return false;
        }

        if (!current_user_can('edit_user', $user->ID)) {
            return false;
        }

        if (self::isUserTwoFactorEnabled($user->ID) && self::getUserSecret($user->ID) !== '') {
            return false;
        }

        return true;
    }

    protected static function isEnrollmentRequestAllowed(WP_User $user): bool
    {
        if (self::isProfileRequestForUser($user)) {
            return true;
        }

        if (self::isAjaxRequest()) {
            $action = sanitize_key((string) ($_REQUEST['action'] ?? ''));

            return $action === 'heartbeat';
        }

        return false;
    }

    protected static function isProfileRequestForUser(WP_User $user): bool
    {
        global $pagenow;

        if ($pagenow === 'profile.php') {
            return true;
        }

        if ($pagenow === 'user-edit.php') {
            $userId = isset($_GET['user_id']) ? (int) $_GET['user_id'] : 0;

            return $userId === (int) $user->ID;
        }

        return false;
    }

    protected static function isAjaxRequest(): bool
    {
        if (function_exists('wp_doing_ajax') && wp_doing_ajax()) {
            return true;
        }

        return defined('DOING_AJAX') && DOING_AJAX;
    }

    protected static function requiresTwoFactor(WP_User $user): bool
    {
        if (!self::isEligibleRole($user)) {
            return false;
        }

        if (!self::isUserTwoFactorEnabled($user->ID)) {
            return false;
        }

        if (self::getUserSecret($user->ID) === '') {
            return false;
        }

        return !self::isTrustedDevice($user->ID);
    }

    protected static function isEligibleRole(WP_User $user): bool
    {
        $roles = is_array($user->roles) ? $user->roles : [];
        $requiredRoles = self::getTwoFactorRoles();

        if (!$requiredRoles) {
            return false;
        }

        foreach ($roles as $role) {
            if (in_array($role, $requiredRoles, true)) {
                return true;
            }
        }

        return false;
    }

    protected static function getTwoFactorRoles(): array
    {
        $settings = FirewallService::getSettings();
        $roles = $settings['two_factor_roles'] ?? ['administrator', 'editor'];

        if (!is_array($roles)) {
            return ['administrator', 'editor'];
        }

        $roles = array_values(array_filter($roles, 'is_string'));

        return $roles ?: ['administrator', 'editor'];
    }

    protected static function isTwoFactorGloballyEnabled(): bool
    {
        $settings = FirewallService::getSettings();

        return !empty($settings['two_factor_enabled']);
    }

    protected static function isUserTwoFactorEnabled(int $userId): bool
    {
        return !empty(get_user_meta($userId, self::META_2FA_ENABLED, true));
    }

    protected static function getUserSecret(int $userId): string
    {
        return trim((string) get_user_meta($userId, self::META_2FA_SECRET, true));
    }

    protected static function getOrProvisionSecret(int $userId): string
    {
        $secret = self::getUserSecret($userId);
        if ($secret !== '') {
            return $secret;
        }

        $secret = self::generateBase32Secret();
        update_user_meta($userId, self::META_2FA_SECRET, $secret);

        return $secret;
    }

    protected static function provisionRecoveryCodes(int $userId, bool $storePreview = false): void
    {
        $codes = [];
        $hashes = [];

        for ($index = 0; $index < 8; $index++) {
            $code = self::generateRecoveryCode();
            $codes[] = $code;
            $hashes[] = wp_hash_password($code);
        }

        update_user_meta($userId, self::META_2FA_RECOVERY_CODES, $hashes);

        if ($storePreview) {
            set_transient(self::RECOVERY_PREVIEW_PREFIX . $userId, $codes, self::RECOVERY_PREVIEW_TTL);
        }
    }

    protected static function getRecoveryCodeHashes(int $userId): array
    {
        $hashes = get_user_meta($userId, self::META_2FA_RECOVERY_CODES, true);

        return is_array($hashes) ? array_values(array_filter($hashes, 'is_string')) : [];
    }

    protected static function verifyTotpCode(int $userId, string $code): bool
    {
        $secret = self::getUserSecret($userId);
        if ($secret === '' || $code === '') {
            return false;
        }

        $currentSlice = (int) floor(time() / 30);

        for ($offset = -1; $offset <= 1; $offset++) {
            if (hash_equals(self::buildTotpCode($secret, $currentSlice + $offset), $code)) {
                return true;
            }
        }

        return false;
    }

    protected static function consumeRecoveryCode(int $userId, string $code): bool
    {
        $hashes = self::getRecoveryCodeHashes($userId);
        if (!$hashes) {
            return false;
        }

        foreach ($hashes as $index => $hash) {
            if (!wp_check_password($code, $hash)) {
                continue;
            }

            unset($hashes[$index]);
            update_user_meta($userId, self::META_2FA_RECOVERY_CODES, array_values($hashes));

            return true;
        }

        return false;
    }

    protected static function rememberTrustedDevice(int $userId): void
    {
        $trustedDays = self::getTrustedDeviceDays();
        if ($trustedDays <= 0) {
            return;
        }

        $devices = self::getTrustedDevices($userId);
        $selector = bin2hex(random_bytes(6));
        $validator = bin2hex(random_bytes(16));
        $expires = time() + ($trustedDays * DAY_IN_SECONDS);

        $devices[$selector] = [
            'hash' => hash('sha256', $validator),
            'expires' => $expires,
            'created_at' => time(),
        ];

        uasort($devices, static function (array $left, array $right): int {
            return (int) ($right['created_at'] ?? 0) <=> (int) ($left['created_at'] ?? 0);
        });
        $devices = array_slice($devices, 0, 10, true);

        update_user_meta($userId, self::META_2FA_TRUSTED_DEVICES, $devices);
        self::setTrustedDeviceCookie($userId, $selector, $validator, $expires);
    }

    protected static function isTrustedDevice(int $userId): bool
    {
        $cookie = isset($_COOKIE[self::TRUSTED_COOKIE]) ? (string) wp_unslash($_COOKIE[self::TRUSTED_COOKIE]) : '';
        if ($cookie === '') {
            return false;
        }

        $parts = explode('|', $cookie);
        if (count($parts) !== 3) {
            self::clearTrustedDeviceCookie();
            return false;
        }

        $cookieUserId = (int) $parts[0];
        $selector = sanitize_key((string) $parts[1]);
        $validator = preg_replace('/[^a-f0-9]/i', '', (string) $parts[2]);

        if ($cookieUserId !== $userId || $selector === '' || $validator === '') {
            return false;
        }

        $devices = self::getTrustedDevices($userId);
        $device = $devices[$selector] ?? null;

        if (!is_array($device)) {
            self::clearTrustedDeviceCookie();
            return false;
        }

        $expires = (int) ($device['expires'] ?? 0);
        if ($expires < time()) {
            unset($devices[$selector]);
            update_user_meta($userId, self::META_2FA_TRUSTED_DEVICES, $devices);
            self::clearTrustedDeviceCookie();
            return false;
        }

        if (!hash_equals((string) ($device['hash'] ?? ''), hash('sha256', $validator))) {
            self::clearTrustedDeviceCookie();
            return false;
        }

        return true;
    }

    protected static function getTrustedDevices(int $userId): array
    {
        $devices = get_user_meta($userId, self::META_2FA_TRUSTED_DEVICES, true);
        $devices = is_array($devices) ? $devices : [];
        $changed = false;

        foreach ($devices as $selector => $device) {
            if (!is_array($device) || (int) ($device['expires'] ?? 0) < time()) {
                unset($devices[$selector]);
                $changed = true;
            }
        }

        if ($changed) {
            update_user_meta($userId, self::META_2FA_TRUSTED_DEVICES, $devices);
        }

        return $devices;
    }

    protected static function setTrustedDeviceCookie(int $userId, string $selector, string $validator, int $expires): void
    {
        $value = $userId . '|' . $selector . '|' . $validator;
        $secure = is_ssl();

        self::setCookieForPath(COOKIEPATH, $value, $expires, $secure);
        if (SITECOOKIEPATH !== COOKIEPATH) {
            self::setCookieForPath(SITECOOKIEPATH, $value, $expires, $secure);
        }
    }

    protected static function clearTrustedDeviceCookie(): void
    {
        $expires = time() - HOUR_IN_SECONDS;
        $secure = is_ssl();

        self::setCookieForPath(COOKIEPATH, '', $expires, $secure);
        if (SITECOOKIEPATH !== COOKIEPATH) {
            self::setCookieForPath(SITECOOKIEPATH, '', $expires, $secure);
        }
    }

    protected static function setCookieForPath(string $path, string $value, int $expires, bool $secure): void
    {
        if ($path === '') {
            $path = '/';
        }

        setcookie(self::TRUSTED_COOKIE, $value, [
            'expires' => $expires,
            'path' => $path,
            'domain' => defined('COOKIE_DOMAIN') ? COOKIE_DOMAIN : '',
            'secure' => $secure,
            'httponly' => true,
            'samesite' => 'Lax',
        ]);
    }

    protected static function getTrustedDeviceDays(): int
    {
        $settings = FirewallService::getSettings();

        return max(0, (int) ($settings['trusted_device_days'] ?? 30));
    }

    protected static function isWeakPasswordBlockingEnabled(): bool
    {
        $settings = FirewallService::getSettings();

        return !empty($settings['weak_password_blocking_enabled']);
    }

    protected static function isStrongPassword(string $password): bool
    {
        if (strlen($password) < 12) {
            return false;
        }

        $categories = 0;
        $categories += preg_match('/[a-z]/', $password) ? 1 : 0;
        $categories += preg_match('/[A-Z]/', $password) ? 1 : 0;
        $categories += preg_match('/\d/', $password) ? 1 : 0;
        $categories += preg_match('/[^a-zA-Z\d]/', $password) ? 1 : 0;

        return $categories >= 3;
    }

    protected static function getWeakPasswordMessage(): string
    {
        return __('Choose a stronger password with at least 12 characters and a mix of uppercase, lowercase, numbers, or symbols.', 'vulntitan');
    }

    protected static function buildTotpCode(string $secret, int $timeSlice): string
    {
        $binarySecret = self::base32Decode($secret);
        if ($binarySecret === '') {
            return '';
        }

        $time = pack('N*', 0) . pack('N*', $timeSlice);
        $hash = hash_hmac('sha1', $time, $binarySecret, true);
        $offset = ord(substr($hash, -1)) & 0x0F;
        $segment = substr($hash, $offset, 4);
        $value = unpack('N', $segment);
        $value = $value ? reset($value) : 0;
        $value = $value & 0x7FFFFFFF;

        return str_pad((string) ($value % 1000000), 6, '0', STR_PAD_LEFT);
    }

    protected static function generateBase32Secret(): string
    {
        return self::base32Encode(random_bytes(20));
    }

    protected static function generateRecoveryCode(): string
    {
        return strtoupper(substr(bin2hex(random_bytes(4)), 0, 4) . '-' . substr(bin2hex(random_bytes(4)), 0, 4));
    }

    protected static function buildOtpAuthUri(WP_User $user, string $secret): string
    {
        $siteName = (string) wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES);
        $label = rawurlencode($siteName . ':' . $user->user_login);
        $issuer = rawurlencode($siteName);

        return 'otpauth://totp/' . $label . '?secret=' . rawurlencode($secret) . '&issuer=' . $issuer . '&digits=6&period=30';
    }

    protected static function formatSecretForDisplay(string $secret): string
    {
        return trim(chunk_split($secret, 4, ' '));
    }

    protected static function sanitizeCodeInput(string $value): string
    {
        return strtoupper((string) preg_replace('/[^A-Z0-9]/i', '', trim($value)));
    }

    protected static function base32Encode(string $data): string
    {
        $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
        $binary = '';
        $length = strlen($data);

        for ($index = 0; $index < $length; $index++) {
            $binary .= str_pad(decbin(ord($data[$index])), 8, '0', STR_PAD_LEFT);
        }

        $chunks = str_split($binary, 5);
        $output = '';

        foreach ($chunks as $chunk) {
            if (strlen($chunk) < 5) {
                $chunk = str_pad($chunk, 5, '0', STR_PAD_RIGHT);
            }

            $output .= $alphabet[bindec($chunk)];
        }

        return $output;
    }

    protected static function base32Decode(string $secret): string
    {
        $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
        $secret = strtoupper(preg_replace('/[^A-Z2-7]/', '', $secret));
        if ($secret === '') {
            return '';
        }

        $binary = '';
        $length = strlen($secret);

        for ($index = 0; $index < $length; $index++) {
            $position = strpos($alphabet, $secret[$index]);
            if ($position === false) {
                return '';
            }

            $binary .= str_pad(decbin($position), 5, '0', STR_PAD_LEFT);
        }

        $bytes = str_split($binary, 8);
        $output = '';

        foreach ($bytes as $byte) {
            if (strlen($byte) < 8) {
                continue;
            }

            $output .= chr(bindec($byte));
        }

        return $output;
    }

    protected static function assetVersion(string $relativePath): string
    {
        $file = VULNTITAN_PLUGIN_DIR . '/' . ltrim($relativePath, '/');
        if (!file_exists($file)) {
            return (string) VULNTITAN_PLUGIN_VERSION;
        }

        $mtime = filemtime($file);

        return $mtime !== false ? (string) $mtime : (string) VULNTITAN_PLUGIN_VERSION;
    }

    protected static function getProfileInlineCss(): string
    {
        return <<<'CSS'
            .vulntitan-2fa-panel {
                display: grid;
                gap: 18px;
                margin: 12px 0 0;
            }
            .vulntitan-2fa-status {
                display: grid;
                grid-template-columns: auto 1fr;
                gap: 14px;
                align-items: start;
                padding: 16px 18px;
                border-radius: 14px;
                border: 1px solid #d7dde5;
                background: linear-gradient(180deg, #ffffff, #f7fafc);
                box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
            }
            .vulntitan-2fa-status.is-active {
                border-color: #86efac;
                background: linear-gradient(180deg, #f0fdf4, #ecfdf5);
            }
            .vulntitan-2fa-status.is-waiting {
                border-color: #fde68a;
                background: linear-gradient(180deg, #fffbeb, #fefce8);
            }
            .vulntitan-2fa-status.is-pending {
                border-color: #bfdbfe;
                background: linear-gradient(180deg, #eff6ff, #f8fbff);
            }
            .vulntitan-2fa-status-badge {
                display: inline-flex;
                align-items: center;
                min-height: 30px;
                padding: 0 12px;
                border-radius: 999px;
                background: #0f172a;
                color: #ffffff;
                font-size: 11px;
                font-weight: 700;
                letter-spacing: 0.08em;
                text-transform: uppercase;
            }
            .vulntitan-2fa-status-copy strong {
                display: block;
                margin-bottom: 4px;
                font-size: 14px;
                color: #0f172a;
            }
            .vulntitan-2fa-status-copy p {
                margin: 0;
                color: #475569;
                line-height: 1.6;
            }
            .vulntitan-2fa-wizard,
            .vulntitan-2fa-manage {
                display: grid;
                gap: 16px;
            }
            .vulntitan-2fa-wizard-head h3,
            .vulntitan-2fa-manage-card h3 {
                margin: 0 0 6px;
                font-size: 16px;
                line-height: 1.35;
            }
            .vulntitan-2fa-wizard-head p,
            .vulntitan-2fa-manage-card p {
                margin: 0;
                color: #475569;
                line-height: 1.6;
            }
            .vulntitan-2fa-steps {
                display: grid;
                grid-template-columns: repeat(2, minmax(0, 1fr));
                gap: 16px;
            }
            .vulntitan-2fa-step-card,
            .vulntitan-2fa-manage-card {
                padding: 18px;
                border: 1px solid #d0d7de;
                border-radius: 14px;
                background: linear-gradient(180deg, #ffffff, #f8fafc);
                box-shadow: 0 10px 24px rgba(15, 23, 42, 0.04);
                transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;
            }
            .vulntitan-2fa-step-card.is-active {
                border-color: #93c5fd;
                box-shadow: 0 14px 28px rgba(37, 99, 235, 0.10);
                transform: translateY(-1px);
            }
            .vulntitan-2fa-step-card.is-complete {
                border-color: #86efac;
                background: linear-gradient(180deg, #f0fdf4, #f7fee7);
            }
            .vulntitan-2fa-step-card.is-muted {
                opacity: 0.72;
            }
            .vulntitan-2fa-step-kicker {
                display: inline-flex;
                align-items: center;
                min-height: 28px;
                padding: 0 10px;
                margin-bottom: 12px;
                border-radius: 999px;
                background: #e2e8f0;
                color: #334155;
                font-size: 11px;
                font-weight: 700;
                letter-spacing: 0.08em;
                text-transform: uppercase;
            }
            .vulntitan-2fa-step-card h4 {
                margin: 0 0 8px;
                font-size: 15px;
                line-height: 1.4;
                color: #0f172a;
            }
            .vulntitan-2fa-step-card p {
                margin: 0 0 12px;
                color: #475569;
                line-height: 1.6;
            }
            .vulntitan-2fa-inline-notice {
                margin: 0 0 12px;
            }
            .vulntitan-2fa-toggle {
                display: flex;
                align-items: center;
                gap: 10px;
                font-weight: 600;
                color: #0f172a;
            }
            .vulntitan-2fa-toggle input {
                margin: 0;
            }
            .vulntitan-2fa-advanced {
                border: 1px solid #d7dde5;
                border-radius: 14px;
                background: #ffffff;
                overflow: hidden;
            }
            .vulntitan-2fa-advanced summary {
                cursor: pointer;
                padding: 14px 18px;
                font-weight: 600;
                color: #0f172a;
            }
            .vulntitan-2fa-advanced-body {
                padding: 0 18px 18px;
            }
            .vulntitan-2fa-code-block {
                display: block;
                max-width: 100%;
                white-space: normal;
                word-break: break-all;
            }
            .vulntitan-2fa-meta-list {
                margin: 12px 0 0 18px;
                color: #475569;
                line-height: 1.7;
            }
            .vulntitan-2fa-qr-shell {
                display: inline-flex;
                align-items: center;
                justify-content: center;
                padding: 14px;
                border-radius: 12px;
                border: 1px solid #dbe5ee;
                background: #ffffff;
            }
            .vulntitan-2fa-qr canvas,
            .vulntitan-2fa-qr table {
                display: block;
                max-width: 100%;
                height: auto;
            }
            .vulntitan-2fa-verify-disabled {
                opacity: 0.65;
            }
            @media (max-width: 782px) {
                .vulntitan-2fa-status {
                    grid-template-columns: 1fr;
                }
                .vulntitan-2fa-steps {
                    grid-template-columns: 1fr;
                }
            }
        CSS;
    }

    protected static function redirectWithProfileSetupState(string $state): void
    {
        $allowed = ['missing_code', 'invalid_code'];
        $state = in_array($state, $allowed, true) ? $state : 'invalid_code';

        $redirectUrl = add_query_arg(
            [
                self::PROFILE_SETUP_QUERY_ARG => $state,
            ],
            self_admin_url('profile.php')
        );

        $redirectUrl .= '#vulntitan-2fa-step-verify';

        wp_safe_redirect($redirectUrl);
        exit;
    }

    protected static function getProfileSetupNotice(): array
    {
        $state = isset($_GET[self::PROFILE_SETUP_QUERY_ARG]) ? sanitize_key((string) wp_unslash($_GET[self::PROFILE_SETUP_QUERY_ARG])) : '';

        $message = '';

        switch ($state) {
            case 'missing_code':
                $message = __('Enter the current 6-digit code from your authenticator app to finish Step 3.', 'vulntitan');
                break;
            case 'invalid_code':
                $message = __('The authenticator code did not match. Scan the QR code again if needed, then enter the current 6-digit code here.', 'vulntitan');
                break;
            default:
                $message = '';
                break;
        }

        return [
            'message' => $message,
        ];
    }
}
