<?php
/**
 * Name: Hide My Login
 * Description: Hide your WordPress login page by creating a custom login URL. Blocks access to wp-login.php and wp-admin for non-logged-in users with advanced security features including IP whitelist, honeypot, rate limiting, and recovery options.
 * Category: Security
 * Icon: -
 * Tags: security, login, hide, custom-url, brute-force, protection
 * Added: 2026-01-22
 * Database: true
 * UseCases: [
 *   {"title": "Hide Login from Bots", "description": "Prevent automated attacks by hiding the default login URL"},
 *   {"title": "Custom Branded Login", "description": "Create a memorable login URL like /company-login/"},
 *   {"title": "Security Through Obscurity", "description": "Add an extra layer of security by making login URL unknown"}
 * ]
 * WhereToFind: {
 *   "location": "Configure in module settings",
 *   "url": "",
 *   "instructions": "Set your custom login slug - all login URLs will automatically update",
 *   "newTab": false
 * }
 */

// Prevent direct access
if (!defined('ABSPATH')) exit;

class SwitchboardHideMyLogin {

    /**
     * Database table name for access logs
     * @var string
     */
    private $table_name;

    /**
     * Option key for module settings
     * @var string
     */
    private $option_key = 'switchboard_hide-my-login_settings';

    /**
     * Blocked user agents
     * @var array
     */
    private $blocked_agents = ['curl', 'wget', 'python', 'scrapy', 'httpclient', 'libwww-perl', 'java', 'nikto', 'sqlmap'];

    /**
     * Rate limit transient prefix
     * @var string
     */
    private $rate_limit_prefix = 'sb_hml_rate_';

    /**
     * Cached settings (loaded once per request)
     * @var array|null
     */
    private $settings = null;

    /**
     * Constructor - register all hooks
     */
    public function __construct() {
        global $wpdb;
        $this->table_name = $wpdb->prefix . 'switchboard_login_access_log';

        // Module loads during init priority 10, so use priority 11 to ensure these fire
        add_action('init', [$this, 'block_default_login'], 11);
        add_action('init', [$this, 'handle_custom_login_request'], 11);

        // Validate login access
        add_action('login_init', [$this, 'validate_login_access'], 1);

        // Honeypot field
        add_action('login_form', [$this, 'add_honeypot_field']);
        add_filter('authenticate', [$this, 'check_honeypot'], 1, 3);

        // URL filters
        add_filter('login_url', [$this, 'filter_login_url'], 10, 3);
        add_filter('lostpassword_url', [$this, 'filter_lostpassword_url'], 10, 2);
        add_filter('register_url', [$this, 'filter_register_url'], 10, 1);
        add_filter('logout_url', [$this, 'filter_logout_url'], 10, 2);
        add_filter('site_url', [$this, 'filter_site_url'], 10, 4);
        add_filter('network_site_url', [$this, 'filter_network_site_url'], 10, 3);

        // Email filters
        add_filter('retrieve_password_message', [$this, 'filter_password_reset_email'], 10, 4);
        add_filter('wp_new_user_notification_email', [$this, 'filter_new_user_email'], 10, 3);
        add_filter('wp_new_user_notification_email_admin', [$this, 'filter_new_user_email_admin'], 10, 3);

        // WooCommerce compatibility
        add_filter('woocommerce_login_redirect', [$this, 'woocommerce_login_redirect'], 10, 2);
        add_filter('woocommerce_lostpassword_url', [$this, 'filter_lostpassword_url'], 10, 1);
        add_filter('woocommerce_registration_redirect', [$this, 'woocommerce_registration_redirect'], 10, 1);

        // Failed login handling - log the attempt (no redirect needed, login form displays errors natively)
        add_action('wp_login_failed', [$this, 'redirect_on_failed_login'], 10, 2);
        add_filter('login_message', [$this, 'add_logged_out_message']);

        // Logout redirect to custom URL
        add_action('wp_logout', [$this, 'redirect_after_logout']);

        // Admin page registration
        add_action('admin_menu', [$this, 'register_admin_page']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);

        // AJAX handlers
        add_action('wp_ajax_switchboard_hide_login_test_slug', [$this, 'ajax_test_custom_slug']);
        add_action('wp_ajax_switchboard_hide_login_get_logs', [$this, 'ajax_get_access_logs']);
        add_action('wp_ajax_switchboard_hide_login_clear_logs', [$this, 'ajax_clear_access_logs']);
        add_action('wp_ajax_switchboard_hide_login_export_logs', [$this, 'ajax_export_logs_csv']);
        add_action('wp_ajax_switchboard_hide_login_get_stats', [$this, 'ajax_get_stats']);

        // Cron for log cleanup
        add_action('switchboard_cleanup_login_access_log', [$this, 'cleanup_old_logs']);

        // Table creation
        $this->maybe_create_table();
    }

    /**
     * Called when module is activated
     */
    public function activate() {
        $this->create_table();
        if (!wp_next_scheduled('switchboard_cleanup_login_access_log')) {
            wp_schedule_event(time(), 'daily', 'switchboard_cleanup_login_access_log');
        }
    }

    /**
     * Called when module is deactivated
     */
    public function deactivate() {
        wp_clear_scheduled_hook('switchboard_cleanup_login_access_log');
    }

    /**
     * Register data locations for cleanup system
     */
    public function register_data_locations() {
        return [
            'options' => [
                'switchboard_hide-my-login_settings',
                'switchboard_hml_table_created',
            ],
            'option_patterns' => [],
            'tables' => [
                'switchboard_login_access_log',
            ],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }

    // =========================================================================
    // DATABASE
    // =========================================================================

    /**
     * Create database table if it doesn't exist (uses option flag to avoid DB query on every load)
     */
    private function maybe_create_table() {
        if (get_option('switchboard_hml_table_created')) {
            return;
        }
        global $wpdb;
        if ($wpdb->get_var("SHOW TABLES LIKE '{$this->table_name}'") != $this->table_name) {
            $this->create_table();
        }
        update_option('switchboard_hml_table_created', 1, true);
    }

    /**
     * Create the access log table
     */
    private function create_table() {
        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            ip_address varchar(45) NOT NULL,
            user_agent varchar(512) DEFAULT NULL,
            request_uri varchar(512) NOT NULL,
            request_type varchar(50) NOT NULL,
            blocked tinyint(1) DEFAULT 0,
            block_reason varchar(100) DEFAULT NULL,
            created_at datetime NOT NULL,
            PRIMARY KEY (id),
            KEY ip_address (ip_address),
            KEY created_at (created_at),
            KEY request_type (request_type),
            KEY blocked (blocked)
        ) $charset_collate;";

        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
    }

    // =========================================================================
    // SETTINGS
    // =========================================================================

    /**
     * Get module settings with defaults
     *
     * @return array
     */
    private function get_settings() {
        if ($this->settings !== null) {
            return $this->settings;
        }

        $defaults = [
            'custom_slug' => '',
            'block_default_login' => 'yes',
            'block_wp_admin' => 'yes',
            'ip_whitelist' => '',
            'path_whitelist' => '',
            'user_agent_blocking' => 'yes',
            'referer_check' => 'yes',
            'honeypot_enabled' => 'yes',
            'rate_limiting' => 'yes',
            'rate_limit_threshold' => 10,
            'rate_limit_window' => 3600,
            'log_attempts' => 'yes',
            'log_retention_days' => 30,
            'allow_site_health' => 'yes',
            'woocommerce_compat' => 'yes',
        ];

        $settings = get_option($this->option_key, []);
        $this->settings = wp_parse_args($settings, $defaults);
        return $this->settings;
    }

    // =========================================================================
    // URL INTERCEPTION & BLOCKING
    // =========================================================================

    /**
     * Block access to default login paths
     * Runs at init priority 1 for earliest possible interception
     */
    public function block_default_login() {
        // Skip for certain request types
        if ($this->should_allow_access()) {
            return;
        }

        // Skip for logged-in users
        if (is_user_logged_in()) {
            return;
        }

        $settings = $this->get_settings();

        // Check if custom slug is configured
        if (empty($settings['custom_slug'])) {
            return;
        }

        $request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
        $request_path = parse_url($request_uri, PHP_URL_PATH);
        $request_path = trim($request_path, '/');

        // Allow whitelisted IPs (recovery bypass)
        $client_ip = $this->get_client_ip();
        if ($this->is_ip_whitelisted($client_ip)) {
            return;
        }

        // Check HTTP referer for POST requests (block external login attempts)
        if ($_SERVER['REQUEST_METHOD'] === 'POST' && !$this->is_valid_referer()) {
            if ($settings['log_attempts'] === 'yes') {
                $this->log_access_attempt('external_post', true, 'invalid_referer');
            }
            $this->serve_404_page();
            exit;
        }

        // Check if accessing default login paths
        $should_block = false;
        $block_path = '';

        // Check for wp-login.php
        if ($settings['block_default_login'] === 'yes') {
            if (strpos($request_path, 'wp-login.php') !== false || $request_path === 'login') {
                $should_block = true;
                $block_path = 'wp-login.php';
            }
        }

        // Check for wp-admin
        if (!$should_block && $settings['block_wp_admin'] === 'yes') {
            if (strpos($request_path, 'wp-admin') === 0 || $request_path === 'wp-admin') {
                // Allow specific wp-admin paths
                if (!$this->is_allowed_admin_path($request_uri)) {
                    $should_block = true;
                    $block_path = 'wp-admin';
                }
            }
        }

        if ($should_block) {
            // Check rate limiting
            $rate_limited = false;
            if ($settings['rate_limiting'] === 'yes') {
                $rate_limited = !$this->check_rate_limit($client_ip);
            }

            // Log the access attempt
            if ($settings['log_attempts'] === 'yes') {
                $reason = $rate_limited ? 'rate_limited' : 'blocked_path';
                $this->log_access_attempt($block_path, true, $reason);
            }

            // Serve 404 page (stealth mode)
            $this->serve_404_page();
            exit;
        }
    }

    /**
     * Handle requests to custom login URL
     */
    public function handle_custom_login_request() {
        $settings = $this->get_settings();
        $custom_slug = sanitize_title($settings['custom_slug']);

        if (empty($custom_slug)) {
            return;
        }

        $request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
        $request_path = parse_url($request_uri, PHP_URL_PATH);
        $request_path = trim($request_path, '/');

        // Handle subdirectory installs
        $home_path = trim(parse_url(home_url(), PHP_URL_PATH), '/');
        if (!empty($home_path) && strpos($request_path, $home_path) === 0) {
            $request_path = trim(substr($request_path, strlen($home_path)), '/');
        }

        // Check if request matches custom slug
        if ($request_path === $custom_slug || strpos($request_path, $custom_slug . '/') === 0) {
            // Check user agent blocking
            if ($settings['user_agent_blocking'] === 'yes' && $this->is_blocked_user_agent()) {
                if ($settings['log_attempts'] === 'yes') {
                    $this->log_access_attempt('custom_login', true, 'blocked_user_agent');
                }
                $this->serve_404_page();
                exit;
            }

            // Log successful access to custom URL
            if ($settings['log_attempts'] === 'yes') {
                $this->log_access_attempt('custom_login', false, null);
            }

            // Set flag for custom login access
            $GLOBALS['switchboard_custom_login'] = true;

            // Preserve query parameters
            $query_string = parse_url($request_uri, PHP_URL_QUERY);
            if ($query_string) {
                parse_str($query_string, $params);
                foreach ($params as $key => $value) {
                    $_GET[$key] = $value;
                    $_REQUEST[$key] = $value;
                }
            }

            // Include wp-login.php
            require_once ABSPATH . 'wp-login.php';
            exit;
        }
    }

    /**
     * Validate login access when login_init fires
     */
    public function validate_login_access() {
        // Skip if coming from custom login
        if (!empty($GLOBALS['switchboard_custom_login'])) {
            return;
        }

        // Skip for certain request types
        if ($this->should_allow_access()) {
            return;
        }

        // Skip for logged-in users
        if (is_user_logged_in()) {
            return;
        }

        $settings = $this->get_settings();

        // Skip if no custom slug configured
        if (empty($settings['custom_slug'])) {
            return;
        }

        // Allow whitelisted IPs
        if ($this->is_ip_whitelisted($this->get_client_ip())) {
            return;
        }

        // Block access to wp-login.php if blocking is enabled
        if ($settings['block_default_login'] === 'yes') {
            if ($settings['log_attempts'] === 'yes') {
                $this->log_access_attempt('wp-login.php', true, 'blocked_path');
            }
            $this->serve_404_page();
            exit;
        }
    }

    /**
     * Check if this path should bypass blocking
     *
     * @param string $uri Request URI
     * @return bool
     */
    private function is_allowed_admin_path($uri) {
        $allowed = [
            'admin-ajax.php',
            'admin-post.php',
        ];

        foreach ($allowed as $path) {
            if (strpos($uri, $path) !== false) {
                return true;
            }
        }

        // Allow Site Health if enabled
        $settings = $this->get_settings();
        if ($settings['allow_site_health'] === 'yes' && $this->is_site_health_request()) {
            return true;
        }

        return false;
    }

    /**
     * Determine if this request should be allowed through
     *
     * @return bool
     */
    private function should_allow_access() {
        // AJAX requests
        if (wp_doing_ajax() || (defined('DOING_AJAX') && DOING_AJAX)) {
            return true;
        }

        // Cron requests
        if (wp_doing_cron() || (defined('DOING_CRON') && DOING_CRON)) {
            return true;
        }

        // REST API requests
        if (defined('REST_REQUEST') && REST_REQUEST) {
            return true;
        }

        // Check for REST API path
        $request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
        if (strpos($request_uri, '/wp-json/') !== false) {
            return true;
        }

        // CLI requests
        if (defined('WP_CLI') && WP_CLI) {
            return true;
        }

        // Allow interim login ONLY for logged-in users (Customizer, block editor)
        if (isset($_REQUEST['interim-login']) && is_user_logged_in()) {
            return true;
        }

        // Allow multisite signup/activate pages
        if ($this->is_multisite_signup()) {
            return true;
        }

        // Allow Jetpack SSO and other external auth systems
        if (isset($_REQUEST['action']) && in_array($_REQUEST['action'], ['jetpack-sso', 'jetpack_sso', 'postpass'])) {
            return true;
        }

        // User Switching plugin compatibility
        if (isset($_GET['action']) && in_array($_GET['action'], ['switch_to_user', 'switch_to_olduser', 'switch_off'])) {
            return true;
        }

        // Paid Memberships Pro compatibility - allow login from PMP forms
        if (isset($_POST['pmpro_login_form_used']) && $_POST['pmpro_login_form_used'] === '1') {
            return true;
        }

        // Check path whitelist
        if ($this->is_path_whitelisted($request_uri)) {
            return true;
        }

        // Allow password reset email links ONLY (must have both key and login params)
        // These come from email links and need direct wp-login.php access
        if (isset($_GET['action']) && in_array($_GET['action'], ['rp', 'resetpass'])) {
            if (isset($_GET['key']) && isset($_GET['login'])) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if request is from Site Health
     *
     * @return bool
     */
    private function is_site_health_request() {
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
        return strpos($user_agent, 'WordPress/') !== false && strpos($user_agent, 'Site Health') !== false;
    }

    /**
     * Check if current page is WooCommerce my-account
     *
     * @return bool
     */
    private function is_woocommerce_page() {
        if (!function_exists('is_account_page')) {
            return false;
        }
        return is_account_page();
    }

    /**
     * Check if request is multisite signup
     *
     * @return bool
     */
    private function is_multisite_signup() {
        if (!is_multisite()) {
            return false;
        }

        $request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
        return (strpos($request_uri, 'wp-signup.php') !== false || strpos($request_uri, 'wp-activate.php') !== false);
    }

    /**
     * Check if request path is in the path whitelist
     *
     * @param string $uri Request URI
     * @return bool
     */
    private function is_path_whitelisted($uri) {
        $settings = $this->get_settings();

        if (empty($settings['path_whitelist'])) {
            return false;
        }

        $whitelist = array_filter(array_map('trim', explode("\n", $settings['path_whitelist'])));
        $uri_path = parse_url($uri, PHP_URL_PATH);
        $uri_path = trim($uri_path, '/');

        foreach ($whitelist as $path) {
            $path = trim($path, '/');
            if (empty($path)) {
                continue;
            }

            // Check if URI starts with or contains the whitelisted path
            if ($uri_path === $path || strpos($uri_path, $path) === 0 || strpos($uri_path, $path . '/') !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if HTTP referer is valid (from same site)
     *
     * @return bool True if referer is valid or check should be skipped
     */
    private function is_valid_referer() {
        $settings = $this->get_settings();

        // Skip if referer check is disabled
        if ($settings['referer_check'] !== 'yes') {
            return true;
        }

        // Only check POST requests
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            return true;
        }

        // Skip for AJAX requests
        if (wp_doing_ajax()) {
            return true;
        }

        $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';

        // If no referer, block (could be cURL with spoofed user agent)
        if (empty($referer)) {
            return false;
        }

        // Check if referer is from the same site
        $site_url = get_site_url();
        $site_host = parse_url($site_url, PHP_URL_HOST);
        $referer_host = parse_url($referer, PHP_URL_HOST);

        // Allow if referer is from the same host
        if ($referer_host === $site_host) {
            return true;
        }

        // Also allow www/non-www variants
        if ($referer_host === 'www.' . $site_host || 'www.' . $referer_host === $site_host) {
            return true;
        }

        return false;
    }

    /**
     * Serve WordPress 404 page (stealth mode)
     */
    private function serve_404_page() {
        global $wp_query;

        // Set 404 status
        status_header(404);
        nocache_headers();

        // Try to load theme's 404 template
        if (function_exists('get_404_template')) {
            $template = get_404_template();
            if ($template) {
                // Set up a minimal query to make WordPress think this is a 404
                if ($wp_query) {
                    $wp_query->set_404();
                }
                include($template);
                return;
            }
        }

        // Fallback: basic 404 response
        wp_die(
            __('The page you are looking for does not exist.', 'wpswitchboard'),
            __('Page Not Found', 'wpswitchboard'),
            ['response' => 404]
        );
    }

    // =========================================================================
    // URL FILTERS
    // =========================================================================

    /**
     * Filter login URL to use custom slug
     *
     * @param string $login_url Original login URL
     * @param string $redirect Redirect destination
     * @param bool $force_reauth Force reauthentication
     * @return string
     */
    public function filter_login_url($login_url, $redirect = '', $force_reauth = false) {
        $settings = $this->get_settings();

        if (empty($settings['custom_slug'])) {
            return $login_url;
        }

        $custom_url = home_url($settings['custom_slug'] . '/');

        if (!empty($redirect)) {
            $custom_url = add_query_arg('redirect_to', urlencode($redirect), $custom_url);
        }

        if ($force_reauth) {
            $custom_url = add_query_arg('reauth', '1', $custom_url);
        }

        return $custom_url;
    }

    /**
     * Filter lost password URL
     * Note: WooCommerce filter only passes 1 param, WordPress passes 2
     *
     * @param string $url Original URL
     * @param string $redirect Redirect destination (optional - not passed by WooCommerce)
     * @return string
     */
    public function filter_lostpassword_url($url, $redirect = '') {
        $settings = $this->get_settings();

        if (empty($settings['custom_slug'])) {
            return $url;
        }

        $custom_url = add_query_arg('action', 'lostpassword', home_url($settings['custom_slug'] . '/'));

        // Only add redirect if it's a non-empty string (handles WooCommerce case)
        if (!empty($redirect) && is_string($redirect)) {
            $custom_url = add_query_arg('redirect_to', urlencode($redirect), $custom_url);
        }

        return $custom_url;
    }

    /**
     * Filter registration URL
     *
     * @param string $url Original URL
     * @return string
     */
    public function filter_register_url($url) {
        $settings = $this->get_settings();

        if (empty($settings['custom_slug'])) {
            return $url;
        }

        return add_query_arg('action', 'register', home_url($settings['custom_slug'] . '/'));
    }

    /**
     * Filter logout URL
     *
     * @param string $url Original URL
     * @param string $redirect Redirect destination
     * @return string
     */
    public function filter_logout_url($url, $redirect = '') {
        $settings = $this->get_settings();

        if (empty($settings['custom_slug'])) {
            return $url;
        }

        // Parse the original URL to get the nonce
        $parsed = wp_parse_url($url);
        $query = [];
        if (!empty($parsed['query'])) {
            parse_str($parsed['query'], $query);
        }

        $custom_url = add_query_arg('action', 'logout', home_url($settings['custom_slug'] . '/'));

        // Add the nonce
        if (!empty($query['_wpnonce'])) {
            $custom_url = add_query_arg('_wpnonce', $query['_wpnonce'], $custom_url);
        }

        if (!empty($redirect)) {
            $custom_url = add_query_arg('redirect_to', urlencode($redirect), $custom_url);
        }

        return $custom_url;
    }

    /**
     * Filter site_url to catch wp-login.php references
     *
     * @param string $url Site URL
     * @param string $path Path
     * @param string|null $scheme URL scheme
     * @param int|null $blog_id Blog ID
     * @return string
     */
    public function filter_site_url($url, $path, $scheme = null, $blog_id = null) {
        return $this->maybe_replace_login_url($url);
    }

    /**
     * Filter network_site_url for multisite
     *
     * @param string $url Site URL
     * @param string $path Path
     * @param string|null $scheme URL scheme
     * @return string
     */
    public function filter_network_site_url($url, $path, $scheme = null) {
        return $this->maybe_replace_login_url($url);
    }

    /**
     * Replace wp-login.php in URL with custom slug
     *
     * @param string $url URL to check
     * @return string
     */
    private function maybe_replace_login_url($url) {
        $settings = $this->get_settings();

        if (empty($settings['custom_slug'])) {
            return $url;
        }

        // Only replace if URL contains wp-login.php
        if (strpos($url, 'wp-login.php') === false) {
            return $url;
        }

        // Parse the URL
        $parsed = wp_parse_url($url);
        $query = [];
        if (!empty($parsed['query'])) {
            parse_str($parsed['query'], $query);
        }

        // Build custom URL
        $custom_url = home_url($settings['custom_slug'] . '/');

        // Re-add query parameters
        if (!empty($query)) {
            $custom_url = add_query_arg($query, $custom_url);
        }

        return $custom_url;
    }

    // =========================================================================
    // EMAIL FILTERS
    // =========================================================================

    /**
     * Filter password reset email to use custom login URL
     *
     * @param string $message Email message
     * @param string $key Reset key
     * @param string $user_login Username
     * @param WP_User $user_data User object
     * @return string
     */
    public function filter_password_reset_email($message, $key, $user_login, $user_data) {
        $settings = $this->get_settings();

        if (empty($settings['custom_slug'])) {
            return $message;
        }

        // Build the custom reset URL
        $custom_url = add_query_arg(
            [
                'action' => 'rp',
                'key' => $key,
                'login' => rawurlencode($user_login),
            ],
            home_url($settings['custom_slug'] . '/')
        );

        // Replace various possible URL formats
        // Format 1: network_site_url version
        $default_url_1 = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
        $message = str_replace($default_url_1, $custom_url, $message);

        // Format 2: site_url version
        $default_url_2 = site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
        $message = str_replace($default_url_2, $custom_url, $message);

        // Format 3: Generic wp-login.php replacement with regex for any remaining instances
        // This catches URLs where query params might be in different order
        $pattern = '/' . preg_quote(site_url('wp-login.php'), '/') . '\?[^\s<>"\']+action=rp[^\s<>"\']*key=' . preg_quote($key, '/') . '[^\s<>"\']*/';
        $message = preg_replace($pattern, $custom_url, $message);

        return $message;
    }

    /**
     * Filter new user notification email
     *
     * @param array $email Email data
     * @param WP_User $user User object
     * @param string $blogname Blog name
     * @return array
     */
    public function filter_new_user_email($email, $user, $blogname) {
        $settings = $this->get_settings();

        if (empty($settings['custom_slug'])) {
            return $email;
        }

        // Replace wp-login.php URLs in the email message
        $email['message'] = str_replace(
            site_url('wp-login.php'),
            home_url($settings['custom_slug'] . '/'),
            $email['message']
        );

        // Also handle network_site_url version
        $email['message'] = str_replace(
            network_site_url('wp-login.php'),
            home_url($settings['custom_slug'] . '/'),
            $email['message']
        );

        return $email;
    }

    /**
     * Filter new user notification email to admin
     *
     * @param array $email Email data
     * @param WP_User $user User object
     * @param string $blogname Blog name
     * @return array
     */
    public function filter_new_user_email_admin($email, $user, $blogname) {
        return $this->filter_new_user_email($email, $user, $blogname);
    }

    // =========================================================================
    // WOOCOMMERCE COMPATIBILITY
    // =========================================================================

    /**
     * Filter WooCommerce login redirect
     *
     * @param string $redirect Redirect URL
     * @param WP_User $user User object
     * @return string
     */
    public function woocommerce_login_redirect($redirect, $user) {
        $settings = $this->get_settings();

        // Check if WooCommerce compatibility is enabled
        if ($settings['woocommerce_compat'] !== 'yes') {
            return $redirect;
        }

        // If redirect is to my-account page, allow it
        if (function_exists('wc_get_page_id')) {
            $myaccount_page_id = wc_get_page_id('myaccount');
            if ($myaccount_page_id > 0) {
                $myaccount_url = get_permalink($myaccount_page_id);
                if (strpos($redirect, $myaccount_url) === 0) {
                    return $redirect;
                }
            }
        }

        return $redirect;
    }

    /**
     * Filter WooCommerce registration redirect
     *
     * @param string $redirect Redirect URL
     * @return string
     */
    public function woocommerce_registration_redirect($redirect) {
        $settings = $this->get_settings();

        // Check if WooCommerce compatibility is enabled
        if ($settings['woocommerce_compat'] !== 'yes') {
            return $redirect;
        }

        // Default to my-account page if empty
        if (empty($redirect) && function_exists('wc_get_page_id')) {
            $myaccount_page_id = wc_get_page_id('myaccount');
            if ($myaccount_page_id > 0) {
                return get_permalink($myaccount_page_id);
            }
        }

        return $redirect;
    }

    // =========================================================================
    // FAILED LOGIN HANDLING
    // =========================================================================

    /**
     * Redirect to custom login URL on failed login
     * This keeps users on the custom login page instead of exposing wp-login.php
     *
     * @param string $username Username that was attempted
     * @param WP_Error $error Error object
     */
    public function redirect_on_failed_login($username, $error = null) {
        $settings = $this->get_settings();

        // Skip if no custom slug configured
        if (empty($settings['custom_slug'])) {
            return;
        }

        // Don't redirect if already on the custom login URL (avoid redirect loops)
        if (!empty($GLOBALS['switchboard_custom_login'])) {
            return;
        }

        // Don't redirect for WooCommerce/custom login forms
        // These forms handle their own error display
        if (!isset($_POST['log']) || !isset($_POST['pwd'])) {
            return;
        }

        // Don't redirect if this is a WooCommerce login form
        if (isset($_POST['woocommerce-login-nonce'])) {
            return;
        }

        // Don't redirect if Paid Memberships Pro login form
        if (isset($_POST['pmpro_login_form_used'])) {
            return;
        }

        // Log the failed attempt (only if not already logged by another reason)
        if ($settings['log_attempts'] === 'yes') {
            $this->log_access_attempt('failed_login', true, 'invalid_credentials');
        }
    }

    /**
     * Add failed login error message to login form
     *
     * @param string $message Current login message
     * @return string
     */
    public function add_failed_login_message($message) {
        $settings = $this->get_settings();

        // Skip if no custom slug configured
        if (empty($settings['custom_slug'])) {
            return $message;
        }

        // Check if this is a failed login redirect
        if (isset($_GET['login_error']) && $_GET['login_error'] === '1') {
            $error_message = '<div id="login_error" class="notice notice-error">';
            $error_message .= '<strong>' . esc_html__('Error:', 'wpswitchboard') . '</strong> ';
            $error_message .= esc_html__('Invalid username/email or incorrect password.', 'wpswitchboard');
            $error_message .= '</div>';
            return $error_message . $message;
        }

        return $message;
    }

    /**
     * Add logged out success message to login form
     *
     * @param string $message Current login message
     * @return string
     */
    public function add_logged_out_message($message) {
        $settings = $this->get_settings();

        // Skip if no custom slug configured
        if (empty($settings['custom_slug'])) {
            return $message;
        }

        // Check if this is a logged out redirect
        if (isset($_GET['logged_out']) && $_GET['logged_out'] === '1') {
            $success_message = '<p class="message">';
            $success_message .= esc_html__('You are now logged out.', 'wpswitchboard');
            $success_message .= '</p>';
            return $success_message . $message;
        }

        return $message;
    }

    /**
     * Redirect to custom login URL after logout
     */
    public function redirect_after_logout() {
        $settings = $this->get_settings();

        // Skip if no custom slug configured
        if (empty($settings['custom_slug'])) {
            return;
        }

        // Build redirect URL with logged_out parameter
        $redirect_url = add_query_arg(
            ['logged_out' => '1'],
            home_url($settings['custom_slug'] . '/')
        );

        wp_safe_redirect($redirect_url);
        exit;
    }

    // =========================================================================
    // SECURITY FEATURES
    // =========================================================================

    /**
     * Add honeypot field to login form
     */
    public function add_honeypot_field() {
        $settings = $this->get_settings();

        if ($settings['honeypot_enabled'] !== 'yes') {
            return;
        }

        // Add a hidden field that bots will fill out
        ?>
        <p class="login-honeypot" style="position: absolute; left: -9999px; opacity: 0; height: 0; overflow: hidden;">
            <label for="sb_website"><?php esc_html_e('Website', 'wpswitchboard'); ?></label>
            <input type="text" name="sb_website" id="sb_website" autocomplete="off" tabindex="-1" value="">
        </p>
        <?php
    }

    /**
     * Check honeypot field on authentication
     *
     * @param WP_User|WP_Error|null $user User object or error
     * @param string $username Username
     * @param string $password Password
     * @return WP_User|WP_Error|null
     */
    public function check_honeypot($user, $username, $password) {
        $settings = $this->get_settings();

        if ($settings['honeypot_enabled'] !== 'yes') {
            return $user;
        }

        // Check if honeypot field was filled
        if (!empty($_POST['sb_website'])) {
            // Log the attempt
            if ($settings['log_attempts'] === 'yes') {
                $this->log_access_attempt('honeypot', true, 'honeypot_triggered');
            }

            // Die silently instead of returning WP_Error to avoid triggering
            // wp_login_failed, which would count this as a failed attempt in
            // Limit Login Attempts and lockout legitimate users sharing the IP
            $this->serve_404_page();
            exit;
        }

        return $user;
    }

    /**
     * Check if IP is in whitelist
     *
     * @param string $ip IP address
     * @return bool
     */
    private function is_ip_whitelisted($ip) {
        $settings = $this->get_settings();

        if (empty($settings['ip_whitelist'])) {
            return false;
        }

        $whitelist = array_filter(array_map('trim', explode("\n", $settings['ip_whitelist'])));

        foreach ($whitelist as $entry) {
            // Check for exact match
            if ($entry === $ip) {
                return true;
            }

            // Check for CIDR notation
            if (strpos($entry, '/') !== false && $this->ip_in_cidr($ip, $entry)) {
                return true;
            }

            // Check for wildcard
            if (strpos($entry, '*') !== false) {
                $pattern = str_replace(['*', '.'], ['[0-9]+', '\.'], $entry);
                if (preg_match('/^' . $pattern . '$/', $ip)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Check if IP is in CIDR range
     *
     * @param string $ip IP address
     * @param string $cidr CIDR notation
     * @return bool
     */
    private function ip_in_cidr($ip, $cidr) {
        list($subnet, $mask) = explode('/', $cidr);

        // IPv4
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $mask = intval($mask);
            if ($mask < 0 || $mask > 32) {
                return false;
            }
            $ip_long = ip2long($ip);
            $subnet_long = ip2long($subnet);
            $mask_long = -1 << (32 - $mask);
            return ($ip_long & $mask_long) === ($subnet_long & $mask_long);
        }

        // IPv6
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            $ip_bin = inet_pton($ip);
            $subnet_bin = inet_pton($subnet);
            $mask = intval($mask);

            if ($ip_bin === false || $subnet_bin === false || $mask < 0 || $mask > 128) {
                return false;
            }

            $mask_bytes = str_repeat("\xff", $mask >> 3);
            if ($mask & 7) {
                $mask_bytes .= chr(0xff << (8 - ($mask & 7)));
            }
            $mask_bytes = str_pad($mask_bytes, 16, "\x00");

            return ($ip_bin & $mask_bytes) === ($subnet_bin & $mask_bytes);
        }

        return false;
    }

    /**
     * Check if user agent is blocked
     *
     * @return bool
     */
    private function is_blocked_user_agent() {
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower($_SERVER['HTTP_USER_AGENT']) : '';

        if (empty($user_agent)) {
            return true; // Block requests with no user agent
        }

        foreach ($this->blocked_agents as $blocked) {
            if (strpos($user_agent, $blocked) !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * Check and update rate limit
     *
     * @param string $ip IP address
     * @return bool True if allowed, false if rate limited
     */
    private function check_rate_limit($ip) {
        $settings = $this->get_settings();

        if ($settings['rate_limiting'] !== 'yes') {
            return true;
        }

        $threshold = intval($settings['rate_limit_threshold']);
        $window = intval($settings['rate_limit_window']);
        $transient_key = $this->rate_limit_prefix . md5($ip);

        $attempts = get_transient($transient_key);

        if ($attempts === false) {
            // First attempt
            set_transient($transient_key, 1, $window);
            return true;
        }

        if ($attempts >= $threshold) {
            // Rate limited
            return false;
        }

        // Increment attempts
        set_transient($transient_key, $attempts + 1, $window);
        return true;
    }

    /**
     * Log access attempt to database
     *
     * @param string $type Request type
     * @param bool $blocked Whether request was blocked
     * @param string|null $reason Block reason
     */
    private function log_access_attempt($type, $blocked, $reason) {
        global $wpdb;

        $ip = $this->get_client_ip();
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? substr($_SERVER['HTTP_USER_AGENT'], 0, 512) : '';
        $request_uri = isset($_SERVER['REQUEST_URI']) ? substr($_SERVER['REQUEST_URI'], 0, 512) : '';

        $wpdb->insert(
            $this->table_name,
            [
                'ip_address' => $ip,
                'user_agent' => $user_agent,
                'request_uri' => $request_uri,
                'request_type' => $type,
                'blocked' => $blocked ? 1 : 0,
                'block_reason' => $reason,
                'created_at' => current_time('mysql'),
            ],
            ['%s', '%s', '%s', '%s', '%d', '%s', '%s']
        );

        // Integration with Activity Log module if active
        if ($blocked && class_exists('SwitchboardActivityLog')) {
            $action_labels = [
                'wp-login.php' => 'Blocked access to wp-login.php',
                'wp-admin' => 'Blocked access to wp-admin',
                'custom_login' => 'Blocked access to custom login URL',
                'honeypot' => 'Honeypot triggered - bot detected',
            ];
            $action_label = isset($action_labels[$type]) ? $action_labels[$type] : 'Blocked login access attempt';

            do_action('switchboard_log_activity', [
                'action' => 'hide_my_login_blocked',
                'object_type' => 'security',
                'object_name' => $action_label,
                'severity' => 'warning',
                'details' => sprintf('IP: %s | Reason: %s | URI: %s', $ip, $reason ?: 'blocked_path', $request_uri),
            ]);
        }
    }

    /**
     * Get client IP address (secure - for blocking/rate limiting)
     *
     * @return string
     */
    private function get_client_ip() {
        // For security rate limiting, ONLY trust REMOTE_ADDR
        // HTTP headers like X-Forwarded-For can be spoofed
        $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
        $validated_ip = filter_var($ip, FILTER_VALIDATE_IP);
        return $validated_ip ? $validated_ip : '0.0.0.0';
    }

    /**
     * Get client IP for display purposes (checks forwarded headers)
     * Only use this for UI display, NOT for security decisions
     *
     * @return string
     */
    private function get_display_ip() {
        $ip = '';

        // Check forwarded headers (for proxies/load balancers)
        $headers = [
            'HTTP_CF_CONNECTING_IP',     // Cloudflare
            'HTTP_X_REAL_IP',            // Nginx proxy
            'HTTP_X_FORWARDED_FOR',      // Standard proxy header
            'HTTP_CLIENT_IP',            // Some proxies
            'HTTP_X_CLUSTER_CLIENT_IP',  // Cluster setups
        ];

        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                // X-Forwarded-For can contain multiple IPs, take the first
                $ips = explode(',', $_SERVER[$header]);
                $ip = trim($ips[0]);
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                    return $ip;
                }
            }
        }

        // Fall back to REMOTE_ADDR
        $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';

        // Check if it's a loopback address
        if (in_array($ip, ['::1', '127.0.0.1', 'localhost'])) {
            // On localhost, return the loopback but indicate it in the UI
            return $ip . ' (localhost)';
        }

        $validated_ip = filter_var($ip, FILTER_VALIDATE_IP);
        return $validated_ip ? $validated_ip : '0.0.0.0';
    }

    /**
     * Cleanup old log entries
     */
    public function cleanup_old_logs() {
        global $wpdb;

        $settings = $this->get_settings();
        $days = intval($settings['log_retention_days']);

        if ($days <= 0) {
            $days = 30;
        }

        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$this->table_name} WHERE created_at < %s",
            date('Y-m-d H:i:s', strtotime("-{$days} days"))
        ));
    }

    // =========================================================================
    // UTILITIES
    // =========================================================================

    /**
     * Get custom login URL
     *
     * @param string $path Additional path
     * @param array $args Query arguments
     * @return string
     */
    public function get_custom_login_url($path = '', $args = []) {
        $settings = $this->get_settings();

        if (empty($settings['custom_slug'])) {
            return wp_login_url();
        }

        $url = home_url($settings['custom_slug'] . '/');

        if (!empty($path)) {
            $url = trailingslashit($url) . ltrim($path, '/');
        }

        if (!empty($args)) {
            $url = add_query_arg($args, $url);
        }

        return $url;
    }

    /**
     * Validate a custom slug
     *
     * @param string $slug Proposed slug
     * @return array Result with 'valid' boolean and 'message' string
     */
    private function validate_custom_slug($slug) {
        $slug = sanitize_title($slug);

        if (empty($slug)) {
            return [
                'valid' => false,
                'message' => __('Please enter a custom login slug.', 'wpswitchboard'),
            ];
        }

        // Check minimum length
        if (strlen($slug) < 3) {
            return [
                'valid' => false,
                'message' => __('The slug must be at least 3 characters long.', 'wpswitchboard'),
            ];
        }

        // Check for reserved slugs
        $reserved = ['wp-admin', 'wp-login', 'wp-login.php', 'login', 'admin', 'dashboard', 'wp-content', 'wp-includes', 'wp-json', 'feed', 'rss'];
        if (in_array($slug, $reserved)) {
            return [
                'valid' => false,
                'message' => __('This slug is reserved and cannot be used.', 'wpswitchboard'),
            ];
        }

        // Check if slug conflicts with existing page/post
        $post = get_page_by_path($slug, OBJECT, ['page', 'post']);
        if ($post) {
            return [
                'valid' => false,
                'message' => sprintf(__('This slug conflicts with an existing %s.', 'wpswitchboard'), $post->post_type),
            ];
        }

        // Check if slug conflicts with a registered taxonomy term
        $term = term_exists($slug);
        if ($term) {
            return [
                'valid' => false,
                'message' => __('This slug conflicts with an existing taxonomy term.', 'wpswitchboard'),
            ];
        }

        return [
            'valid' => true,
            'message' => __('This slug is available.', 'wpswitchboard'),
        ];
    }

    // =========================================================================
    // ADMIN PAGE
    // =========================================================================

    /**
     * Register admin submenu page
     */
    public function register_admin_page() {
        add_submenu_page(
            'switchboard', // Parent slug
            __('Hide My Login - Access Log', 'wpswitchboard'),
            __('Hide My Login Log', 'wpswitchboard'),
            'manage_options',
            'switchboard-hide-my-login',
            [$this, 'render_admin_page']
        );
    }

    /**
     * Render admin page
     */
    public function render_admin_page() {
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_die(__('You do not have sufficient permissions to access this page.', 'wpswitchboard'));
        }

        // Include template
        include plugin_dir_path(__FILE__) . '../../assets/hide-my-login/admin-page.php';
    }

    /**
     * Enqueue admin assets
     *
     * @param string $hook Current admin page hook
     */
    public function enqueue_admin_assets($hook) {
        // Only load on our admin page - check for various hook formats
        if (strpos($hook, 'switchboard-hide-my-login') === false) {
            return;
        }

        // Enqueue the main Switchboard admin CSS (provides .sb-module-* classes)
        if (defined('SWITCHBOARD_URL') && defined('SWITCHBOARD_VERSION')) {
            wp_enqueue_style(
                'switchboard-admin',
                SWITCHBOARD_URL . 'switchboard-admin.css',
                [],
                SWITCHBOARD_VERSION
            );
        }

        // Enqueue dashicons
        wp_enqueue_style('dashicons');

        $assets_url = plugins_url('assets/hide-my-login/', dirname(dirname(__FILE__)));

        // Enqueue hide-my-login specific CSS
        wp_enqueue_style(
            'switchboard-hide-my-login-admin',
            $assets_url . 'admin-page.css',
            ['switchboard-admin'],
            filemtime(plugin_dir_path(__FILE__) . '../../assets/hide-my-login/admin-page.css')
        );

        wp_enqueue_script(
            'switchboard-hide-my-login-admin',
            $assets_url . 'admin-page.js',
            ['jquery'],
            filemtime(plugin_dir_path(__FILE__) . '../../assets/hide-my-login/admin-page.js'),
            true
        );

        wp_localize_script('switchboard-hide-my-login-admin', 'switchboardHideLogin', [
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('switchboard_hide_login_nonce'),
            'strings' => [
                'loading' => __('Loading...', 'wpswitchboard'),
                'noResults' => __('No access attempts found.', 'wpswitchboard'),
                'confirmDelete' => __('Are you sure you want to delete the selected entries? This cannot be undone.', 'wpswitchboard'),
                'confirmDeleteAll' => __('Are you sure you want to delete ALL access log entries? This cannot be undone.', 'wpswitchboard'),
                'deleteSuccess' => __('Entries deleted successfully.', 'wpswitchboard'),
                'error' => __('An error occurred. Please try again.', 'wpswitchboard'),
            ],
        ]);
    }

    // =========================================================================
    // AJAX HANDLERS
    // =========================================================================

    /**
     * AJAX: Test if custom slug is available
     */
    public function ajax_test_custom_slug() {
        check_ajax_referer('switchboard_hide_login_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied.', 'wpswitchboard')]);
        }

        $slug = isset($_POST['slug']) ? sanitize_title($_POST['slug']) : '';
        $result = $this->validate_custom_slug($slug);

        if ($result['valid']) {
            wp_send_json_success(['message' => $result['message']]);
        } else {
            wp_send_json_error(['message' => $result['message']]);
        }
    }

    /**
     * AJAX: Get paginated access logs
     */
    public function ajax_get_access_logs() {
        check_ajax_referer('switchboard_hide_login_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied.', 'wpswitchboard')]);
        }

        global $wpdb;

        $page = isset($_POST['page']) ? max(1, intval($_POST['page'])) : 1;
        $per_page = isset($_POST['per_page']) ? min(100, max(10, intval($_POST['per_page']))) : 50;
        $offset = ($page - 1) * $per_page;

        // Build where clause
        $where = ['1=1'];
        $params = [];

        // Filter by type
        if (!empty($_POST['type'])) {
            $type = sanitize_text_field($_POST['type']);
            if ($type === 'blocked') {
                $where[] = 'blocked = 1';
            } elseif ($type === 'allowed') {
                $where[] = 'blocked = 0';
            }
        }

        // Filter by date range
        if (!empty($_POST['date_from'])) {
            $where[] = 'created_at >= %s';
            $params[] = sanitize_text_field($_POST['date_from']) . ' 00:00:00';
        }
        if (!empty($_POST['date_to'])) {
            $where[] = 'created_at <= %s';
            $params[] = sanitize_text_field($_POST['date_to']) . ' 23:59:59';
        }

        // Filter by IP
        if (!empty($_POST['ip'])) {
            $where[] = 'ip_address LIKE %s';
            $params[] = '%' . $wpdb->esc_like(sanitize_text_field($_POST['ip'])) . '%';
        }

        // Filter by search term
        if (!empty($_POST['search'])) {
            $search = '%' . $wpdb->esc_like(sanitize_text_field($_POST['search'])) . '%';
            $where[] = '(ip_address LIKE %s OR user_agent LIKE %s OR request_uri LIKE %s)';
            $params[] = $search;
            $params[] = $search;
            $params[] = $search;
        }

        $where_sql = implode(' AND ', $where);

        // Get total count
        $count_sql = "SELECT COUNT(*) FROM {$this->table_name} WHERE {$where_sql}";
        if (!empty($params)) {
            $count_sql = $wpdb->prepare($count_sql, ...$params);
        }
        $total = $wpdb->get_var($count_sql);

        // Get results
        $query = "SELECT * FROM {$this->table_name} WHERE {$where_sql} ORDER BY created_at DESC LIMIT %d OFFSET %d";
        $query_params = array_merge($params, [$per_page, $offset]);
        $results = $wpdb->get_results($wpdb->prepare($query, ...$query_params));

        wp_send_json_success([
            'logs' => $results,
            'total' => intval($total),
            'page' => $page,
            'per_page' => $per_page,
            'total_pages' => ceil($total / $per_page),
        ]);
    }

    /**
     * AJAX: Clear access logs
     */
    public function ajax_clear_access_logs() {
        check_ajax_referer('switchboard_hide_login_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied.', 'wpswitchboard')]);
        }

        global $wpdb;

        $ids = isset($_POST['ids']) ? array_map('intval', (array)$_POST['ids']) : [];

        if (empty($ids)) {
            // Clear all logs
            $wpdb->query("TRUNCATE TABLE {$this->table_name}");
        } else {
            // Delete specific entries
            $placeholders = implode(',', array_fill(0, count($ids), '%d'));
            $wpdb->query($wpdb->prepare(
                "DELETE FROM {$this->table_name} WHERE id IN ({$placeholders})",
                ...$ids
            ));
        }

        wp_send_json_success(['message' => __('Logs cleared successfully.', 'wpswitchboard')]);
    }

    /**
     * AJAX: Export logs as CSV (respects current filters)
     */
    public function ajax_export_logs_csv() {
        // Check nonce from GET or POST (export uses GET with query params)
        if (!wp_verify_nonce(isset($_GET['nonce']) ? $_GET['nonce'] : (isset($_POST['nonce']) ? $_POST['nonce'] : ''), 'switchboard_hide_login_nonce')) {
            wp_die(__('Security check failed.', 'wpswitchboard'));
        }

        if (!current_user_can('manage_options')) {
            wp_die(__('Permission denied.', 'wpswitchboard'));
        }

        global $wpdb;

        // Build where clause matching the same filters as ajax_get_access_logs()
        $where = ['1=1'];
        $params = [];

        if (!empty($_GET['type'])) {
            $type = sanitize_text_field($_GET['type']);
            if ($type === 'blocked') {
                $where[] = 'blocked = 1';
            } elseif ($type === 'allowed') {
                $where[] = 'blocked = 0';
            }
        }

        if (!empty($_GET['date_from'])) {
            $where[] = 'created_at >= %s';
            $params[] = sanitize_text_field($_GET['date_from']) . ' 00:00:00';
        }
        if (!empty($_GET['date_to'])) {
            $where[] = 'created_at <= %s';
            $params[] = sanitize_text_field($_GET['date_to']) . ' 23:59:59';
        }

        if (!empty($_GET['search'])) {
            $search = '%' . $wpdb->esc_like(sanitize_text_field($_GET['search'])) . '%';
            $where[] = '(ip_address LIKE %s OR user_agent LIKE %s OR request_uri LIKE %s)';
            $params[] = $search;
            $params[] = $search;
            $params[] = $search;
        }

        $where_sql = implode(' AND ', $where);
        $query = "SELECT * FROM {$this->table_name} WHERE {$where_sql} ORDER BY created_at DESC";

        if (!empty($params)) {
            $results = $wpdb->get_results($wpdb->prepare($query, ...$params), ARRAY_A);
        } else {
            $results = $wpdb->get_results($query, ARRAY_A);
        }

        // Set headers for CSV download
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename=hide-my-login-access-log-' . date('Y-m-d') . '.csv');
        header('Pragma: no-cache');
        header('Expires: 0');

        $output = fopen('php://output', 'w');

        // Add BOM for Excel UTF-8 compatibility
        fputs($output, "\xEF\xBB\xBF");

        // Header row
        fputcsv($output, [
            'ID',
            'IP Address',
            'User Agent',
            'Request URI',
            'Request Type',
            'Blocked',
            'Block Reason',
            'Date/Time',
        ]);

        // Data rows
        foreach ($results as $row) {
            fputcsv($output, [
                $row['id'],
                $row['ip_address'],
                $row['user_agent'],
                $row['request_uri'],
                $row['request_type'],
                $row['blocked'] ? 'Yes' : 'No',
                $row['block_reason'],
                $row['created_at'],
            ]);
        }

        fclose($output);
        exit;
    }

    /**
     * AJAX: Get stats for admin page
     */
    public function ajax_get_stats() {
        check_ajax_referer('switchboard_hide_login_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied.', 'wpswitchboard')]);
        }

        global $wpdb;

        $stats = [
            'total' => $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name}"),
            'total_blocked' => $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name} WHERE blocked = 1"),
            'total_allowed' => $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name} WHERE blocked = 0"),
            'today' => $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$this->table_name} WHERE created_at >= %s",
                date('Y-m-d 00:00:00')
            )),
            'today_blocked' => $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$this->table_name} WHERE blocked = 1 AND created_at >= %s",
                date('Y-m-d 00:00:00')
            )),
            'top_ips' => $wpdb->get_results(
                "SELECT ip_address, COUNT(*) as count FROM {$this->table_name} WHERE blocked = 1 GROUP BY ip_address ORDER BY count DESC LIMIT 5"
            ),
        ];

        wp_send_json_success($stats);
    }

    // =========================================================================
    // SETTINGS UI
    // =========================================================================

    /**
     * Render settings panel
     *
     * @param array $settings Current module settings
     */
    public function render_settings($settings = []) {
        global $wpdb;

        $settings = wp_parse_args($settings, $this->get_settings());
        $custom_slug = isset($settings['custom_slug']) ? $settings['custom_slug'] : '';
        $block_default_login = isset($settings['block_default_login']) ? $settings['block_default_login'] : 'yes';
        $block_wp_admin = isset($settings['block_wp_admin']) ? $settings['block_wp_admin'] : 'yes';
        $ip_whitelist = isset($settings['ip_whitelist']) ? $settings['ip_whitelist'] : '';
        $path_whitelist = isset($settings['path_whitelist']) ? $settings['path_whitelist'] : '';
        $user_agent_blocking = isset($settings['user_agent_blocking']) ? $settings['user_agent_blocking'] : 'yes';
        $referer_check = isset($settings['referer_check']) ? $settings['referer_check'] : 'yes';
        $honeypot_enabled = isset($settings['honeypot_enabled']) ? $settings['honeypot_enabled'] : 'yes';
        $rate_limiting = isset($settings['rate_limiting']) ? $settings['rate_limiting'] : 'yes';
        $rate_limit_threshold = isset($settings['rate_limit_threshold']) ? intval($settings['rate_limit_threshold']) : 10;
        $rate_limit_window = isset($settings['rate_limit_window']) ? intval($settings['rate_limit_window']) : 3600;
        $log_attempts = isset($settings['log_attempts']) ? $settings['log_attempts'] : 'yes';
        $log_retention_days = isset($settings['log_retention_days']) ? intval($settings['log_retention_days']) : 30;
        $allow_site_health = isset($settings['allow_site_health']) ? $settings['allow_site_health'] : 'yes';
        $woocommerce_compat = isset($settings['woocommerce_compat']) ? $settings['woocommerce_compat'] : 'yes';

        // Get stats
        $total_blocked = 0;
        $total_allowed = 0;
        $today_blocked = 0;
        if ($wpdb->get_var("SHOW TABLES LIKE '{$this->table_name}'") == $this->table_name) {
            $total_blocked = $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name} WHERE blocked = 1");
            $total_allowed = $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name} WHERE blocked = 0");
            $today_blocked = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$this->table_name} WHERE blocked = 1 AND created_at >= %s",
                date('Y-m-d 00:00:00')
            ));
        }
        ?>
        <input type="hidden" name="module" value="hide-my-login">

        <!-- How to Use Section -->
        <div class="sb-sp-info-box sb-sp-info-box-tip" style="margin-bottom: 16px; background: #fef3c7; border-color: #fcd34d;">
            <h4 class="sb-sp-heading-sm" style="color: #92400e; margin: 0 0 12px 0;"><?php esc_html_e('How to Use Hide My Login', 'wpswitchboard'); ?></h4>
            <div style="font-size: 13px; color: #78350f; line-height: 1.6;">
                <p style="margin: 0 0 8px;"><strong><?php esc_html_e('Step 1:', 'wpswitchboard'); ?></strong> <?php esc_html_e('Enter a custom slug below (e.g., "my-secret-login" or "secure-access").', 'wpswitchboard'); ?></p>
                <p style="margin: 0 0 8px;"><strong><?php esc_html_e('Step 2:', 'wpswitchboard'); ?></strong> <?php esc_html_e('Save settings. Your new login URL will be active immediately.', 'wpswitchboard'); ?></p>
                <p style="margin: 0;"><strong><?php esc_html_e('Step 3:', 'wpswitchboard'); ?></strong> <?php esc_html_e('Test your new URL in a private/incognito window before logging out!', 'wpswitchboard'); ?></p>
            </div>
        </div>

        <!-- Recovery Warning Box -->
        <div style="margin-bottom: 24px; background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%); border: 2px solid #f87171; border-radius: 12px; padding: 20px; position: relative; overflow: hidden;">
            <div style="position: absolute; top: -10px; right: -10px; font-size: 80px; opacity: 0.1; transform: rotate(15deg);">🔐</div>
            <div style="display: flex; align-items: flex-start; gap: 16px; position: relative; z-index: 1;">
                <div style="flex-shrink: 0; width: 48px; height: 48px; background: #dc2626; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 12px rgba(220, 38, 38, 0.3);">
                    <span class="dashicons dashicons-shield" style="color: white; font-size: 24px; width: 24px; height: 24px;"></span>
                </div>
                <div style="flex: 1;">
                    <h4 style="margin: 0 0 8px; font-size: 16px; font-weight: 700; color: #991b1b;">
                        <?php esc_html_e('⚠️ Before You Continue: Set Up Recovery Access!', 'wpswitchboard'); ?>
                    </h4>
                    <p style="margin: 0 0 12px; font-size: 14px; color: #b91c1c; line-height: 1.5;">
                        <?php esc_html_e('If you forget your custom login URL, you will be locked out of your site. Prevent this by:', 'wpswitchboard'); ?>
                    </p>
                    <div style="display: grid; gap: 8px;">
                        <div style="display: flex; align-items: center; gap: 8px; background: white; padding: 10px 14px; border-radius: 8px; border: 1px solid #fca5a5;">
                            <span style="background: #dc2626; color: white; font-size: 11px; font-weight: 700; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center;">1</span>
                            <span style="font-size: 13px; color: #7f1d1d;">
                                <strong><?php esc_html_e('Add your IP to the whitelist', 'wpswitchboard'); ?></strong>
                                <?php esc_html_e('— Scroll down to "IP Whitelist" and click "Add My Current IP"', 'wpswitchboard'); ?>
                            </span>
                        </div>
                        <div style="display: flex; align-items: center; gap: 8px; background: white; padding: 10px 14px; border-radius: 8px; border: 1px solid #fca5a5;">
                            <span style="background: #dc2626; color: white; font-size: 11px; font-weight: 700; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center;">2</span>
                            <span style="font-size: 13px; color: #7f1d1d;">
                                <strong><?php esc_html_e('Write down your custom URL', 'wpswitchboard'); ?></strong>
                                <?php esc_html_e('— Save it somewhere safe like a password manager', 'wpswitchboard'); ?>
                            </span>
                        </div>
                        <div style="display: flex; align-items: center; gap: 8px; background: white; padding: 10px 14px; border-radius: 8px; border: 1px solid #fca5a5;">
                            <span style="background: #dc2626; color: white; font-size: 11px; font-weight: 700; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center;">3</span>
                            <span style="font-size: 13px; color: #7f1d1d;">
                                <strong><?php esc_html_e('Test before logging out', 'wpswitchboard'); ?></strong>
                                <?php esc_html_e('— Open your new URL in an incognito window to confirm it works', 'wpswitchboard'); ?>
                            </span>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <?php if (!empty($custom_slug)): ?>
        <!-- Stats Card -->
        <div class="sb-module-card sb-module-form-section" style="margin-bottom: 24px;">
            <div class="sb-module-card-header">
                <div class="sb-module-card-header-left">
                    <h2 class="sb-module-card-title"><?php esc_html_e('Protection Overview', 'wpswitchboard'); ?></h2>
                    <p class="sb-module-card-desc"><?php esc_html_e('Login protection statistics', 'wpswitchboard'); ?></p>
                </div>
                <a href="<?php echo esc_url(admin_url('admin.php?page=switchboard-hide-my-login')); ?>" class="sb-module-button sb-module-button-secondary sb-module-button-sm">
                    <?php esc_html_e('View Full Log', 'wpswitchboard'); ?> &rarr;
                </a>
            </div>
            <div class="sb-module-card-body">
                <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 16px;">
                    <div style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 20px; text-align: center;">
                        <div style="font-size: 32px; font-weight: 700; color: #dc2626;"><?php echo intval($total_blocked); ?></div>
                        <div style="font-size: 13px; color: #991b1b; margin-top: 4px;"><?php esc_html_e('Total Blocked', 'wpswitchboard'); ?></div>
                    </div>
                    <div style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 20px; text-align: center;">
                        <div style="font-size: 32px; font-weight: 700; color: #dc2626;"><?php echo intval($today_blocked); ?></div>
                        <div style="font-size: 13px; color: #991b1b; margin-top: 4px;"><?php esc_html_e('Blocked Today', 'wpswitchboard'); ?></div>
                    </div>
                    <div style="background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 20px; text-align: center;">
                        <div style="font-size: 32px; font-weight: 700; color: #16a34a;"><?php echo intval($total_allowed); ?></div>
                        <div style="font-size: 13px; color: #166534; margin-top: 4px;"><?php esc_html_e('Legitimate Logins', 'wpswitchboard'); ?></div>
                    </div>
                </div>
            </div>
        </div>
        <?php endif; ?>

        <!-- Custom Login URL Card -->
        <div class="sb-module-card sb-module-form-section" style="margin-bottom: 24px;">
            <div class="sb-module-card-header">
                <div class="sb-module-card-header-left">
                    <h2 class="sb-module-card-title"><?php esc_html_e('Custom Login URL', 'wpswitchboard'); ?></h2>
                    <p class="sb-module-card-desc"><?php esc_html_e('Create a secret URL for your login page', 'wpswitchboard'); ?></p>
                </div>
                <?php if (!empty($custom_slug)): ?>
                    <span style="background: #dcfce7; color: #166534; padding: 4px 12px; border-radius: 4px; font-size: 12px; font-weight: 500;"><?php esc_html_e('Active', 'wpswitchboard'); ?></span>
                <?php else: ?>
                    <span style="background: #fef3c7; color: #92400e; padding: 4px 12px; border-radius: 4px; font-size: 12px; font-weight: 500;"><?php esc_html_e('Not Configured', 'wpswitchboard'); ?></span>
                <?php endif; ?>
            </div>
            <div class="sb-module-card-body">
                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 16px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="setting_custom_slug" class="sb-sp-label"><?php esc_html_e('Custom Login Slug', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Choose a unique, hard-to-guess slug for your login page.', 'wpswitchboard'); ?></p>
                    </div>
                    <div style="display: flex; flex-direction: column; gap: 8px; flex: 1; max-width: 100%;">
                        <div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
                            <input type="text" name="setting_custom_slug" id="setting_custom_slug" value="<?php echo esc_attr($custom_slug); ?>" class="sb-sp-input" style="width: 180px; flex-shrink: 0;" placeholder="my-secret-login">
                            <button type="button" id="test-slug-btn" class="sb-module-button sb-module-button-secondary sb-module-button-sm" style="white-space: nowrap;">
                                <?php esc_html_e('Check & Test', 'wpswitchboard'); ?>
                            </button>
                        </div>
                        <div id="slug-test-result" style="display: none; padding: 8px 12px; border-radius: 6px; font-size: 13px;"></div>
                        <!-- Live URL Preview -->
                        <div id="url-live-preview" style="background: #f8fafc; border: 1px solid #e2e8f0; padding: 12px; border-radius: 6px; margin-top: 4px;">
                            <p style="margin: 0 0 6px; font-size: 12px; color: #64748b; font-weight: 500;"><?php esc_html_e('Your login URL will be:', 'wpswitchboard'); ?></p>
                            <div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
                                <code id="url-preview-text" style="background: #e2e8f0; padding: 6px 10px; border-radius: 4px; font-size: 13px; word-break: break-all; flex: 1;"><?php echo esc_html(home_url((!empty($custom_slug) ? $custom_slug : 'your-slug') . '/')); ?></code>
                                <button type="button" id="copy-url-btn" class="sb-module-button sb-module-button-sm" style="white-space: nowrap;" title="<?php esc_attr_e('Copy URL', 'wpswitchboard'); ?>">
                                    <span class="dashicons dashicons-clipboard" style="font-size: 16px; width: 16px; height: 16px;"></span>
                                </button>
                                <button type="button" id="open-url-btn" class="sb-module-button sb-module-button-sm" style="white-space: nowrap;" title="<?php esc_attr_e('Open in new tab', 'wpswitchboard'); ?>">
                                    <span class="dashicons dashicons-external" style="font-size: 16px; width: 16px; height: 16px;"></span>
                                </button>
                            </div>
                            <?php if (!empty($custom_slug)): ?>
                            <p style="margin: 8px 0 0; font-size: 11px; color: #16a34a;">
                                <span class="dashicons dashicons-yes-alt" style="font-size: 14px; width: 14px; height: 14px; vertical-align: middle;"></span>
                                <?php esc_html_e('This URL is currently active and working.', 'wpswitchboard'); ?>
                            </p>
                            <?php else: ?>
                            <p style="margin: 8px 0 0; font-size: 11px; color: #d97706;">
                                <span class="dashicons dashicons-warning" style="font-size: 14px; width: 14px; height: 14px; vertical-align: middle;"></span>
                                <?php esc_html_e('Enter a slug and save to activate.', 'wpswitchboard'); ?>
                            </p>
                            <?php endif; ?>
                        </div>
                    </div>
                </div>

                <div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px;">
                    <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 16px;">
                        <div class="sb-sp-field-horizontal-left">
                            <label for="setting_block_default_login" class="sb-sp-label"><?php esc_html_e('Block /wp-login.php', 'wpswitchboard'); ?></label>
                            <p class="sb-sp-description"><?php esc_html_e('Return 404 when accessing the default login URL.', 'wpswitchboard'); ?></p>
                        </div>
                        <label class="sb-module-toggle">
                            <input type="hidden" name="setting_block_default_login" value="no">
                            <input type="checkbox" name="setting_block_default_login" id="setting_block_default_login" value="yes" <?php checked($block_default_login, 'yes'); ?>>
                            <span class="sb-module-toggle-slider"></span>
                        </label>
                    </div>

                    <div class="sb-sp-group sb-sp-field-horizontal">
                        <div class="sb-sp-field-horizontal-left">
                            <label for="setting_block_wp_admin" class="sb-sp-label"><?php esc_html_e('Block /wp-admin/', 'wpswitchboard'); ?></label>
                            <p class="sb-sp-description"><?php esc_html_e('Return 404 for wp-admin when not logged in.', 'wpswitchboard'); ?></p>
                        </div>
                        <label class="sb-module-toggle">
                            <input type="hidden" name="setting_block_wp_admin" value="no">
                            <input type="checkbox" name="setting_block_wp_admin" id="setting_block_wp_admin" value="yes" <?php checked($block_wp_admin, 'yes'); ?>>
                            <span class="sb-module-toggle-slider"></span>
                        </label>
                    </div>
                </div>

                <div class="sb-sp-info-box" style="background: #f0fdf4; border-color: #bbf7d0; margin-top: 20px;">
                    <p class="sb-sp-description" style="margin: 0; color: #166534; font-size: 13px;">
                        <strong><?php esc_html_e('Stealth Mode:', 'wpswitchboard'); ?></strong>
                        <?php esc_html_e('Blocked requests receive your theme\'s real 404 page, making it impossible for attackers to know the login exists.', 'wpswitchboard'); ?>
                    </p>
                </div>
            </div>
        </div>

        <!-- Security Features Card -->
        <div class="sb-module-card sb-module-form-section" style="margin-bottom: 24px;">
            <div class="sb-module-card-header">
                <div class="sb-module-card-header-left">
                    <h2 class="sb-module-card-title"><?php esc_html_e('Security Features', 'wpswitchboard'); ?></h2>
                    <p class="sb-module-card-desc"><?php esc_html_e('Additional protection for your login page', 'wpswitchboard'); ?></p>
                </div>
            </div>
            <div class="sb-module-card-body">
                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 20px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="setting_honeypot_enabled" class="sb-sp-label"><?php esc_html_e('Honeypot Field', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Adds an invisible field that catches automated bots.', 'wpswitchboard'); ?></p>
                    </div>
                    <label class="sb-module-toggle">
                        <input type="hidden" name="setting_honeypot_enabled" value="no">
                        <input type="checkbox" name="setting_honeypot_enabled" id="setting_honeypot_enabled" value="yes" <?php checked($honeypot_enabled, 'yes'); ?>>
                        <span class="sb-module-toggle-slider"></span>
                    </label>
                </div>

                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 20px; padding-top: 20px; border-top: 1px solid #e5e7eb;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="setting_user_agent_blocking" class="sb-sp-label"><?php esc_html_e('Block Bot User Agents', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Block curl, wget, python, and other common bot tools.', 'wpswitchboard'); ?></p>
                    </div>
                    <label class="sb-module-toggle">
                        <input type="hidden" name="setting_user_agent_blocking" value="no">
                        <input type="checkbox" name="setting_user_agent_blocking" id="setting_user_agent_blocking" value="yes" <?php checked($user_agent_blocking, 'yes'); ?>>
                        <span class="sb-module-toggle-slider"></span>
                    </label>
                </div>

                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 20px; padding-top: 20px; border-top: 1px solid #e5e7eb;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="setting_referer_check" class="sb-sp-label"><?php esc_html_e('HTTP Referer Validation', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Block login POST requests from external sites or missing referer.', 'wpswitchboard'); ?></p>
                    </div>
                    <label class="sb-module-toggle">
                        <input type="hidden" name="setting_referer_check" value="no">
                        <input type="checkbox" name="setting_referer_check" id="setting_referer_check" value="yes" <?php checked($referer_check, 'yes'); ?>>
                        <span class="sb-module-toggle-slider"></span>
                    </label>
                </div>

                <div class="sb-sp-group sb-sp-field-horizontal" style="padding-top: 20px; border-top: 1px solid #e5e7eb;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="setting_rate_limiting" class="sb-sp-label"><?php esc_html_e('Rate Limiting', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Limit URL discovery attempts per IP address.', 'wpswitchboard'); ?></p>
                    </div>
                    <label class="sb-module-toggle">
                        <input type="hidden" name="setting_rate_limiting" value="no">
                        <input type="checkbox" name="setting_rate_limiting" id="setting_rate_limiting" value="yes" <?php checked($rate_limiting, 'yes'); ?>>
                        <span class="sb-module-toggle-slider"></span>
                    </label>
                </div>

                <div id="rate-limit-settings" style="<?php echo $rate_limiting === 'yes' ? '' : 'display: none;'; ?> margin-top: 16px; margin-left: 24px; padding: 16px; background: #f8fafc; border-radius: 8px; border-left: 3px solid var(--primary-500);">
                    <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 12px;">
                        <div class="sb-sp-field-horizontal-left">
                            <label for="setting_rate_limit_threshold" class="sb-sp-label"><?php esc_html_e('Threshold', 'wpswitchboard'); ?></label>
                            <p class="sb-sp-description"><?php esc_html_e('Blocked attempts before extended block.', 'wpswitchboard'); ?></p>
                        </div>
                        <input type="number" name="setting_rate_limit_threshold" id="setting_rate_limit_threshold" value="<?php echo esc_attr($rate_limit_threshold); ?>" min="1" max="100" class="sb-sp-input" style="width: 80px;">
                    </div>
                    <div class="sb-sp-group sb-sp-field-horizontal">
                        <div class="sb-sp-field-horizontal-left">
                            <label for="setting_rate_limit_window" class="sb-sp-label"><?php esc_html_e('Time Window', 'wpswitchboard'); ?></label>
                            <p class="sb-sp-description"><?php esc_html_e('Rate limit window in seconds.', 'wpswitchboard'); ?></p>
                        </div>
                        <input type="number" name="setting_rate_limit_window" id="setting_rate_limit_window" value="<?php echo esc_attr($rate_limit_window); ?>" min="60" max="86400" class="sb-sp-input" style="width: 80px;">
                    </div>
                </div>
            </div>
        </div>

        <!-- IP Whitelist Card -->
        <div class="sb-module-card sb-module-form-section" style="margin-bottom: 24px; border: 2px solid #f87171;">
            <div class="sb-module-card-header" style="background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%); border-bottom-color: #fecaca;">
                <div class="sb-module-card-header-left">
                    <h2 class="sb-module-card-title" style="color: #991b1b; display: flex; align-items: center; gap: 8px;">
                        <span class="dashicons dashicons-shield-alt" style="font-size: 20px; width: 20px; height: 20px;"></span>
                        <?php esc_html_e('IP Whitelist (Recovery Bypass)', 'wpswitchboard'); ?>
                    </h2>
                    <p class="sb-module-card-desc" style="color: #b91c1c;"><?php esc_html_e('Your safety net — these IPs can always access /wp-login.php directly', 'wpswitchboard'); ?></p>
                </div>
                <?php
                $current_ip = $this->get_client_ip();
                $is_whitelisted = $this->is_ip_whitelisted($current_ip);
                if ($is_whitelisted): ?>
                    <span style="background: #dcfce7; color: #166534; padding: 6px 14px; border-radius: 6px; font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 6px;">
                        <span class="dashicons dashicons-yes-alt" style="font-size: 16px; width: 16px; height: 16px;"></span>
                        <?php esc_html_e('Your IP Protected', 'wpswitchboard'); ?>
                    </span>
                <?php else: ?>
                    <span style="background: #fef3c7; color: #92400e; padding: 6px 14px; border-radius: 6px; font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 6px; animation: pulse 2s infinite;">
                        <span class="dashicons dashicons-warning" style="font-size: 16px; width: 16px; height: 16px;"></span>
                        <?php esc_html_e('Not Protected', 'wpswitchboard'); ?>
                    </span>
                    <style>
                        @keyframes pulse {
                            0%, 100% { opacity: 1; }
                            50% { opacity: 0.7; }
                        }
                    </style>
                <?php endif; ?>
            </div>
            <div class="sb-module-card-body">
                <?php if (!$is_whitelisted): ?>
                <div style="background: #fef3c7; border: 1px solid #fcd34d; border-radius: 8px; padding: 16px; margin-bottom: 20px; display: flex; align-items: center; gap: 12px;">
                    <div style="flex-shrink: 0; width: 40px; height: 40px; background: #f59e0b; border-radius: 50%; display: flex; align-items: center; justify-content: center;">
                        <span class="dashicons dashicons-warning" style="color: white; font-size: 20px; width: 20px; height: 20px;"></span>
                    </div>
                    <div>
                        <strong style="color: #92400e; font-size: 14px;"><?php esc_html_e('Your IP is not whitelisted!', 'wpswitchboard'); ?></strong>
                        <p style="margin: 4px 0 0; color: #78350f; font-size: 13px;">
                            <?php esc_html_e('Click the button below to add your current IP address as a backup login method.', 'wpswitchboard'); ?>
                        </p>
                    </div>
                </div>
                <?php endif; ?>

                <div class="sb-sp-group">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
                        <label for="setting_ip_whitelist" class="sb-sp-label"><?php esc_html_e('Whitelisted IPs', 'wpswitchboard'); ?></label>
                        <button type="button" id="add-my-ip-btn" class="sb-module-button sb-module-button-primary sb-module-button-sm" style="<?php echo $is_whitelisted ? '' : 'animation: pulse 2s infinite;'; ?>">
                            + <?php esc_html_e('Add My Current IP', 'wpswitchboard'); ?>
                        </button>
                    </div>
                    <textarea name="setting_ip_whitelist" id="setting_ip_whitelist" class="sb-sp-textarea" rows="4" placeholder="192.168.1.1&#10;10.0.0.0/8&#10;203.0.113.*"><?php echo esc_textarea($ip_whitelist); ?></textarea>
                    <p class="sb-sp-description" style="margin-top: 8px;"><?php esc_html_e('One IP per line. Supports: exact IPs, CIDR (10.0.0.0/8), wildcards (192.168.1.*).', 'wpswitchboard'); ?></p>
                    <div style="margin-top: 8px; padding: 10px 12px; background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 6px; display: flex; align-items: center; gap: 8px;">
                        <span class="dashicons dashicons-location" style="color: #16a34a; font-size: 16px; width: 16px; height: 16px;"></span>
                        <span style="font-size: 13px; color: #166534;">
                            <?php esc_html_e('Your current IP:', 'wpswitchboard'); ?>
                            <code style="background: white; color: #166534; padding: 2px 8px; border-radius: 4px; font-weight: 600; margin-left: 4px;"><?php echo esc_html($this->get_display_ip()); ?></code>
                        </span>
                    </div>
                </div>

                <div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px;">
                    <div class="sb-sp-group">
                        <label for="setting_path_whitelist" class="sb-sp-label"><?php esc_html_e('Path Whitelist', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description" style="margin-bottom: 8px;"><?php esc_html_e('Allow login from these URL paths (one per line). Useful for custom login forms.', 'wpswitchboard'); ?></p>
                        <textarea name="setting_path_whitelist" id="setting_path_whitelist" class="sb-sp-textarea" rows="3" placeholder="my-custom-login&#10;member-area/login&#10;shop/my-account"><?php echo esc_textarea($path_whitelist); ?></textarea>
                        <p class="sb-sp-description" style="margin-top: 8px;"><?php esc_html_e('Example: "my-login" allows access from yoursite.com/my-login/', 'wpswitchboard'); ?></p>
                    </div>
                </div>

                <!-- Locked Out Help Section -->
                <div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px;">
                    <details style="cursor: pointer;">
                        <summary style="display: flex; align-items: center; gap: 10px; padding: 12px 16px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 14px; font-weight: 600; color: #475569;">
                            <span class="dashicons dashicons-sos" style="color: #dc2626; font-size: 18px; width: 18px; height: 18px;"></span>
                            <?php esc_html_e('Locked Out? Emergency Recovery Instructions', 'wpswitchboard'); ?>
                            <span style="margin-left: auto; font-size: 12px; font-weight: 400; color: #94a3b8;"><?php esc_html_e('Click to expand', 'wpswitchboard'); ?></span>
                        </summary>
                        <div style="margin-top: 12px; padding: 16px; background: #fafafa; border: 1px solid #e5e7eb; border-radius: 8px;">
                            <p style="margin: 0 0 16px; font-size: 13px; color: #64748b;">
                                <?php esc_html_e('If you forgot your custom login URL and your IP is not whitelisted, use one of these methods to regain access:', 'wpswitchboard'); ?>
                            </p>

                            <div style="margin-bottom: 16px;">
                                <h5 style="margin: 0 0 8px; font-size: 13px; font-weight: 600; color: #374151; display: flex; align-items: center; gap: 6px;">
                                    <span style="background: #6366f1; color: white; font-size: 11px; font-weight: 700; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center;">1</span>
                                    <?php esc_html_e('Method 1: FTP/File Manager (Easiest)', 'wpswitchboard'); ?>
                                </h5>
                                <div style="margin-left: 26px; font-size: 12px; color: #6b7280; line-height: 1.6;">
                                    <p style="margin: 0 0 6px;"><?php esc_html_e('Rename or delete the module file to disable it:', 'wpswitchboard'); ?></p>
                                    <code style="display: block; background: #1f2937; color: #10b981; padding: 8px 12px; border-radius: 4px; font-size: 11px; word-break: break-all;">
                                        wp-content/plugins/wpswitchboard-pro/modules/security/hide-my-login.php
                                    </code>
                                    <p style="margin: 6px 0 0;"><?php esc_html_e('Rename to hide-my-login.php.bak — then access /wp-login.php normally.', 'wpswitchboard'); ?></p>
                                </div>
                            </div>

                            <div style="margin-bottom: 16px;">
                                <h5 style="margin: 0 0 8px; font-size: 13px; font-weight: 600; color: #374151; display: flex; align-items: center; gap: 6px;">
                                    <span style="background: #6366f1; color: white; font-size: 11px; font-weight: 700; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center;">2</span>
                                    <?php esc_html_e('Method 2: Database (phpMyAdmin)', 'wpswitchboard'); ?>
                                </h5>
                                <div style="margin-left: 26px; font-size: 12px; color: #6b7280; line-height: 1.6;">
                                    <p style="margin: 0 0 6px;"><?php esc_html_e('Find the option in your database and delete or update it:', 'wpswitchboard'); ?></p>
                                    <code style="display: block; background: #1f2937; color: #10b981; padding: 8px 12px; border-radius: 4px; font-size: 11px;">
                                        DELETE FROM wp_options WHERE option_name = 'switchboard_hide-my-login_settings';
                                    </code>
                                </div>
                            </div>

                            <div>
                                <h5 style="margin: 0 0 8px; font-size: 13px; font-weight: 600; color: #374151; display: flex; align-items: center; gap: 6px;">
                                    <span style="background: #6366f1; color: white; font-size: 11px; font-weight: 700; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center;">3</span>
                                    <?php esc_html_e('Method 3: WP-CLI (Advanced)', 'wpswitchboard'); ?>
                                </h5>
                                <div style="margin-left: 26px; font-size: 12px; color: #6b7280; line-height: 1.6;">
                                    <p style="margin: 0 0 6px;"><?php esc_html_e('If you have SSH access, run this command:', 'wpswitchboard'); ?></p>
                                    <code style="display: block; background: #1f2937; color: #10b981; padding: 8px 12px; border-radius: 4px; font-size: 11px;">
                                        wp option delete switchboard_hide-my-login_settings
                                    </code>
                                </div>
                            </div>

                            <div style="margin-top: 16px; padding: 12px; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 6px;">
                                <p style="margin: 0; font-size: 12px; color: #1d4ed8;">
                                    <strong><?php esc_html_e('Tip:', 'wpswitchboard'); ?></strong>
                                    <?php esc_html_e('After recovery, re-enable the module and make sure to add your IP to the whitelist!', 'wpswitchboard'); ?>
                                </p>
                            </div>
                        </div>
                    </details>
                </div>
            </div>
        </div>

        <!-- Logging & Compatibility Card -->
        <div class="sb-module-card sb-module-form-section" style="margin-bottom: 24px;">
            <div class="sb-module-card-header">
                <div class="sb-module-card-header-left">
                    <h2 class="sb-module-card-title"><?php esc_html_e('Logging & Compatibility', 'wpswitchboard'); ?></h2>
                    <p class="sb-module-card-desc"><?php esc_html_e('Access logging and plugin compatibility options', 'wpswitchboard'); ?></p>
                </div>
            </div>
            <div class="sb-module-card-body">
                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 20px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="setting_log_attempts" class="sb-sp-label"><?php esc_html_e('Enable Access Logging', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Log all login page access attempts.', 'wpswitchboard'); ?></p>
                    </div>
                    <label class="sb-module-toggle">
                        <input type="hidden" name="setting_log_attempts" value="no">
                        <input type="checkbox" name="setting_log_attempts" id="setting_log_attempts" value="yes" <?php checked($log_attempts, 'yes'); ?>>
                        <span class="sb-module-toggle-slider"></span>
                    </label>
                </div>

                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 20px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="setting_log_retention_days" class="sb-sp-label"><?php esc_html_e('Log Retention', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php esc_html_e('Days to keep log entries before auto-cleanup.', 'wpswitchboard'); ?></p>
                    </div>
                    <div style="display: flex; align-items: center; gap: 8px;">
                        <input type="number" name="setting_log_retention_days" id="setting_log_retention_days" value="<?php echo esc_attr($log_retention_days); ?>" min="1" max="365" class="sb-sp-input" style="width: 80px;">
                        <span style="color: #6b7280; font-size: 13px;"><?php esc_html_e('days', 'wpswitchboard'); ?></span>
                    </div>
                </div>

                <div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px;">
                    <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 20px;">
                        <div class="sb-sp-field-horizontal-left">
                            <label for="setting_allow_site_health" class="sb-sp-label"><?php esc_html_e('Allow Site Health', 'wpswitchboard'); ?></label>
                            <p class="sb-sp-description"><?php esc_html_e('Allow WordPress Site Health loopback tests.', 'wpswitchboard'); ?></p>
                        </div>
                        <label class="sb-module-toggle">
                            <input type="hidden" name="setting_allow_site_health" value="no">
                            <input type="checkbox" name="setting_allow_site_health" id="setting_allow_site_health" value="yes" <?php checked($allow_site_health, 'yes'); ?>>
                            <span class="sb-module-toggle-slider"></span>
                        </label>
                    </div>

                    <?php if (class_exists('WooCommerce')): ?>
                    <div class="sb-sp-group sb-sp-field-horizontal">
                        <div class="sb-sp-field-horizontal-left">
                            <label for="setting_woocommerce_compat" class="sb-sp-label"><?php esc_html_e('WooCommerce Compatibility', 'wpswitchboard'); ?></label>
                            <p class="sb-sp-description"><?php esc_html_e('Works with WooCommerce my-account login.', 'wpswitchboard'); ?></p>
                        </div>
                        <label class="sb-module-toggle">
                            <input type="hidden" name="setting_woocommerce_compat" value="no">
                            <input type="checkbox" name="setting_woocommerce_compat" id="setting_woocommerce_compat" value="yes" <?php checked($woocommerce_compat, 'yes'); ?>>
                            <span class="sb-module-toggle-slider"></span>
                        </label>
                    </div>
                    <?php else: ?>
                    <input type="hidden" name="setting_woocommerce_compat" value="<?php echo esc_attr($woocommerce_compat); ?>">
                    <?php endif; ?>
                </div>
            </div>
        </div>

        <script>
        jQuery(document).ready(function($) {
            var homeUrl = '<?php echo esc_js(trailingslashit(home_url())); ?>';
            var savedSlug = '<?php echo esc_js($custom_slug); ?>';

            // Live URL preview as user types
            function updateUrlPreview() {
                var slug = $('#setting_custom_slug').val().trim();
                var displaySlug = slug || 'your-slug';
                // Sanitize for display (basic slug format)
                displaySlug = displaySlug.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
                if (!slug) displaySlug = 'your-slug';
                $('#url-preview-text').text(homeUrl + displaySlug + '/');
            }

            $('#setting_custom_slug').on('input keyup', function() {
                updateUrlPreview();
            });

            // Toggle rate limit settings visibility
            $('#setting_rate_limiting').on('change', function() {
                $('#rate-limit-settings').slideToggle(200);
            });

            // Copy URL to clipboard
            $('#copy-url-btn').on('click', function() {
                var url = $('#url-preview-text').text();
                var slug = $('#setting_custom_slug').val().trim();
                if (!slug) {
                    alert('<?php echo esc_js(__('Please enter a slug first.', 'wpswitchboard')); ?>');
                    return;
                }
                navigator.clipboard.writeText(url).then(function() {
                    var $btn = $('#copy-url-btn');
                    $btn.find('.dashicons').removeClass('dashicons-clipboard').addClass('dashicons-yes');
                    setTimeout(function() {
                        $btn.find('.dashicons').removeClass('dashicons-yes').addClass('dashicons-clipboard');
                    }, 1500);
                });
            });

            // Open URL in new tab
            $('#open-url-btn').on('click', function() {
                var slug = $('#setting_custom_slug').val().trim();
                if (!slug) {
                    alert('<?php echo esc_js(__('Please enter a slug first.', 'wpswitchboard')); ?>');
                    return;
                }
                // Only open if slug is saved (active)
                if (slug !== savedSlug) {
                    if (!confirm('<?php echo esc_js(__('The URL has not been saved yet. The test will use the current saved URL. Continue?', 'wpswitchboard')); ?>')) {
                        return;
                    }
                    if (savedSlug) {
                        window.open(homeUrl + savedSlug + '/', '_blank');
                    } else {
                        alert('<?php echo esc_js(__('Please save the settings first to test the URL.', 'wpswitchboard')); ?>');
                    }
                } else {
                    window.open(homeUrl + slug + '/', '_blank');
                }
            });

            // Test slug availability
            $('#test-slug-btn').on('click', function() {
                var slug = $('#setting_custom_slug').val().trim();
                var $result = $('#slug-test-result');
                var $btn = $(this);

                if (!slug) {
                    $result.html('<?php echo esc_js(__('Please enter a slug to test.', 'wpswitchboard')); ?>')
                        .css({'background': '#fef2f2', 'color': '#dc2626', 'display': 'block'});
                    return;
                }

                $btn.prop('disabled', true).text('<?php echo esc_js(__('Checking...', 'wpswitchboard')); ?>');

                $.post(ajaxurl, {
                    action: 'switchboard_hide_login_test_slug',
                    nonce: '<?php echo wp_create_nonce('switchboard_hide_login_nonce'); ?>',
                    slug: slug
                }, function(response) {
                    if (response.success) {
                        $result.html('<strong><?php echo esc_js(__('Available!', 'wpswitchboard')); ?></strong> ' + response.data.message + ' <a href="#" id="test-open-link" style="margin-left: 8px;"><?php echo esc_js(__('Save & test in new tab →', 'wpswitchboard')); ?></a>')
                            .css({'background': '#f0fdf4', 'color': '#16a34a', 'display': 'block'});

                        // Add click handler for the test link
                        $('#test-open-link').on('click', function(e) {
                            e.preventDefault();
                            if (slug === savedSlug) {
                                window.open(homeUrl + slug + '/', '_blank');
                            } else {
                                alert('<?php echo esc_js(__('Please save settings first, then click the external link icon to test.', 'wpswitchboard')); ?>');
                            }
                        });
                    } else {
                        $result.html('<strong><?php echo esc_js(__('Not Available:', 'wpswitchboard')); ?></strong> ' + response.data.message)
                            .css({'background': '#fef2f2', 'color': '#dc2626', 'display': 'block'});
                    }
                }).fail(function() {
                    $result.html('<?php echo esc_js(__('Error testing slug. Please try again.', 'wpswitchboard')); ?>')
                        .css({'background': '#fef2f2', 'color': '#dc2626', 'display': 'block'});
                }).always(function() {
                    $btn.prop('disabled', false).text('<?php echo esc_js(__('Check & Test', 'wpswitchboard')); ?>');
                });
            });

            // Add current IP to whitelist
            $('#add-my-ip-btn').on('click', function() {
                var $textarea = $('#setting_ip_whitelist');
                var displayIp = '<?php echo esc_js($this->get_display_ip()); ?>';
                // Strip "(localhost)" suffix if present for the actual value
                var currentIp = displayIp.replace(' (localhost)', '');
                var currentValue = $textarea.val().trim();

                // Check if it's a localhost IP
                if (currentIp === '::1' || currentIp === '127.0.0.1') {
                    alert('<?php echo esc_js(__('You are on localhost. In production, your real public IP will be detected.', 'wpswitchboard')); ?>');
                }

                if (currentValue.indexOf(currentIp) !== -1) {
                    alert('<?php echo esc_js(__('Your IP is already in the whitelist.', 'wpswitchboard')); ?>');
                    return;
                }

                if (currentValue) {
                    $textarea.val(currentValue + '\n' + currentIp);
                } else {
                    $textarea.val(currentIp);
                }

                // Visual feedback
                $(this).text('<?php echo esc_js(__('Added!', 'wpswitchboard')); ?>').prop('disabled', true);
                setTimeout(function() {
                    $('#add-my-ip-btn').text('+ <?php echo esc_js(__('Add My Current IP', 'wpswitchboard')); ?>').prop('disabled', false);
                }, 1500);
            });
        });
        </script>
        <?php
    }
}

$switchboard_instance = new SwitchboardHideMyLogin();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);
