<?php
if (!defined('ABSPATH'))
    exit;

class Foxblock_TwoFactorAuth
{
    /** Holds the pending token when step-1 auth succeeds */
    private $pending_token = null;

    public function __construct()
    {
        // Only run hooks when master switch is on
        if (!get_option('foxblock_2fa_master_enabled', 0)) {
            // Still need profile hooks so admins can set up / tear down
        }

        // ── Login hooks ──────────────────────────────────────────────────────
        add_filter('authenticate', [$this, 'handle_otp_step'], 5, 3);
        add_filter('authenticate', [$this, 'intercept_post_auth'], 30, 3);
        add_action('login_form', [$this, 'render_otp_field']);

        // ── Profile hooks ────────────────────────────────────────────────────
        add_action('show_user_profile', [$this, 'render_profile_section']);
        add_action('edit_user_profile', [$this, 'render_profile_section']);
        add_action('personal_options_update', [$this, 'save_profile_section']);
        add_action('edit_user_profile_update', [$this, 'save_profile_section']);

        // ── AJAX (logged-in) ─────────────────────────────────────────────────
        add_action('wp_ajax_foxblock_2fa_get_secret', [$this, 'ajax_get_secret']);
        add_action('wp_ajax_foxblock_2fa_verify_setup', [$this, 'ajax_verify_setup']);
        add_action('wp_ajax_foxblock_2fa_disable', [$this, 'ajax_disable']);
        add_action('wp_ajax_foxblock_2fa_regen_backup', [$this, 'ajax_regen_backup']);

        // ── Enqueue ──────────────────────────────────────────────────────────
        add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
    }

    // ═══════════════════════════════════════════════════════════════
    //  TOTP CORE
    // ═══════════════════════════════════════════════════════════════

    public function generate_secret(int $length = 16): string
    {
        $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
        $bytes = random_bytes($length);
        $secret = '';
        for ($i = 0; $i < $length; $i++) {
            $secret .= $charset[ord($bytes[$i]) & 31];
        }
        return $secret;
    }

    private function base32_decode(string $secret): string
    {
        $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
        $secret = strtoupper(str_replace(' ', '', $secret));
        $buffer = 0;
        $bitsLeft = 0;
        $result = '';
        for ($i = 0; $i < strlen($secret); $i++) {
            $val = strpos($charset, $secret[$i]);
            if ($val === false)
                continue;
            $buffer = ($buffer << 5) | $val;
            $bitsLeft += 5;
            if ($bitsLeft >= 8) {
                $bitsLeft -= 8;
                $result .= chr(($buffer >> $bitsLeft) & 0xFF);
            }
        }
        return $result;
    }

    public function generate_totp(string $secret, int $time = 0): string
    {
        if ($time === 0)
            $time = time();
        $step = (int) floor($time / 30);
        $key = $this->base32_decode($secret);
        $msg = pack('N*', 0) . pack('N*', $step);
        $hash = hash_hmac('sha1', $msg, $key, true);
        $off = ord($hash[19]) & 0x0F;
        $code = (
            ((ord($hash[$off]) & 0x7F) << 24) |
            ((ord($hash[$off + 1]) & 0xFF) << 16) |
            ((ord($hash[$off + 2]) & 0xFF) << 8) |
            (ord($hash[$off + 3]) & 0xFF)
        ) % 1000000;
        return str_pad((string) $code, 6, '0', STR_PAD_LEFT);
    }

    public function verify_totp(string $secret, string $code): bool
    {
        if (!preg_match('/^\d{6}$/', $code))
            return false;
        $now = time();
        for ($drift = -1; $drift <= 1; $drift++) {
            if (hash_equals($this->generate_totp($secret, $now + $drift * 30), $code)) {
                return true;
            }
        }
        return false;
    }

    // ═══════════════════════════════════════════════════════════════
    //  BACKUP CODES
    // ═══════════════════════════════════════════════════════════════

    public function generate_backup_codes(int $user_id): array
    {
        $plain = [];
        $hashed = [];
        for ($i = 0; $i < 5; $i++) {
            $code = strtoupper(substr(bin2hex(random_bytes(5)), 0, 8));
            $plain[] = $code;
            $hashed[] = wp_hash_password($code);
        }
        update_user_meta($user_id, 'foxblock_2fa_backup_codes', $hashed);
        return $plain;
    }

    private function verify_and_consume_backup(int $user_id, string $code): bool
    {
        $codes = get_user_meta($user_id, 'foxblock_2fa_backup_codes', true);
        if (!is_array($codes))
            return false;
        foreach ($codes as $idx => $hash) {
            if (wp_check_password(strtoupper($code), $hash)) {
                unset($codes[$idx]);
                update_user_meta($user_id, 'foxblock_2fa_backup_codes', array_values($codes));
                return true;
            }
        }
        return false;
    }

    // ═══════════════════════════════════════════════════════════════
    //  LOGIN FLOW
    // ═══════════════════════════════════════════════════════════════

    /**
     * Priority 5 – handle OTP step (before WP credential checks).
     */
    public function handle_otp_step($user, $username, $password)
    {
        $token = isset($_POST['foxblock_2fa_token']) ? sanitize_text_field($_POST['foxblock_2fa_token']) : '';
        if (empty($token))
            return $user;

        $user_id = (int) get_transient('foxblock_2fa_' . $token);
        if (!$user_id) {
            return new WP_Error(
                'foxblock_2fa_expired',
                '<strong>Lỗi:</strong> Phiên xác thực đã hết hạn. Hãy đăng nhập lại.'
            );
        }

        $otp = isset($_POST['foxblock_otp']) ? trim(sanitize_text_field($_POST['foxblock_otp'])) : '';

        // Try TOTP (6 digits)
        $secret = get_user_meta($user_id, 'foxblock_2fa_secret', true);
        if (preg_match('/^\d{6}$/', $otp) && $this->verify_totp($secret, $otp)) {
            delete_transient('foxblock_2fa_' . $token);
            return get_user_by('id', $user_id);
        }

        // Try backup code (8 chars)
        if (strlen($otp) === 8 && $this->verify_and_consume_backup($user_id, $otp)) {
            delete_transient('foxblock_2fa_' . $token);
            return get_user_by('id', $user_id);
        }

        return new WP_Error(
            'foxblock_bad_otp',
            '<strong>Lỗi:</strong> Mã xác thực không đúng. Kiểm tra lại ứng dụng hoặc backup code.'
        );
    }

    /**
     * Priority 30 – intercept successful WP login when 2FA is active.
     */
    public function intercept_post_auth($user, $username, $password)
    {
        if (!($user instanceof \WP_User))
            return $user;

        // If this is the second step (OTP submission), don't intercept again!
        if (!empty($_POST['foxblock_2fa_token'])) {
            return $user;
        }

        // Master switch or per-user?
        $master = get_option('foxblock_2fa_master_enabled', 0);
        $enabled = get_user_meta($user->ID, 'foxblock_2fa_enabled', true);
        $verified = get_user_meta($user->ID, 'foxblock_2fa_verified', true);

        if (!$master || !$enabled || !$verified)
            return $user;

        // Admins can exempt themselves if they lose access
        $token = wp_generate_password(40, false, false);
        set_transient('foxblock_2fa_' . $token, $user->ID, 5 * MINUTE_IN_SECONDS);
        $this->pending_token = $token;

        return new WP_Error(
            'foxblock_2fa_required',
            'Tài khoản này đã bật bảo mật 2 lớp. Vui lòng nhập mã từ ứng dụng Authenticator.'
        );
    }

    public function render_otp_field()
    {
        if (!$this->pending_token)
            return;
        $token = esc_attr($this->pending_token);
        ?>
        <input type="hidden" name="foxblock_2fa_token" value="<?php echo $token; ?>">
        <p class="foxblock-otp-wrap" style="margin-bottom: 20px;">
            <label for="foxblock_otp">
                <?php esc_html_e('Mã xác thực (6 chữ số hoặc backup code)', 'foxblock'); ?>
            </label>
            <input id="foxblock_otp" type="text" name="foxblock_otp" class="input" inputmode="numeric"
                autocomplete="one-time-code" maxlength="8" placeholder="000000" autofocus
                style="letter-spacing:4px;font-size:20px;text-align:center;">
        </p>
        <script>
            document.addEventListener('DOMContentLoaded', function () {
                // Hide the wrapper containing Username
                var userLog = document.getElementById('user_login');
                if (userLog) {
                    userLog.removeAttribute('required');
                    var p = userLog.closest('p');
                    if (p) { p.style.display = 'none'; }
                }
                // Hide the wrapper containing Password
                var userPass = document.getElementById('user_pass');
                if (userPass) {
                    userPass.removeAttribute('required');
                    var passWrap = userPass.closest('.user-pass-wrap') || userPass.closest('p');
                    if (passWrap) { passWrap.style.display = 'none'; }
                }
                // Hide "Remember Me"
                var forget = document.querySelector('.forgetmenot');
                if (forget) { forget.style.display = 'none'; }

                // Auto focus
                var otp = document.getElementById('foxblock_otp');
                if (otp) { otp.focus(); }
            });
        </script>
        <?php
    }

    // ═══════════════════════════════════════════════════════════════
    //  USER PROFILE
    // ═══════════════════════════════════════════════════════════════

    public function render_profile_section($user)
    {
        if (!($user instanceof WP_User))
            return;
        if (!get_option('foxblock_2fa_master_enabled', 0))
            return;

        $enabled = (bool) get_user_meta($user->ID, 'foxblock_2fa_enabled', true);
        $verified = (bool) get_user_meta($user->ID, 'foxblock_2fa_verified', true);
        $secret = get_user_meta($user->ID, 'foxblock_2fa_secret', true);
        $backup_cnt = 0;
        $backups = get_user_meta($user->ID, 'foxblock_2fa_backup_codes', true);
        if (is_array($backups))
            $backup_cnt = count($backups);

        $issuer = get_bloginfo('name');
        $label = rawurlencode($issuer . ':' . $user->user_email);
        $otpauth = 'otpauth://totp/' . $label . '?secret=' . ($secret ?: '') . '&issuer=' . rawurlencode($issuer);
        ?>
        <div id="foxblock-2fa-profile"
            style="margin:30px 0 20px;padding:24px;background:#fff;border:1px solid #e1e5ea;border-radius:12px;max-width:700px;">
            <h2 style="margin:0 0 6px;font-size:17px;display:flex;align-items:center;gap:8px;">
                Xác thực 2 lớp (2FA)
            </h2>
            <p style="margin:0 0 20px;color:#646970;font-size:13px;">Bảo vệ tài khoản bằng mã OTP từ Google/Microsoft
                Authenticator.</p>

            <?php if ($verified && $enabled): ?>
                <!-- ──── Active State ──── -->
                <div
                    style="display:flex;align-items:center;gap:10px;padding:12px 16px;background:#f0fdf4;border:1px solid #86efac;border-radius:8px;margin-bottom:16px;">
                    <span style="color:#16a34a;font-size:20px;">✓</span>
                    <div>
                        <strong style="color:#166534;">2FA đang hoạt động</strong>
                        <div style="font-size:12px;color:#166534;">Còn <strong><?php echo $backup_cnt; ?></strong> backup code chưa
                            dùng.</div>
                    </div>
                </div>
                <div style="display:flex;gap:10px;flex-wrap:wrap;">
                    <button type="button" id="foxblock-2fa-regen-backup" class="button button-secondary">Tạo lại backup
                        codes</button>
                    <button type="button" id="foxblock-2fa-disable-btn" class="button"
                        style="color:#dc2626;border-color:#dc2626;">Tắt 2FA</button>
                </div>
                <div id="foxblock-2fa-backup-display"
                    style="display:none;margin-top:16px;padding:14px;background:#fefce8;border:1px solid #fde047;border-radius:8px;">
                    <strong style="font-size:13px;color:#854d0e;">Backup Codes (mỗi code dùng 1 lần — lưu lại ngay!):</strong>
                    <div id="foxblock-2fa-codes-list" style="margin-top:10px;display:flex;flex-wrap:wrap;gap:8px;"></div>
                </div>

            <?php else: ?>
                <!-- ──── Setup State ──── -->
                <button type="button" id="foxblock-2fa-setup-btn" class="button button-primary">
                    <?php echo $enabled ? 'Hoàn thiện thiết lập' : '+ Bật 2FA'; ?>
                </button>

                <div id="foxblock-2fa-setup-panel" style="display:none;margin-top:20px;">
                    <ol style="color:#1d2327;font-size:13px;line-height:2;margin:0 0 16px 20px;">
                        <li>Cài đặt <strong>Google Authenticator</strong> hoặc <strong>Microsoft Authenticator</strong> trên điện
                            thoại</li>
                        <li>Quét mã QR bên dưới hoặc nhập secret key thủ công</li>
                        <li>Nhập mã 6 số xuất hiện trên ứng dụng để xác nhận</li>
                    </ol>

                    <div style="display:flex;align-items:flex-start;gap:24px;flex-wrap:wrap;">
                        <div>
                            <div id="foxblock-qrcode"
                                style="width:160px;height:160px;padding:8px;background:#fff;border:1px solid #038;border-radius:8px;">
                            </div>
                        </div>
                        <div style="flex:1;min-width:220px;">
                            <label style="font-size:12px;font-weight:600;color:#374151;display:block;margin-bottom:4px;">SECRET KEY
                                (nhập thủ công nếu không quét được QR)</label>
                            <code id="foxblock-secret-display"
                                style="display:block;font-size:14px;background:#f3f4f6;padding:8px 12px;border-radius:6px;letter-spacing:2px;margin-bottom:14px;word-break:break-all;"></code>
                            <input type="hidden" id="foxblock-2fa-secret-val">
                            <label style="font-size:12px;font-weight:600;color:#374151;display:block;margin-bottom:4px;">NHẬP MÃ XÁC
                                NHẬN (6 chữ số từ app)</label>
                            <div style="display:flex;gap:8px;">
                                <input type="text" id="foxblock-2fa-verify-code" class="regular-text" inputmode="numeric"
                                    maxlength="6" placeholder="000000"
                                    style="letter-spacing:4px;font-size:18px;width:130px;text-align:center;">
                                <button type="button" id="foxblock-2fa-confirm-btn" class="button button-primary">Xác nhận</button>
                            </div>
                            <div id="foxblock-2fa-verify-msg" style="margin-top:8px;font-size:13px;"></div>
                        </div>
                    </div>

                    <!-- Backup codes after verification -->
                    <div id="foxblock-2fa-backup-first"
                        style="display:none;margin-top:20px;padding:14px;background:#fefce8;border:1px solid #fde047;border-radius:8px;">
                        <strong style="font-size:13px;color:#854d0e;">2FA đã được kích hoạt! Lưu lại các backup code sau (dùng 1
                            lần nếu mất điện thoại):</strong>
                        <div id="foxblock-2fa-backup-codes" style="margin-top:10px;display:flex;flex-wrap:wrap;gap:8px;"></div>
                        <p style="font-size:12px;color:#92400e;margin:10px 0 0;">Sau khi đóng trang này, bạn sẽ không xem lại
                            được các code này.</p>
                    </div>
                </div>
            <?php endif; ?>

            <p style="margin:16px 0 0;font-size:12px;color:#9ca3af;">
                <?php echo wp_nonce_field('foxblock_2fa_nonce', 'foxblock_2fa_nonce', true, false); ?>
                User ID: <?php echo esc_html($user->ID); ?>
            </p>
        </div>
        <script>
            window.foxblock2faConfig = {
                userId: <?php echo (int) $user->ID; ?>,
                ajaxUrl: <?php echo wp_json_encode(admin_url('admin-ajax.php')); ?>,
                nonce: <?php echo wp_json_encode(wp_create_nonce('foxblock_2fa_nonce')); ?>,
                otpauth: <?php echo wp_json_encode($otpauth); ?>,
                hasSecret: <?php echo wp_json_encode(!empty($secret)); ?>,
                issuer: <?php echo wp_json_encode($issuer); ?>,
                email: <?php echo wp_json_encode($user->user_email); ?>,
            };
        </script>
        <?php
    }

    public function save_profile_section($user_id)
    {
        // Nothing to save here; all profile changes go via AJAX
    }

    // ═══════════════════════════════════════════════════════════════
    //  AJAX HANDLERS
    // ═══════════════════════════════════════════════════════════════

    public function ajax_get_secret()
    {
        check_ajax_referer('foxblock_2fa_nonce', 'nonce');
        $user_id = get_current_user_id();
        if (!$user_id)
            wp_send_json_error('Unauthorized');

        // Reuse existing secret or create new one
        $secret = get_user_meta($user_id, 'foxblock_2fa_secret', true);
        if (empty($secret)) {
            $secret = $this->generate_secret();
            update_user_meta($user_id, 'foxblock_2fa_secret', $secret);
        }

        $issuer = rawurlencode(get_bloginfo('name'));
        $user = get_user_by('id', $user_id);
        $label = rawurlencode(get_bloginfo('name') . ':' . $user->user_email);
        $otpauth = 'otpauth://totp/' . $label . '?secret=' . $secret . '&issuer=' . $issuer;

        wp_send_json_success(['secret' => $secret, 'otpauth' => $otpauth]);
    }

    public function ajax_verify_setup()
    {
        check_ajax_referer('foxblock_2fa_nonce', 'nonce');
        $user_id = get_current_user_id();
        if (!$user_id)
            wp_send_json_error('Unauthorized');

        $code = isset($_POST['code']) ? sanitize_text_field($_POST['code']) : '';
        $secret = get_user_meta($user_id, 'foxblock_2fa_secret', true);

        if (empty($secret))
            wp_send_json_error('Chưa có secret key.');

        if (!$this->verify_totp($secret, $code)) {
            wp_send_json_error('Mã không đúng. Kiểm tra đồng hồ điện thoại và thử lại.');
        }

        update_user_meta($user_id, 'foxblock_2fa_enabled', 1);
        update_user_meta($user_id, 'foxblock_2fa_verified', 1);
        $plain_codes = $this->generate_backup_codes($user_id);

        wp_send_json_success(['backup_codes' => $plain_codes]);
    }

    public function ajax_disable()
    {
        check_ajax_referer('foxblock_2fa_nonce', 'nonce');
        $user_id = get_current_user_id();
        if (!$user_id)
            wp_send_json_error('Unauthorized');

        delete_user_meta($user_id, 'foxblock_2fa_enabled');
        delete_user_meta($user_id, 'foxblock_2fa_verified');
        delete_user_meta($user_id, 'foxblock_2fa_secret');
        delete_user_meta($user_id, 'foxblock_2fa_backup_codes');

        wp_send_json_success('2FA đã được tắt thành công.');
    }

    public function ajax_regen_backup()
    {
        check_ajax_referer('foxblock_2fa_nonce', 'nonce');
        $user_id = get_current_user_id();
        if (!$user_id)
            wp_send_json_error('Unauthorized');

        $plain_codes = $this->generate_backup_codes($user_id);
        wp_send_json_success(['backup_codes' => $plain_codes]);
    }

    // ═══════════════════════════════════════════════════════════════
    //  ENQUEUE
    // ═══════════════════════════════════════════════════════════════

    public function enqueue_assets(string $hook)
    {
        // Load only on profile pages
        if (!in_array($hook, ['profile.php', 'user-edit.php'], true))
            return;
        if (!get_option('foxblock_2fa_master_enabled', 0))
            return;

        $base = FOXBLOCK_PLUGIN_URL . 'assets/js/';
        wp_enqueue_script('foxblock-qrcode', $base . 'qrcode.min.js', [], FOXBLOCK_VERSION, true);
        wp_enqueue_script('foxblock-2fa', $base . 'foxblock-2fa.js', ['foxblock-qrcode', 'jquery'], FOXBLOCK_VERSION, true);
    }
}
