<?php
/**
 * Name: Google reCAPTCHA
 * Description: Protect your forms from spam and bots with Google reCAPTCHA v2 or v3. Works with WordPress login, registration, comments, WooCommerce, Contact Form 7, Fluent Forms, and more.
 * Category: Security
 * Icon: -
 * Version: 1.0
 * Database: no
 * UseCases: [
 *   {
 *     "title": "Login Protection",
 *     "description": "Add reCAPTCHA to WordPress login form to prevent brute force attacks."
 *   },
 *   {
 *     "title": "Registration Protection",
 *     "description": "Prevent spam user registrations with reCAPTCHA verification."
 *   },
 *   {
 *     "title": "Comment Spam Prevention",
 *     "description": "Stop spam bots from flooding your comments with reCAPTCHA."
 *   },
 *   {
 *     "title": "Simple Forms Integration",
 *     "description": "Protect Switchboard Simple Forms with reCAPTCHA validation."
 *   }
 * ]
 * Added: 2025-01-07
 * Tags: recaptcha, captcha, spam, security, bot protection, google, login, registration, comments
 * WhereToFind: {
 *   "location": "Module Settings",
 *   "instructions": "Configure your Google reCAPTCHA API keys and select which forms to protect."
 * }
 */

if (!defined('ABSPATH')) exit;

class SwitchboardGoogleReCaptcha {

    private $option_key = 'switchboard_google-recaptcha_settings';
    private $default_settings;

    public function __construct() {
        $this->default_settings = [
            'site_key' => '',
            'secret_key' => '',
            'version' => 'v2_checkbox', // v2_checkbox, v2_invisible, v3
            'v3_score_threshold' => 0.5,
            'theme' => 'light', // light, dark
            'badge_position' => 'bottomright', // bottomright, bottomleft, inline (v3 only)
            'language' => 'auto', // auto = use WordPress locale
            'protect_login' => false,
            'protect_registration' => false,
            'protect_password_reset' => false,
            'protect_comments' => false,
            'protect_magic_login' => false,
            'protect_simple_forms' => true,
            'protect_cf7' => false,
            'protect_fluent_forms' => false,
            'whitelist_logged_in' => true,
            'error_message' => 'reCAPTCHA verification failed. Please try again.',
        ];

        // Admin settings
        add_action('switchboard_module_settings_google-recaptcha', [$this, 'render_settings']);
        add_action('wp_ajax_test_google_recaptcha_keys', [$this, 'test_api_keys']);

        $settings = $this->get_settings();

        // Only add protection if keys are configured
        if (empty($settings['site_key']) || empty($settings['secret_key'])) {
            return;
        }

        // Login page scripts - load if ANY login page protection is enabled
        if ($settings['protect_login'] || $settings['protect_registration'] || $settings['protect_password_reset']) {
            add_action('login_footer', [$this, 'enqueue_scripts_login']);
        }

        // Login form protection
        if ($settings['protect_login']) {
            add_action('login_form', [$this, 'render_captcha']);
            add_filter('authenticate', [$this, 'verify_login'], 999, 3);
        }

        // Registration form protection
        if ($settings['protect_registration']) {
            add_action('register_form', [$this, 'render_captcha']);
            add_filter('registration_errors', [$this, 'verify_registration'], 10, 3);
        }

        // Password reset protection
        if ($settings['protect_password_reset']) {
            add_action('lostpassword_form', [$this, 'render_captcha']);
            add_action('resetpass_form', [$this, 'render_captcha']); // Also protect the actual reset form
            add_action('lostpassword_post', [$this, 'verify_password_reset'], 10, 2);
            add_action('validate_password_reset', [$this, 'verify_password_reset_validation'], 10, 2);
        }

        // Comment form protection
        if ($settings['protect_comments']) {
            add_action('comment_form_after_fields', [$this, 'render_captcha']);
            add_action('comment_form_logged_in_after', [$this, 'render_captcha']);
            add_filter('preprocess_comment', [$this, 'verify_comment']);
        }

        // Simple Forms integration
        if ($settings['protect_simple_forms']) {
            add_filter('switchboard_simple_form_validate', [$this, 'verify_simple_form'], 10, 2);
            add_action('switchboard_simple_form_after_fields', [$this, 'render_captcha_for_form']);
        }

        // Magic Login integration hook
        if ($settings['protect_magic_login']) {
            add_filter('switchboard_magic_login_validate', [$this, 'verify_magic_login'], 10, 2);
            add_action('switchboard_magic_login_form_fields', [$this, 'render_captcha_for_form']);
        }

        // Contact Form 7 integration
        if (class_exists('WPCF7')) {
            // Always add tag generator in admin so users can add the tag before enabling protection
            if (is_admin()) {
                add_action('wpcf7_admin_init', [$this, 'cf7_add_tag_generator'], 60);
            }
            // Only add protection/validation hooks if enabled
            if ($settings['protect_cf7']) {
                $this->init_cf7_integration();
            }
        }

        // Fluent Forms integration
        if ($settings['protect_fluent_forms'] && defined('FLUENTFORM')) {
            $this->init_fluent_forms_integration();
        }

        // Frontend scripts (non-login pages)
        add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts_frontend']);

        // Provide filter for other modules
        add_filter('switchboard_recaptcha_is_valid', [$this, 'check_recaptcha_response'], 10, 2);
    }

    /**
     * Get settings
     */
    public function get_settings() {
        $saved = get_option($this->option_key, []);
        return wp_parse_args($saved, $this->default_settings);
    }

    /**
     * Get reCAPTCHA language code
     * Returns language code based on settings or WordPress locale
     */
    private function get_recaptcha_language() {
        $settings = $this->get_settings();

        if ($settings['language'] !== 'auto' && !empty($settings['language'])) {
            return $settings['language'];
        }

        // Map WordPress locale to reCAPTCHA language code
        $locale = get_locale();
        $lang = substr($locale, 0, 2); // Get first 2 chars (e.g., 'en' from 'en_US')

        // Some special cases where reCAPTCHA uses different codes
        $locale_map = [
            'zh_CN' => 'zh-CN',
            'zh_TW' => 'zh-TW',
            'pt_BR' => 'pt-BR',
            'pt_PT' => 'pt-PT',
        ];

        if (isset($locale_map[$locale])) {
            return $locale_map[$locale];
        }

        return $lang;
    }

    /**
     * Enqueue reCAPTCHA scripts on login page
     */
    public function enqueue_scripts_login() {
        $settings = $this->get_settings();
        $this->output_script_tag($settings);
    }

    /**
     * Enqueue scripts on frontend
     */
    public function enqueue_scripts_frontend() {
        $settings = $this->get_settings();

        // Only enqueue if needed on this page
        // Simple Forms protection is always enabled when module is active
        $needs_script = $settings['protect_comments']
            || $settings['protect_magic_login']
            || $settings['protect_cf7']
            || $settings['protect_fluent_forms']
            || class_exists('SwitchboardSimpleForms');

        if (!$needs_script) {
            return;
        }

        // Add submit button protection script for invisible modes
        if (in_array($settings['version'], ['v2_invisible', 'v3'])) {
            wp_add_inline_script('google-recaptcha', '
                // Handle sb-recaptcha-protect class for invisible mode
                // Function to disable protected buttons
                function switchboardDisableProtectedButtons() {
                    document.querySelectorAll(".sb-recaptcha-protect:not([data-sb-recaptcha-processed])").forEach(function(btn) {
                        btn.setAttribute("data-sb-recaptcha-processed", "1");
                        btn.disabled = true;
                        btn.style.opacity = "0.6";
                        btn.style.cursor = "not-allowed";
                    });
                }
                // Disable immediately for any buttons already in DOM
                switchboardDisableProtectedButtons();
                // Also disable on DOMContentLoaded for buttons added before this script runs
                document.addEventListener("DOMContentLoaded", switchboardDisableProtectedButtons);
                // Watch for dynamically added buttons (AJAX forms)
                if (typeof MutationObserver !== "undefined") {
                    new MutationObserver(function(mutations) {
                        mutations.forEach(function(mutation) {
                            if (mutation.addedNodes.length) {
                                switchboardDisableProtectedButtons();
                            }
                        });
                    }).observe(document.body || document.documentElement, { childList: true, subtree: true });
                }

                // Global callback for reCAPTCHA ready - enables protected buttons
                window.switchboardRecaptchaReady = function() {
                    document.querySelectorAll(".sb-recaptcha-protect").forEach(function(btn) {
                        btn.disabled = false;
                        btn.style.opacity = "";
                        btn.style.cursor = "";
                    });
                };
            ', 'before');
        }

        // Add v2 invisible callback script before API loads
        if ($settings['version'] === 'v2_invisible') {
            wp_add_inline_script('google-recaptcha', $this->get_v2_invisible_inline_script($settings), 'before');
        }

        $api_url = $this->get_recaptcha_api_url($settings);

        wp_enqueue_script('google-recaptcha', $api_url, [], null, true);

        if ($settings['version'] === 'v3') {
            wp_add_inline_script('google-recaptcha', $this->get_v3_inline_script($settings));
        }
    }

    /**
     * Build the reCAPTCHA API URL with appropriate parameters
     */
    private function get_recaptcha_api_url($settings) {
        $api_url = 'https://www.google.com/recaptcha/api.js';
        $params = [];

        // Add language parameter
        $lang = $this->get_recaptcha_language();
        if (!empty($lang)) {
            $params['hl'] = $lang;
        }

        // Add version-specific parameters
        if ($settings['version'] === 'v3') {
            $params['render'] = $settings['site_key'];
        } else if ($settings['version'] === 'v2_invisible') {
            $params['onload'] = 'switchboardRecaptchaCallback';
            $params['render'] = 'explicit';
        }

        if (!empty($params)) {
            $api_url .= '?' . http_build_query($params);
        }

        return $api_url;
    }

    /**
     * Output script tag directly (for login page)
     */
    private function output_script_tag($settings) {
        $api_url = $this->get_recaptcha_api_url($settings);
        ?>
        <?php if ($settings['version'] === 'v2_invisible'): ?>
        <script>
        <?php echo $this->get_v2_invisible_inline_script($settings); ?>
        </script>
        <?php endif; ?>
        <script src="<?php echo esc_url($api_url); ?>" async defer></script>
        <?php if ($settings['version'] === 'v3'): ?>
        <script>
        <?php echo $this->get_v3_inline_script($settings); ?>
        </script>
        <?php endif;
    }

    /**
     * Get v3 inline script
     * Note: v3 tokens expire after 2 minutes, so we always get a fresh token on submit
     */
    private function get_v3_inline_script($settings) {
        return "
        function switchboardRecaptchaV3Execute(formId) {
            grecaptcha.ready(function() {
                grecaptcha.execute('" . esc_js($settings['site_key']) . "', {action: 'submit'}).then(function(token) {
                    var input = document.querySelector(formId + ' input[name=\"g-recaptcha-response\"]');
                    if (input) {
                        input.value = token;
                    }
                });
            });
        }
        var switchboardV3PendingSubmitters = {};
        document.addEventListener('DOMContentLoaded', function() {
            var forms = document.querySelectorAll('form');
            forms.forEach(function(form, index) {
                if (form.querySelector('input[name=\"g-recaptcha-response\"]')) {
                    var formKey = form.id || 'form_' + index;
                    form.addEventListener('submit', function(e) {
                        var tokenInput = form.querySelector('input[name=\"g-recaptcha-response\"]');
                        // Always get a fresh token on submit (v3 tokens expire after 2 min)
                        // Check if we're already fetching to prevent loops
                        if (form.dataset.recaptchaFetching === 'true') {
                            return; // Let the form submit naturally
                        }
                        e.preventDefault();
                        form.dataset.recaptchaFetching = 'true';
                        // Store the submit button that was clicked
                        switchboardV3PendingSubmitters[formKey] = e.submitter || null;
                        grecaptcha.ready(function() {
                            grecaptcha.execute('" . esc_js($settings['site_key']) . "', {action: 'submit'}).then(function(token) {
                                tokenInput.value = token;
                                // Reset flag before submitting so subsequent attempts work
                                form.dataset.recaptchaFetching = 'false';
                                // Use requestSubmit if available (triggers submit event), otherwise use submit
                                if (typeof form.requestSubmit === 'function') {
                                    form.requestSubmit(switchboardV3PendingSubmitters[formKey]);
                                } else {
                                    form.submit();
                                }
                            }).catch(function(err) {
                                form.dataset.recaptchaFetching = 'false';
                                console.error('reCAPTCHA error:', err);
                            });
                        });
                    });
                }
            });
            // Enable any protected submit buttons once grecaptcha is ready
            grecaptcha.ready(function() {
                if (typeof window.switchboardRecaptchaReady === 'function') {
                    window.switchboardRecaptchaReady();
                }
            });
        });";
    }

    /**
     * Get v2 invisible inline script
     * Defines the callback function and handles form submission
     */
    private function get_v2_invisible_inline_script($settings) {
        return "
        var switchboardRecaptchaWidgets = {};
        var switchboardRecaptchaPendingForms = {};
        function switchboardRecaptchaCallback() {
            var recaptchas = document.querySelectorAll('.g-recaptcha');
            recaptchas.forEach(function(el, index) {
                var form = el.closest('form');
                if (form && !el.dataset.rendered) {
                    el.dataset.rendered = 'true';
                    var formKey = form.id || 'form_' + index;
                    var widgetId = grecaptcha.render(el, {
                        'sitekey': '" . esc_js($settings['site_key']) . "',
                        'size': 'invisible',
                        'badge': '" . esc_js($settings['badge_position']) . "',
                        'callback': function(token) {
                            var input = form.querySelector('input[name=\"g-recaptcha-response\"]');
                            if (input) {
                                input.value = token;
                            }
                            // Mark that we have the token and resubmit
                            form.dataset.recaptchaVerified = 'true';
                            // Use requestSubmit to trigger submit event handlers, or fall back to submit()
                            if (typeof form.requestSubmit === 'function') {
                                form.requestSubmit(switchboardRecaptchaPendingForms[formKey] || null);
                            } else {
                                form.submit();
                            }
                        },
                        'expired-callback': function() {
                            // Reset the token if it expires
                            var input = form.querySelector('input[name=\"g-recaptcha-response\"]');
                            if (input) {
                                input.value = '';
                            }
                            form.dataset.recaptchaVerified = 'false';
                        },
                        'error-callback': function() {
                            // On error, allow form to submit anyway (fail open for UX)
                            console.warn('reCAPTCHA error occurred, allowing form submission');
                            form.dataset.recaptchaVerified = 'true';
                        }
                    });
                    switchboardRecaptchaWidgets[formKey] = widgetId;
                    form.addEventListener('submit', function(e) {
                        // If already verified, let it through
                        if (form.dataset.recaptchaVerified === 'true') {
                            form.dataset.recaptchaVerified = 'false'; // Reset for next submission
                            return;
                        }
                        var token = form.querySelector('input[name=\"g-recaptcha-response\"]');
                        if (!token || !token.value) {
                            e.preventDefault();
                            e.stopPropagation();
                            // Store the submit button if one was clicked
                            switchboardRecaptchaPendingForms[formKey] = e.submitter || null;
                            grecaptcha.execute(widgetId);
                        }
                    });
                }
            });
            // Enable any protected submit buttons
            if (typeof window.switchboardRecaptchaReady === 'function') {
                window.switchboardRecaptchaReady();
            }
        }";
    }

    /**
     * Render reCAPTCHA widget
     */
    public function render_captcha() {
        // Check if another CAPTCHA module already rendered
        global $switchboard_captcha_rendered;
        if (!empty($switchboard_captcha_rendered)) {
            return;
        }
        $switchboard_captcha_rendered = 'recaptcha';

        $settings = $this->get_settings();

        // Skip for logged-in users if whitelist enabled
        if ($settings['whitelist_logged_in'] && is_user_logged_in()) {
            return;
        }

        if ($settings['version'] === 'v2_checkbox') {
            ?>
            <div class="g-recaptcha"
                 data-sitekey="<?php echo esc_attr($settings['site_key']); ?>"
                 data-theme="<?php echo esc_attr($settings['theme']); ?>"
                 data-size="normal"
                 style="margin: 10px 0;"></div>
            <?php
        } else if ($settings['version'] === 'v2_invisible') {
            ?>
            <div class="g-recaptcha"
                 data-sitekey="<?php echo esc_attr($settings['site_key']); ?>"
                 data-size="invisible"
                 data-badge="<?php echo esc_attr($settings['badge_position']); ?>"></div>
            <input type="hidden" name="g-recaptcha-response" value="">
            <?php if ($settings['badge_position'] === 'inline'): ?>
            <style>.grecaptcha-badge { visibility: hidden !important; }</style>
            <p class="recaptcha-branding" style="font-size: 12px; color: #666; margin: 10px 0;">
                This site is protected by reCAPTCHA and the Google
                <a href="https://policies.google.com/privacy" target="_blank">Privacy Policy</a> and
                <a href="https://policies.google.com/terms" target="_blank">Terms of Service</a> apply.
            </p>
            <?php endif;
            ?><?php
        } else if ($settings['version'] === 'v3') {
            ?>
            <input type="hidden" name="g-recaptcha-response" value="">
            <?php if ($settings['badge_position'] === 'inline'): ?>
            <style>.grecaptcha-badge { visibility: hidden !important; }</style>
            <p class="recaptcha-branding" style="font-size: 12px; color: #666; margin: 10px 0;">
                This site is protected by reCAPTCHA and the Google
                <a href="https://policies.google.com/privacy" target="_blank">Privacy Policy</a> and
                <a href="https://policies.google.com/terms" target="_blank">Terms of Service</a> apply.
            </p>
            <?php endif;
        }
    }

    /**
     * Render captcha for form (with form context)
     * This bypasses duplicate prevention since Magic Login etc. are separate forms
     */
    public function render_captcha_for_form($form_id = null) {
        // Check if another CAPTCHA module already rendered for THIS form
        global $switchboard_captcha_rendered_form;
        $form_key = $form_id ?: 'default';
        if (!empty($switchboard_captcha_rendered_form[$form_key])) {
            return;
        }
        $switchboard_captcha_rendered_form[$form_key] = 'recaptcha';

        $settings = $this->get_settings();

        // Skip for logged-in users if whitelist enabled
        if ($settings['whitelist_logged_in'] && is_user_logged_in()) {
            return;
        }

        if ($settings['version'] === 'v2_checkbox') {
            ?>
            <div class="g-recaptcha"
                 data-sitekey="<?php echo esc_attr($settings['site_key']); ?>"
                 data-theme="<?php echo esc_attr($settings['theme']); ?>"
                 data-size="normal"
                 style="margin: 10px 0;"></div>
            <?php
        } else if ($settings['version'] === 'v2_invisible') {
            ?>
            <div class="g-recaptcha"
                 data-sitekey="<?php echo esc_attr($settings['site_key']); ?>"
                 data-size="invisible"
                 data-badge="<?php echo esc_attr($settings['badge_position']); ?>"></div>
            <input type="hidden" name="g-recaptcha-response" value="">
            <?php
        } else if ($settings['version'] === 'v3') {
            ?>
            <input type="hidden" name="g-recaptcha-response" value="">
            <?php
        }
    }

    /**
     * Verify reCAPTCHA response
     */
    public function verify_response($response = null) {
        $settings = $this->get_settings();

        // Skip for logged-in users if enabled
        if ($settings['whitelist_logged_in'] && is_user_logged_in()) {
            return true;
        }

        if ($response === null) {
            $response = isset($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : '';
        }

        if (empty($response)) {
            return false;
        }

        $verify_url = 'https://www.google.com/recaptcha/api/siteverify';
        $verify_response = wp_remote_post($verify_url, [
            'body' => [
                'secret' => $settings['secret_key'],
                'response' => $response,
                'remoteip' => $this->get_client_ip(),
            ],
            'timeout' => 30,
        ]);

        if (is_wp_error($verify_response)) {
            return false;
        }

        $result = json_decode(wp_remote_retrieve_body($verify_response), true);

        if (!isset($result['success']) || !$result['success']) {
            return false;
        }

        // For v3, check score threshold
        if ($settings['version'] === 'v3') {
            $score = isset($result['score']) ? floatval($result['score']) : 0;
            if ($score < $settings['v3_score_threshold']) {
                return false;
            }
        }

        return true;
    }

    /**
     * Get client IP
     */
    private function get_client_ip() {
        $ip_headers = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'];
        foreach ($ip_headers as $header) {
            if (!empty($_SERVER[$header])) {
                $ip = sanitize_text_field($_SERVER[$header]);
                // Handle comma-separated list (X-Forwarded-For)
                if (strpos($ip, ',') !== false) {
                    $ips = explode(',', $ip);
                    $ip = trim($ips[0]);
                }
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }
        return '127.0.0.1';
    }

    /**
     * Check if current request is a REST API request
     */
    private function is_rest_request() {
        if (defined('REST_REQUEST') && REST_REQUEST) {
            return true;
        }

        // Check for rest_route in query string
        if (isset($_GET['rest_route']) && strpos(sanitize_text_field(wp_unslash($_GET['rest_route'])), '/') === 0) {
            return true;
        }

        // Check URL path for REST API
        $rest_url = wp_parse_url(trailingslashit(rest_url()));
        $current_url = wp_parse_url(add_query_arg([]));

        if (isset($current_url['path']) && isset($rest_url['path'])) {
            return strpos($current_url['path'], $rest_url['path']) === 0;
        }

        return false;
    }

    /**
     * Check if current request is an XML-RPC request
     */
    private function is_xmlrpc_request() {
        return defined('XMLRPC_REQUEST') && XMLRPC_REQUEST;
    }

    /**
     * Check if another CAPTCHA module is handling this form
     * Skip validation if another CAPTCHA's response is present
     */
    private function another_captcha_is_active() {
        // Check if Turnstile response is present (Cloudflare Turnstile is active)
        if (!empty($_POST['cf-turnstile-response'])) {
            return true;
        }
        // Check if Simple CAPTCHA response is present
        if (!empty($_POST['switchboard_captcha_token'])) {
            return true;
        }
        return false;
    }

    /**
     * Verify login
     */
    public function verify_login($user, $username, $password) {
        // Skip for REST API and XML-RPC requests (authentication handled differently)
        if ($this->is_rest_request() || $this->is_xmlrpc_request()) {
            return $user;
        }

        // If another plugin already failed authentication, don't pile on with reCAPTCHA error
        if (is_wp_error($user)) {
            return $user;
        }

        if (empty($username) || empty($password)) {
            return $user;
        }

        // Skip if another CAPTCHA module is handling this form
        if ($this->another_captcha_is_active()) {
            return $user;
        }

        if (!$this->verify_response()) {
            return new WP_Error('recaptcha_failed', $this->get_settings()['error_message']);
        }

        return $user;
    }

    /**
     * Verify registration
     */
    public function verify_registration($errors, $sanitized_user_login, $user_email) {
        // Skip if another CAPTCHA module is handling this form
        if ($this->another_captcha_is_active()) {
            return $errors;
        }
        if (!$this->verify_response()) {
            $errors->add('recaptcha_failed', $this->get_settings()['error_message']);
        }
        return $errors;
    }

    /**
     * Verify password reset (lostpassword_post)
     */
    public function verify_password_reset($errors, $user_data) {
        // Skip if another CAPTCHA module is handling this form
        if ($this->another_captcha_is_active()) {
            return;
        }
        if (!$this->verify_response()) {
            $errors->add('recaptcha_failed', $this->get_settings()['error_message']);
        }
    }

    /**
     * Verify password reset validation (validate_password_reset - actual reset form)
     */
    public function verify_password_reset_validation($errors, $user) {
        // Only verify if pass1 is set (actual password reset, not initial load)
        if (!isset($_POST['pass1'])) {
            return;
        }
        // Skip if another CAPTCHA module is handling this form
        if ($this->another_captcha_is_active()) {
            return;
        }
        if (!$this->verify_response()) {
            $errors->add('recaptcha_failed', $this->get_settings()['error_message']);
        }
    }

    /**
     * Verify comment
     */
    public function verify_comment($commentdata) {
        // Skip if another CAPTCHA module is handling this form
        if ($this->another_captcha_is_active()) {
            return $commentdata;
        }
        if (!$this->verify_response()) {
            wp_die($this->get_settings()['error_message'], 'reCAPTCHA Failed', ['back_link' => true]);
        }
        return $commentdata;
    }

    /**
     * Verify Simple Form submission
     * Only validates if this module rendered the CAPTCHA
     */
    public function verify_simple_form($valid, $form_id) {
        global $switchboard_captcha_rendered;
        // Only validate if reCAPTCHA was the one that rendered, or if no flag (backwards compat)
        if (!empty($switchboard_captcha_rendered) && $switchboard_captcha_rendered !== 'recaptcha') {
            return $valid;
        }
        if (!$this->verify_response()) {
            return new WP_Error('recaptcha_failed', $this->get_settings()['error_message']);
        }
        return $valid;
    }

    /**
     * Verify Magic Login submission
     * Only validates if this module rendered the CAPTCHA
     */
    public function verify_magic_login($valid, $email) {
        global $switchboard_captcha_rendered;
        // Only validate if reCAPTCHA was the one that rendered, or if no flag (backwards compat)
        if (!empty($switchboard_captcha_rendered) && $switchboard_captcha_rendered !== 'recaptcha') {
            return $valid;
        }
        // Skip if another CAPTCHA module is handling this form
        if ($this->another_captcha_is_active()) {
            return $valid;
        }
        if (!$this->verify_response()) {
            return new WP_Error('recaptcha_failed', $this->get_settings()['error_message']);
        }
        return $valid;
    }

    /**
     * Filter for other modules to check reCAPTCHA
     */
    public function check_recaptcha_response($valid, $response = null) {
        return $this->verify_response($response);
    }

    // =========================================================================
    // Contact Form 7 Integration
    // =========================================================================

    /**
     * Initialize Contact Form 7 integration
     */
    private function init_cf7_integration() {
        // CF7's wpcf7_init fires on 'init' priority 10
        // Switchboard also loads modules on 'init' priority 10
        // If wpcf7_add_form_tag exists, wpcf7_init has already fired - register immediately
        // Otherwise, hook into wpcf7_init for later registration
        if (function_exists('wpcf7_add_form_tag')) {
            $this->cf7_add_form_tag();
        }
        // Also add the hook in case wpcf7_init fires later (e.g., different load order)
        add_action('wpcf7_init', [$this, 'cf7_add_form_tag'], 1);

        // Validate submission - use wpcf7_validate filter with our tag
        add_filter('wpcf7_validate_switchboard_recaptcha', [$this, 'cf7_validate_tag'], 10, 2);
        add_filter('wpcf7_validate_switchboard_recaptcha*', [$this, 'cf7_validate_tag'], 10, 2);

        // Enqueue scripts on CF7 pages
        add_action('wpcf7_enqueue_scripts', [$this, 'enqueue_scripts_frontend']);

        // Tag generator button is added in constructor (unconditionally in admin)
    }

    /**
     * Register CF7 form tag
     * Users add [switchboard_recaptcha] to their form
     */
    public function cf7_add_form_tag() {
        static $registered = false;

        if ($registered || !function_exists('wpcf7_add_form_tag')) {
            return;
        }
        $registered = true;

        wpcf7_add_form_tag(
            ['switchboard_recaptcha', 'switchboard_recaptcha*'],
            [$this, 'cf7_render_tag'],
            ['name-attr' => true]
        );
    }

    /**
     * Render CF7 form tag
     */
    public function cf7_render_tag($tag) {
        $settings = $this->get_settings();

        if ($settings['whitelist_logged_in'] && is_user_logged_in()) {
            return '';
        }

        $validation_error = wpcf7_get_validation_error($tag->name);

        ob_start();
        echo '<span class="wpcf7-form-control-wrap" data-name="' . esc_attr($tag->name) . '">';
        $this->render_captcha();
        echo wp_kses_post($validation_error);
        echo '</span>';
        return ob_get_clean();
    }

    /**
     * Validate CF7 form tag
     */
    public function cf7_validate_tag($result, $tag) {
        $settings = $this->get_settings();

        // Skip for logged-in users if whitelist enabled
        if ($settings['whitelist_logged_in'] && is_user_logged_in()) {
            return $result;
        }

        if (!$this->verify_response()) {
            $result->invalidate($tag, $settings['error_message']);
        }

        return $result;
    }

    /**
     * Add tag generator button in CF7 form editor
     */
    public function cf7_add_tag_generator() {
        if (!function_exists('wpcf7_add_tag_generator')) {
            return;
        }

        wpcf7_add_tag_generator(
            'switchboard_recaptcha',
            __('Google reCAPTCHA', 'wpswitchboard'),
            'wpcf7-tg-pane-switchboard-recaptcha',
            [$this, 'cf7_tag_generator_panel']
        );
    }

    /**
     * Render tag generator panel in CF7 form editor
     */
    public function cf7_tag_generator_panel($contact_form, $args = '') {
        $args = wp_parse_args($args, []);
        $type = 'switchboard_recaptcha';
        ?>
        <div class="control-box">
            <fieldset>
                <legend><?php esc_html_e('Generate a Google reCAPTCHA tag', 'wpswitchboard'); ?></legend>
                <table class="form-table">
                    <tbody>
                        <tr>
                            <th scope="row">
                                <label for="<?php echo esc_attr($args['content'] . '-required'); ?>">
                                    <?php esc_html_e('Required field', 'wpswitchboard'); ?>
                                </label>
                            </th>
                            <td>
                                <input type="checkbox" name="required" id="<?php echo esc_attr($args['content'] . '-required'); ?>" checked="checked" />
                            </td>
                        </tr>
                        <tr>
                            <th scope="row">
                                <label for="<?php echo esc_attr($args['content'] . '-name'); ?>">
                                    <?php esc_html_e('Name', 'wpswitchboard'); ?>
                                </label>
                            </th>
                            <td>
                                <input type="text" name="name" class="tg-name oneline" id="<?php echo esc_attr($args['content'] . '-name'); ?>" value="recaptcha" />
                            </td>
                        </tr>
                    </tbody>
                </table>
            </fieldset>
        </div>
        <div class="insert-box">
            <input type="text" name="<?php echo esc_attr($type); ?>" class="tag code" readonly="readonly" onfocus="this.select()" />
            <div class="submitbox">
                <input type="button" class="button button-primary insert-tag" value="<?php esc_attr_e('Insert Tag', 'wpswitchboard'); ?>" />
            </div>
            <br class="clear" />
            <p class="description mail-tag">
                <label>
                    <?php esc_html_e('This field does not need to be included in the mail template.', 'wpswitchboard'); ?>
                </label>
            </p>
        </div>
        <?php
    }

    // =========================================================================
    // Fluent Forms Integration
    // =========================================================================

    /**
     * Initialize Fluent Forms integration
     * Registers a custom field type using BaseFieldManager
     */
    private function init_fluent_forms_integration() {
        // Only initialize if Fluent Forms is active
        if (!class_exists('FluentForm\App\Services\FormBuilder\BaseFieldManager')) {
            return;
        }
        // Initialize the Fluent Forms field manager
        new SwitchboardFluentFormsRecaptcha($this);
    }

    /**
     * Render settings page
     */
    public function render_settings() {
        $settings = $this->get_settings();
        ?>
        <div class="sb-module-settings-form">
            <input type="hidden" name="module" value="google-recaptcha">

            <!-- Info Box -->
            <div class="sb-sp-alert sb-sp-alert-info">
                <div class="sb-sp-alert-title">Google reCAPTCHA</div>
                <div class="sb-sp-alert-text">Protect your forms from spam and abuse using Google's reCAPTCHA service. Get your API keys from <a href="https://www.google.com/recaptcha/admin" target="_blank">Google reCAPTCHA Admin</a>.</div>
            </div>

            <?php
            // Check for other active CAPTCHA modules
            $other_captchas = [];
            if (class_exists('SwitchboardCloudflareTurnstile')) {
                $turnstile_settings = get_option('switchboard_cloudflare-turnstile_settings', []);
                if (!empty($turnstile_settings['site_key']) && !empty($turnstile_settings['secret_key'])) {
                    $other_captchas[] = 'Cloudflare Turnstile';
                }
            }
            if (class_exists('SwitchboardSimpleCaptcha')) {
                $other_captchas[] = 'Simple CAPTCHA';
            }
            if (!empty($other_captchas)): ?>
            <div class="sb-sp-alert sb-sp-alert-warning" style="margin-bottom: 16px;">
                <div class="sb-sp-alert-title"><?php esc_html_e('Other CAPTCHA Module Active', 'wpswitchboard'); ?></div>
                <div class="sb-sp-alert-text">
                    <?php echo esc_html(implode(' and ', $other_captchas)); ?> <?php echo count($other_captchas) > 1 ? esc_html__('are', 'wpswitchboard') : esc_html__('is', 'wpswitchboard'); ?> <?php esc_html_e('also enabled. Only one CAPTCHA will be shown per form - enabling multiple CAPTCHA modules is unnecessary and may cause conflicts. We recommend using only one CAPTCHA solution.', 'wpswitchboard'); ?>
                </div>
            </div>
            <?php endif; ?>

            <!-- API Configuration -->
            <h3 class="sb-sp-heading">API Configuration</h3>

            <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 24px;">
                <div class="sb-sp-field-horizontal-left">
                    <label for="site_key" class="sb-sp-label"><?php esc_html_e('Site Key', 'wpswitchboard'); ?></label>
                    <p class="sb-sp-description"><?php esc_html_e('Public key visible in your site\'s HTML', 'wpswitchboard'); ?></p>
                </div>
                <input type="text" name="site_key" id="site_key" value="<?php echo esc_attr($settings['site_key']); ?>" class="sb-sp-input" placeholder="6Lc...">
            </div>

            <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 24px;">
                <div class="sb-sp-field-horizontal-left">
                    <label for="secret_key" class="sb-sp-label"><?php esc_html_e('Secret Key', 'wpswitchboard'); ?></label>
                    <p class="sb-sp-description"><?php esc_html_e('Private key for server-side verification', 'wpswitchboard'); ?></p>
                </div>
                <input type="password" name="secret_key" id="secret_key" value="<?php echo esc_attr($settings['secret_key']); ?>" class="sb-sp-input" placeholder="6Lc...">
            </div>

            <!-- Test API Keys Button -->
            <div class="sb-sp-group" style="margin-bottom: 24px;">
                <button type="button" id="test_recaptcha_keys" class="sb-sp-button sb-sp-button-secondary" style="padding: 8px 16px; cursor: pointer;">
                    <?php esc_html_e('Test API Keys', 'wpswitchboard'); ?>
                </button>
                <span id="test_recaptcha_result" style="margin-left: 12px; display: none;"></span>
                <input type="hidden" id="test_recaptcha_nonce" value="<?php echo esc_attr(wp_create_nonce('test_google_recaptcha_keys')); ?>">
            </div>

            <div class="sb-sp-divider"></div>

            <!-- Version Selection -->
            <h3 class="sb-sp-heading">reCAPTCHA Version</h3>
            <p class="sb-sp-description" style="margin-bottom: 16px;">Choose the type of reCAPTCHA to use. Each requires different API keys.</p>

            <div class="sb-sp-radio-cards" style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 24px;">
                <label class="sb-sp-radio-card" style="display: block; padding: 16px; background: #fff; border: 2px solid var(--gray-200, #e5e7eb); border-radius: 12px; cursor: pointer; transition: all 0.2s ease; position: relative;">
                    <input type="radio" name="version" value="v2_checkbox" <?php checked($settings['version'], 'v2_checkbox'); ?> style="position: absolute; top: 16px; right: 16px; width: 18px; height: 18px; accent-color: var(--primary-500, #3b82f6);">
                    <div style="padding-right: 30px;">
                        <div style="font-size: 24px; margin-bottom: 8px;"></div>
                        <div style="font-weight: 600; color: var(--gray-900, #111827); margin-bottom: 4px;">v2 Checkbox <span style="color: #16a34a; font-size: 11px; font-weight: 500;">(Recommended)</span></div>
                        <div style="font-size: 13px; color: var(--gray-500, #6b7280);">"I'm not a robot" - most reliable</div>
                    </div>
                </label>

                <label class="sb-sp-radio-card" style="display: block; padding: 16px; background: #fff; border: 2px solid var(--gray-200, #e5e7eb); border-radius: 12px; cursor: pointer; transition: all 0.2s ease; position: relative; opacity: 0.7;">
                    <input type="radio" name="version" value="v2_invisible" <?php checked($settings['version'], 'v2_invisible'); ?> style="position: absolute; top: 16px; right: 16px; width: 18px; height: 18px; accent-color: var(--primary-500, #3b82f6);">
                    <div style="padding-right: 30px;">
                        <div style="font-size: 24px; margin-bottom: 8px;"></div>
                        <div style="font-weight: 600; color: var(--gray-900, #111827); margin-bottom: 4px;">v2 Invisible <span style="color: #dc2626; font-size: 11px; font-weight: 500;">(Not Recommended)</span></div>
                        <div style="font-size: 13px; color: var(--gray-500, #6b7280);">May cause issues in some environments</div>
                    </div>
                </label>

                <label class="sb-sp-radio-card" style="display: block; padding: 16px; background: #fff; border: 2px solid var(--gray-200, #e5e7eb); border-radius: 12px; cursor: pointer; transition: all 0.2s ease; position: relative; opacity: 0.7;">
                    <input type="radio" name="version" value="v3" <?php checked($settings['version'], 'v3'); ?> style="position: absolute; top: 16px; right: 16px; width: 18px; height: 18px; accent-color: var(--primary-500, #3b82f6);">
                    <div style="padding-right: 30px;">
                        <div style="font-size: 24px; margin-bottom: 8px;"></div>
                        <div style="font-weight: 600; color: var(--gray-900, #111827); margin-bottom: 4px;">v3 (Score-based) <span style="color: #dc2626; font-size: 11px; font-weight: 500;">(Not Recommended)</span></div>
                        <div style="font-size: 13px; color: var(--gray-500, #6b7280);">May cause issues in some environments</div>
                    </div>
                </label>
            </div>

            <div id="invisible-mode-notice" class="sb-sp-notice sb-sp-notice-warning" style="margin-top: 16px; margin-bottom: 16px; padding: 12px 16px; background: #fee2e2; border-left: 4px solid #dc2626; border-radius: 4px; display: <?php echo in_array($settings['version'], ['v2_invisible', 'v3']) ? 'block' : 'none'; ?>;">
                <strong style="color: #991b1b;"><?php esc_html_e('Use At Your Own Risk', 'wpswitchboard'); ?></strong>
                <p style="margin: 8px 0 0; font-size: 13px; color: #991b1b;">
                    <?php esc_html_e('Invisible modes may cause verification failures depending on your hosting environment, caching setup, theme, or other plugins. Every environment is different and we cannot guarantee this will work reliably on your site.', 'wpswitchboard'); ?>
                </p>
                <p style="margin: 8px 0 0; font-size: 13px;">
                    <?php esc_html_e('If you must use invisible mode, add the CSS class', 'wpswitchboard'); ?>
                    <code style="background: #fff; padding: 2px 6px; border-radius: 3px;">sb-recaptcha-protect</code>
                    <?php esc_html_e('to your submit buttons to help prevent premature submissions.', 'wpswitchboard'); ?>
                </p>
            </div>

            <script>
            document.querySelectorAll('input[name="version"]').forEach(function(radio) {
                radio.addEventListener('change', function() {
                    var notice = document.getElementById('invisible-mode-notice');
                    notice.style.display = (this.value === 'v2_invisible' || this.value === 'v3') ? 'block' : 'none';
                });
            });
            </script>

            <div id="v3_options" style="margin-top: 16px; <?php echo $settings['version'] !== 'v3' ? 'display:none;' : ''; ?>">
                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 24px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="v3_score_threshold" class="sb-sp-label"><?php esc_html_e('Score Threshold', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Requests below this score (0.0-1.0) will be blocked. Higher = stricter. Default: 0.5', 'wpswitchboard'); ?></p>
                    </div>
                    <input type="number" name="v3_score_threshold" id="v3_score_threshold" value="<?php echo esc_attr($settings['v3_score_threshold']); ?>" min="0" max="1" step="0.1" class="sb-sp-input" style="width: 100px;">
                </div>
            </div>

            <div class="sb-sp-divider"></div>

            <!-- Appearance -->
            <h3 class="sb-sp-heading">Appearance</h3>

            <!-- v2 Checkbox specific options -->
            <div id="v2_checkbox_options" style="<?php echo $settings['version'] !== 'v2_checkbox' ? 'display:none;' : ''; ?>">
                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 24px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="theme" class="sb-sp-label"><?php esc_html_e('Theme', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Color scheme for the reCAPTCHA widget', 'wpswitchboard'); ?></p>
                    </div>
                    <select name="theme" id="theme" class="sb-sp-select">
                        <option value="light" <?php selected($settings['theme'], 'light'); ?>>Light</option>
                        <option value="dark" <?php selected($settings['theme'], 'dark'); ?>>Dark</option>
                    </select>
                </div>
            </div>

            <!-- Badge position for v2 invisible and v3 -->
            <div id="badge_options" style="<?php echo $settings['version'] === 'v2_checkbox' ? 'display:none;' : ''; ?>">
                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 24px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="badge_position" class="sb-sp-label"><?php esc_html_e('Badge Position', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Position for reCAPTCHA badge (or hide with inline text)', 'wpswitchboard'); ?></p>
                    </div>
                    <select name="badge_position" id="badge_position" class="sb-sp-select">
                        <option value="bottomright" <?php selected($settings['badge_position'], 'bottomright'); ?>>Bottom Right</option>
                        <option value="bottomleft" <?php selected($settings['badge_position'], 'bottomleft'); ?>>Bottom Left</option>
                        <option value="inline" <?php selected($settings['badge_position'], 'inline'); ?>>Inline (hide badge, show text)</option>
                    </select>
                </div>
            </div>

            <!-- Language setting -->
            <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 24px;">
                <div class="sb-sp-field-horizontal-left">
                    <label for="language" class="sb-sp-label"><?php esc_html_e('Language', 'wpswitchboard'); ?></label>
                    <p class="sb-sp-description"><?php esc_html_e('Language for reCAPTCHA interface', 'wpswitchboard'); ?></p>
                </div>
                <select name="language" id="language" class="sb-sp-select">
                    <option value="auto" <?php selected($settings['language'], 'auto'); ?>>Auto-detect (WordPress locale)</option>
                    <option value="en" <?php selected($settings['language'], 'en'); ?>>English</option>
                    <option value="es" <?php selected($settings['language'], 'es'); ?>>Spanish</option>
                    <option value="fr" <?php selected($settings['language'], 'fr'); ?>>French</option>
                    <option value="de" <?php selected($settings['language'], 'de'); ?>>German</option>
                    <option value="it" <?php selected($settings['language'], 'it'); ?>>Italian</option>
                    <option value="pt" <?php selected($settings['language'], 'pt'); ?>>Portuguese</option>
                    <option value="nl" <?php selected($settings['language'], 'nl'); ?>>Dutch</option>
                    <option value="ja" <?php selected($settings['language'], 'ja'); ?>>Japanese</option>
                    <option value="ko" <?php selected($settings['language'], 'ko'); ?>>Korean</option>
                    <option value="zh-CN" <?php selected($settings['language'], 'zh-CN'); ?>>Chinese (Simplified)</option>
                    <option value="zh-TW" <?php selected($settings['language'], 'zh-TW'); ?>>Chinese (Traditional)</option>
                    <option value="ar" <?php selected($settings['language'], 'ar'); ?>>Arabic</option>
                    <option value="ru" <?php selected($settings['language'], 'ru'); ?>>Russian</option>
                </select>
            </div>

            <div class="sb-sp-divider"></div>

            <!-- Form Protection -->
            <h3 class="sb-sp-heading">Form Protection</h3>
            <p class="sb-sp-description" style="margin-bottom: 16px;">Select which forms to protect with reCAPTCHA</p>

            <div class="sb-sp-checkbox-grid" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <div class="sb-module-option-info">
                                <strong class="sb-module-option-title">WordPress Login</strong>
                                <p class="sb-module-option-text">Add reCAPTCHA to wp-login.php</p>
                            </div>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="protect_login" value="1" <?php checked($settings['protect_login']); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                    </div>
                </div>

                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <div class="sb-module-option-info">
                                <strong class="sb-module-option-title">Registration Form</strong>
                                <p class="sb-module-option-text">Prevent spam registrations</p>
                            </div>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="protect_registration" value="1" <?php checked($settings['protect_registration']); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                    </div>
                </div>

                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <div class="sb-module-option-info">
                                <strong class="sb-module-option-title">Password Reset</strong>
                                <p class="sb-module-option-text">Protect lost password requests</p>
                            </div>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="protect_password_reset" value="1" <?php checked($settings['protect_password_reset']); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                    </div>
                </div>

                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <div class="sb-module-option-info">
                                <strong class="sb-module-option-title">Comment Form</strong>
                                <p class="sb-module-option-text">Stop spam comments on posts</p>
                            </div>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="protect_comments" value="1" <?php checked($settings['protect_comments']); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                    </div>
                </div>

                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <div class="sb-module-option-info">
                                <strong class="sb-module-option-title">Magic Login</strong>
                                <p class="sb-module-option-text">Protect passwordless login</p>
                            </div>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="protect_magic_login" value="1" <?php checked($settings['protect_magic_login']); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                    </div>
                </div>

                <div class="sb-module-option-item sb-module-toggle-item">
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <div class="sb-module-option-info">
                                <strong class="sb-module-option-title">Simple Forms</strong>
                                <p class="sb-module-option-text">Protect Switchboard forms</p>
                            </div>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="protect_simple_forms" value="1" <?php checked($settings['protect_simple_forms']); ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Third-Party Integrations -->
            <h4 class="sb-sp-heading-sm" style="margin-top: 24px;">Third-Party Integrations</h4>
            <div class="sb-sp-checkbox-grid" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-top: 12px;">
                <?php $cf7_installed = class_exists('WPCF7'); ?>
                <div class="sb-module-option-item sb-module-toggle-item" <?php echo !$cf7_installed ? 'style="opacity: 0.5;"' : ''; ?>>
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <div class="sb-module-option-info">
                                <strong class="sb-module-option-title">Contact Form 7</strong>
                                <p class="sb-module-option-text"><?php echo $cf7_installed ? 'Use shortcode tag' : 'Not installed'; ?></p>
                            </div>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="protect_cf7" value="1" <?php checked($settings['protect_cf7']); ?> <?php echo !$cf7_installed ? 'disabled' : ''; ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                    </div>
                </div>

                <?php $ff_installed = defined('FLUENTFORM'); ?>
                <div class="sb-module-option-item sb-module-toggle-item" <?php echo !$ff_installed ? 'style="opacity: 0.5;"' : ''; ?>>
                    <div class="sb-module-option-content">
                        <div class="sb-module-toggle-header">
                            <div class="sb-module-option-info">
                                <strong class="sb-module-option-title">Fluent Forms</strong>
                                <p class="sb-module-option-text"><?php echo $ff_installed ? 'Adds "Google reCAPTCHA" in Advanced fields' : 'Not installed'; ?></p>
                            </div>
                            <label class="sb-module-toggle">
                                <input type="checkbox" name="protect_fluent_forms" value="1" <?php checked($settings['protect_fluent_forms']); ?> <?php echo !$ff_installed ? 'disabled' : ''; ?>>
                                <span class="sb-module-toggle-slider"></span>
                            </label>
                        </div>
                    </div>
                </div>
            </div>

            <div class="sb-sp-divider"></div>

            <!-- Options -->
            <h3 class="sb-sp-heading">Options</h3>

            <div class="sb-module-option-item sb-module-toggle-item">
                <div class="sb-module-option-content">
                    <div class="sb-module-toggle-header">
                        <strong class="sb-module-option-title"><?php esc_html_e('Skip for Logged-in Users', 'wpswitchboard'); ?></strong>
                        <label class="sb-module-toggle">
                            <input type="checkbox" name="whitelist_logged_in" value="1" <?php checked($settings['whitelist_logged_in']); ?>>
                            <span class="sb-module-toggle-slider"></span>
                        </label>
                    </div>
                    <p class="sb-module-option-text"><?php esc_html_e('Logged-in users will not see reCAPTCHA challenges', 'wpswitchboard'); ?></p>
                </div>
            </div>

            <div class="sb-sp-group sb-sp-field-horizontal" style="margin-top: 24px; margin-bottom: 24px;">
                <div class="sb-sp-field-horizontal-left">
                    <label for="error_message" class="sb-sp-label"><?php esc_html_e('Error Message', 'wpswitchboard'); ?></label>
                    <p class="sb-sp-description"><?php esc_html_e('Message shown when reCAPTCHA verification fails', 'wpswitchboard'); ?></p>
                </div>
                <input type="text" name="error_message" id="error_message" value="<?php echo esc_attr($settings['error_message']); ?>" class="sb-sp-input">
            </div>

            <div class="sb-sp-divider"></div>

            <!-- Tip Box -->
            <div class="sb-sp-card sb-sp-p-4">
                <h4 class="sb-sp-heading-sm sb-sp-mb-3">Tips</h4>
                <ul class="sb-sp-list">
                    <li class="sb-sp-list-item"><strong>v2 Checkbox:</strong> Best for user experience - most users pass without interaction</li>
                    <li class="sb-sp-list-item"><strong>v2 Invisible:</strong> Good balance of security and UX - no visible checkbox</li>
                    <li class="sb-sp-list-item"><strong>v3:</strong> Most seamless but may require tuning the score threshold</li>
                    <li class="sb-sp-list-item"><strong>Different keys:</strong> v2 and v3 require separate API keys from Google</li>
                </ul>
            </div>
        </div>

        <script>
        // Toggle version-specific options
        function updateVersionOptions(version) {
            document.getElementById('v3_options').style.display = version === 'v3' ? 'block' : 'none';
            document.getElementById('v2_checkbox_options').style.display = version === 'v2_checkbox' ? 'block' : 'none';
            document.getElementById('badge_options').style.display = version !== 'v2_checkbox' ? 'block' : 'none';
        }

        document.querySelectorAll('input[name="version"]').forEach(function(radio) {
            radio.addEventListener('change', function() {
                updateVersionOptions(this.value);
            });
        });

        // Test API Keys functionality
        document.getElementById('test_recaptcha_keys').addEventListener('click', function() {
            var btn = this;
            var resultEl = document.getElementById('test_recaptcha_result');
            var siteKey = document.getElementById('site_key').value;
            var secretKey = document.getElementById('secret_key').value;
            var nonce = document.getElementById('test_recaptcha_nonce').value;

            btn.disabled = true;
            btn.textContent = '<?php esc_html_e('Testing...', 'wpswitchboard'); ?>';
            resultEl.style.display = 'none';

            var formData = new FormData();
            formData.append('action', 'test_google_recaptcha_keys');
            formData.append('nonce', nonce);
            formData.append('site_key', siteKey);
            formData.append('secret_key', secretKey);

            fetch(ajaxurl, {
                method: 'POST',
                body: formData
            })
            .then(function(response) { return response.json(); })
            .then(function(data) {
                resultEl.style.display = 'inline';
                if (data.success) {
                    resultEl.innerHTML = '<span style="color: #16a34a;">' + data.data.message + '</span>';
                } else {
                    resultEl.innerHTML = '<span style="color: #dc2626;">' + data.data.message + '</span>';
                }
            })
            .catch(function(error) {
                resultEl.style.display = 'inline';
                resultEl.innerHTML = '<span style="color: #dc2626;">Connection error</span>';
            })
            .finally(function() {
                btn.disabled = false;
                btn.textContent = '<?php esc_html_e('Test API Keys', 'wpswitchboard'); ?>';
            });
        });
        </script>
        <?php
    }


    /**
     * Test API keys validity
     */
    public function test_api_keys() {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Permission denied']);
        }

        if (!wp_verify_nonce($_POST['nonce'] ?? '', 'test_google_recaptcha_keys')) {
            wp_send_json_error(['message' => 'Security check failed']);
        }

        $site_key = sanitize_text_field($_POST['site_key'] ?? '');
        $secret_key = sanitize_text_field($_POST['secret_key'] ?? '');

        if (empty($site_key) || empty($secret_key)) {
            wp_send_json_error(['message' => 'Please enter both Site Key and Secret Key']);
        }

        // Test the secret key by making a verification request with a dummy token
        // Google returns specific error codes for invalid keys
        $verify_url = 'https://www.google.com/recaptcha/api/siteverify';
        $response = wp_remote_post($verify_url, [
            'body' => [
                'secret' => $secret_key,
                'response' => 'test_token_for_key_validation',
            ],
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            wp_send_json_error(['message' => 'Connection failed: ' . $response->get_error_message()]);
        }

        $result = json_decode(wp_remote_retrieve_body($response), true);

        // Check for malformed response
        if (!is_array($result)) {
            wp_send_json_error(['message' => 'Invalid response from Google. Please try again.']);
        }

        // Check for specific error codes that indicate invalid keys
        if (isset($result['error-codes']) && is_array($result['error-codes'])) {
            if (in_array('invalid-input-secret', $result['error-codes'])) {
                wp_send_json_error(['message' => 'Invalid Secret Key. Please check your key and try again.']);
            }
        }

        // If we get here without invalid-input-secret, the secret key format is valid
        // The response will show success=false with "invalid-input-response" which is expected for the dummy token
        wp_send_json_success([
            'message' => 'API keys appear to be valid! Make sure your Site Key matches the domain configured in Google reCAPTCHA admin.'
        ]);
    }
}

/**
 * Fluent Forms Custom Field - Google reCAPTCHA
 * Extends BaseFieldManager to register a proper custom field type
 */
if (class_exists('FluentForm\App\Services\FormBuilder\BaseFieldManager')) {
    class SwitchboardFluentFormsRecaptcha extends \FluentForm\App\Services\FormBuilder\BaseFieldManager
    {
        private $recaptcha_module;

        public function __construct($recaptcha_module)
        {
            $this->recaptcha_module = $recaptcha_module;

            parent::__construct(
                'switchboard_recaptcha',                    // key
                __('Google reCAPTCHA', 'wpswitchboard'),   // title
                ['recaptcha', 'captcha', 'security', 'spam', 'google'], // tags
                'advanced'                                  // section
            );

            // Add validation filter
            add_filter('fluentform/validate_input_item_switchboard_recaptcha', [$this, 'validate'], 10, 5);

            // Hide field from entries (captcha responses shouldn't be stored)
            $this->hideFieldFromEntries();
        }

        /**
         * Get component definition for form builder
         */
        public function getComponent()
        {
            return [
                'index'          => 21,
                'element'        => $this->key,
                'attributes'     => [
                    'name'  => $this->key,
                    'class' => '',
                    'value' => '',
                    'type'  => 'text',
                ],
                'settings'       => [
                    'container_class'    => '',
                    'label'              => __('Security Check', 'wpswitchboard'),
                    'label_placement'    => '',
                    'help_message'       => '',
                    'error_message'      => __('reCAPTCHA verification failed. Please try again.', 'wpswitchboard'),
                    'conditional_logics' => [],
                ],
                'editor_options' => [
                    'title'      => __('Google reCAPTCHA', 'wpswitchboard'),
                    'icon_class' => 'ff-edit-shield',
                    'template'   => 'inputText',
                ],
            ];
        }

        /**
         * Define which settings appear in General tab
         */
        public function getGeneralEditorElements()
        {
            return [
                'label',
                'label_placement',
            ];
        }

        /**
         * Define custom editor elements
         */
        public function generalEditorElement()
        {
            return [
                'error_message' => [
                    'template' => 'inputText',
                    'label'    => __('Error Message', 'wpswitchboard'),
                ],
            ];
        }

        /**
         * Render the reCAPTCHA field on frontend
         */
        public function render($data, $form)
        {
            $settings = $this->recaptcha_module->get_settings();

            // Skip for logged-in users if whitelist enabled
            if ($settings['whitelist_logged_in'] && is_user_logged_in()) {
                return '';
            }

            // Check if API keys are configured
            if (empty($settings['site_key']) || empty($settings['secret_key'])) {
                if (current_user_can('manage_options')) {
                    echo '<div class="ff-el-group"><p style="color: #dc2626;">Google reCAPTCHA: API keys not configured. <a href="' . admin_url('admin.php?page=switchboard') . '">Configure in Switchboard</a></p></div>';
                }
                return '';
            }

            // render_captcha() outputs the complete reCAPTCHA widget
            echo '<div class="ff-el-group" style="margin-bottom: 15px;">';
            $this->recaptcha_module->render_captcha();
            echo '</div>';

            // Return empty string - reCAPTCHA widget is self-contained
            return '';
        }

        /**
         * Validate the reCAPTCHA on form submission
         */
        public function validate($errorMessage, $field, $formData, $fields, $form)
        {
            $settings = $this->recaptcha_module->get_settings();

            // Skip for logged-in users if whitelist enabled
            if ($settings['whitelist_logged_in'] && is_user_logged_in()) {
                return $errorMessage;
            }

            // Skip if keys not configured
            if (empty($settings['site_key']) || empty($settings['secret_key'])) {
                return $errorMessage;
            }

            if (!$this->recaptcha_module->verify_response()) {
                $error_msg = isset($field['raw']['settings']['error_message']) ? $field['raw']['settings']['error_message'] : $settings['error_message'];
                return [$error_msg];
            }

            return $errorMessage;
        }

        /**
         * Hide reCAPTCHA field from form entries
         */
        private function hideFieldFromEntries()
        {
            add_filter('fluentform/all_entry_labels', function ($formLabels, $form_id) {
                if (!function_exists('wpFluent')) {
                    return $formLabels;
                }
                $form = wpFluent()->table('fluentform_forms')->find($form_id);
                if (!$form) {
                    return $formLabels;
                }
                $fields = \FluentForm\App\Modules\Form\FormFieldsParser::getInputsByElementTypes($form, [$this->key]);
                if (is_array($fields) && !empty($fields)) {
                    foreach (array_keys($fields) as $key) {
                        unset($formLabels[$key]);
                    }
                }
                return $formLabels;
            }, 10, 2);
        }
    }
}

new SwitchboardGoogleReCaptcha();
