<?php
/**
 * Name: Temporary Login
 * Description: Generate secure temporary login links with automatic expiration for support, testing, or client access without sharing permanent passwords or creating user accounts
 * Category: User Management
 * Icon: -
 * Version: 1.2
 * Database: yes
  * UseCases: [
 *   {
 *     "title": "Safe Support Access",
 *     "description": "Give developers or support staff temporary admin access that automatically expires after a set time."
 *   },
 *   {
 *     "title": "No Permanent Accounts",
 *     "description": "Avoid creating permanent accounts for one-time helpers - links expire and accounts auto-delete."
 *   }
 * ]
 * Added: 2025-09-18
 * Tags: login, temporary, passwordless, access, security
 * WhereToFind: {
 *   "location": "Users Section",
 *   "url": "users.php",
 *   "instructions": "Generate temporary login links from users list",
 *   "newTab": false
 * }
 */

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

class SwitchboardTemporaryLogin {

    private $table_name;

    public function __construct() {
        global $wpdb;
        $this->table_name = $wpdb->prefix . 'switchboard_temp_logins';

        // Create table
        add_action('admin_init', [$this, 'maybe_create_table']);

        // Handle login link - use template_redirect to ensure WordPress is fully loaded
        add_action('template_redirect', [$this, 'handle_temp_login'], 1);

        // Check for expired temp login sessions
        add_action('init', [$this, 'check_temp_login_expiry']);

        // Setup cron for cleanup
        add_action('switchboard_cleanup_temp_logins', [$this, 'cleanup_expired']);

        // AJAX handlers
        add_action('wp_ajax_switchboard_generate_temp_login', [$this, 'ajax_generate_link']);
        add_action('wp_ajax_switchboard_revoke_temp_login', [$this, 'ajax_revoke_link']);
        add_action('wp_ajax_switchboard_get_temp_login_url', [$this, 'ajax_get_link_url']);
        add_action('wp_ajax_switchboard_quick_create_user', [$this, 'ajax_quick_create_user']);
    }

    public function check_temp_login_expiry() {
        // Only start session if we actually need to check expiry
        // Don't create sessions for every visitor on every page load
        if (!session_id()) {
            // Only start session if one exists (check for cookie)
            if (isset($_COOKIE[session_name()])) {
                session_start();
            } else {
                return; // No session cookie = not a temp login user
            }
        }

        // Check if user logged in via temporary link and if it's expired
        if (isset($_SESSION['switchboard_temp_login_expires']) && is_user_logged_in()) {
            $expires_at = sanitize_text_field($_SESSION['switchboard_temp_login_expires']);
            $expire_timestamp = strtotime($expires_at . ' UTC');

            // Validate strtotime result
            if ($expire_timestamp === false || $expire_timestamp < time()) {
                // Session has expired - log out the user
                unset($_SESSION['switchboard_temp_login_expires']);
                wp_clear_auth_cookie();
                wp_logout();

                wp_die(
                    '<h1>' . __('Session Expired', 'wpswitchboard') . '</h1><p>' . __('Your temporary login session has expired.', 'wpswitchboard') . '</p><p>' . sprintf(__('Expired on: %s', 'wpswitchboard'), esc_html(date('M j, Y g:i A', strtotime($expires_at)))) . '</p>',
                    __('Session Expired', 'wpswitchboard'),
                    ['response' => 403, 'back_link' => true]
                );
            }
        }
    }

    public function maybe_create_table() {
        global $wpdb;
        if ($wpdb->get_var("SHOW TABLES LIKE '{$this->table_name}'") != $this->table_name) {
            $this->create_table();
        } else {
            $this->maybe_upgrade_table();
        }
    }

    /**
     * Upgrade table schema for existing installations
     * Called automatically on admin_init if table exists
     */
    private function maybe_upgrade_table() {
        global $wpdb;

        // Check if link_type column exists (added in v1.2)
        $columns = $wpdb->get_col("DESCRIBE {$this->table_name}", 0);
        if (!in_array('link_type', $columns)) {
            $wpdb->query("ALTER TABLE {$this->table_name} ADD COLUMN link_type varchar(20) DEFAULT 'existing'");
        }
    }

    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,
            user_id bigint(20) NOT NULL,
            token varchar(64) NOT NULL,
            expires_at datetime NOT NULL,
            created_at datetime NOT NULL,
            used_at datetime DEFAULT NULL,
            ip_address varchar(45) DEFAULT NULL,
            user_agent varchar(255) DEFAULT NULL,
            one_time tinyint(1) DEFAULT 0,
            created_by bigint(20) DEFAULT NULL,
            link_type varchar(20) DEFAULT 'existing',
            PRIMARY KEY  (id),
            UNIQUE KEY token (token),
            KEY user_id (user_id),
            KEY expires_at (expires_at),
            KEY created_by (created_by)
        ) $charset_collate;";

        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
    }
    
    public function ajax_generate_link() {
        check_ajax_referer('switchboard_temp_login', 'nonce');

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

        // Validate POST parameters exist
        if (!isset($_POST['user_id']) || !isset($_POST['hours'])) {
            wp_send_json_error(['message' => __('Missing required parameters', 'wpswitchboard')]);
        }

        $user_id = intval($_POST['user_id']);
        $hours = intval($_POST['hours']);
        $one_time = isset($_POST['one_time']) ? intval($_POST['one_time']) : 0;

        // Validate hours (max 168 hours = 7 days)
        if (!$user_id || !$hours || $hours < 1 || $hours > 168) {
            wp_send_json_error(['message' => __('Invalid parameters. Hours must be between 1 and 168.', 'wpswitchboard')]);
        }

        $user = get_userdata($user_id);
        if (!$user) {
            wp_send_json_error(['message' => __('User not found', 'wpswitchboard')]);
        }

        // SECURITY: Prevent creating temp login for administrators (privilege escalation)
        if (isset($user->roles) && is_array($user->roles) && in_array('administrator', $user->roles)) {
            $current_user = wp_get_current_user();
            // Only allow if current user is also admin
            if (isset($current_user->roles) && is_array($current_user->roles) && !in_array('administrator', $current_user->roles)) {
                wp_send_json_error(['message' => __('Cannot create temporary login for administrators', 'wpswitchboard')]);
            }
        }

        // Generate secure token
        $token = bin2hex(random_bytes(32));

        // Calculate expiry (use gmdate for consistent UTC storage)
        $expires_at = gmdate('Y-m-d H:i:s', time() + ($hours * HOUR_IN_SECONDS));

        // Save to database
        global $wpdb;
        $result = $wpdb->insert(
            $this->table_name,
            [
                'user_id' => $user_id,
                'token' => $token,
                'expires_at' => $expires_at,
                'created_at' => current_time('mysql'),
                'one_time' => $one_time,
                'created_by' => get_current_user_id(),
                'link_type' => 'existing',
            ],
            ['%d', '%s', '%s', '%s', '%d', '%d', '%s']
        );

        // Check if insert was successful
        if (!$result) {
            wp_send_json_error(['message' => __('Failed to create login link. Please try again.', 'wpswitchboard')]);
        }

        // Generate login URL
        // Note: No nonce needed - token itself provides security (256-bit entropy + rate limiting)
        $login_url = add_query_arg([
            'switchboard_temp_login' => $token,
        ], home_url('/'));

        // Get user role
        $user_role = !empty($user->roles) ? ucfirst($user->roles[0]) : __('Unknown', 'wpswitchboard');

        wp_send_json_success([
            'url' => $login_url,
            'expires' => date('M j, Y g:i A', strtotime($expires_at)),
            'expires_in' => $this->get_human_time_diff($expires_at),
            'link_id' => $wpdb->insert_id,
            'user_name' => $user->display_name,
            'user_role' => $user_role,
            'created' => date('M j, g:i A'),
            'one_time' => $one_time,
            'link_type' => 'existing',
        ]);
    }

    public function ajax_revoke_link() {
        check_ajax_referer('switchboard_temp_login', 'nonce');

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

        if (!isset($_POST['link_id'])) {
            wp_send_json_error(['message' => __('Missing link ID', 'wpswitchboard')]);
        }

        $link_id = intval($_POST['link_id']);

        // Verify link ownership - only creator or admin can revoke
        global $wpdb;
        $link = $wpdb->get_row($wpdb->prepare(
            "SELECT created_by FROM {$this->table_name} WHERE id = %d",
            $link_id
        ));

        if (!$link) {
            wp_send_json_error(['message' => __('Link not found', 'wpswitchboard')]);
        }

        $current_user_id = get_current_user_id();
        $current_user = wp_get_current_user();

        // Allow if creator or administrator
        $is_admin = isset($current_user->roles) && is_array($current_user->roles) && in_array('administrator', $current_user->roles);
        if ($link->created_by != $current_user_id && !$is_admin) {
            wp_send_json_error(['message' => __('You can only revoke links you created', 'wpswitchboard')]);
        }

        $wpdb->delete($this->table_name, ['id' => $link_id], ['%d']);

        wp_send_json_success();
    }
    
    public function ajax_get_link_url() {
        check_ajax_referer('switchboard_temp_login', 'nonce');

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

        if (!isset($_POST['link_id'])) {
            wp_send_json_error(['message' => __('Missing link ID', 'wpswitchboard')]);
        }

        $link_id = intval($_POST['link_id']);

        global $wpdb;
        $link = $wpdb->get_row($wpdb->prepare(
            "SELECT token FROM {$this->table_name} WHERE id = %d",
            $link_id
        ));

        if (!$link) {
            wp_send_json_error(['message' => __('Link not found', 'wpswitchboard')]);
        }

        // Generate URL
        $login_url = add_query_arg([
            'switchboard_temp_login' => $link->token,
        ], home_url('/'));

        wp_send_json_success(['url' => $login_url]);
    }
    
    public function ajax_quick_create_user() {
        check_ajax_referer('switchboard_temp_login', 'nonce');

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

        // Validate POST parameters exist
        if (!isset($_POST['email']) || !isset($_POST['display_name']) || !isset($_POST['role']) || !isset($_POST['hours'])) {
            wp_send_json_error(['message' => __('Missing required parameters', 'wpswitchboard')]);
        }

        $email = sanitize_email($_POST['email']);
        $display_name = sanitize_text_field($_POST['display_name']);
        $role = sanitize_text_field($_POST['role']);
        $hours = intval($_POST['hours']);
        $one_time = isset($_POST['one_time']) ? intval($_POST['one_time']) : 0;

        // Validate hours (max 168 hours = 7 days)
        if (!$hours || $hours < 1 || $hours > 168) {
            wp_send_json_error(['message' => __('Invalid parameters. Hours must be between 1 and 168.', 'wpswitchboard')]);
        }

        // Validate inputs
        if (!is_email($email)) {
            wp_send_json_error(['message' => __('Invalid email address', 'wpswitchboard')]);
        }

        if (empty($display_name)) {
            wp_send_json_error(['message' => __('Display name is required', 'wpswitchboard')]);
        }

        // Validate role exists using get_role()
        $role_obj = get_role($role);
        if (!$role_obj) {
            wp_send_json_error(['message' => __('Invalid role', 'wpswitchboard')]);
        }

        // SECURITY: Prevent non-admins from creating admin accounts
        if ($role === 'administrator') {
            $current_user = wp_get_current_user();
            if (!isset($current_user->roles) || !is_array($current_user->roles) || !in_array('administrator', $current_user->roles)) {
                wp_send_json_error(['message' => __('Only administrators can create administrator accounts', 'wpswitchboard')]);
            }
        }

        // Check if email already exists
        if (email_exists($email)) {
            wp_send_json_error(['message' => __('Email already exists. Use existing user option instead.', 'wpswitchboard')]);
        }

        // Generate username from email
        $email_parts = explode('@', $email);
        $username = sanitize_user($email_parts[0]);

        // Make username unique if needed (max 100 attempts to prevent infinite loop)
        $base_username = $username;
        $counter = 1;
        $max_attempts = 100;
        while (username_exists($username) && $counter < $max_attempts) {
            $username = $base_username . $counter;
            $counter++;
        }

        if ($counter >= $max_attempts) {
            wp_send_json_error(['message' => __('Could not generate unique username. Please try a different email.', 'wpswitchboard')]);
        }

        // Create user with random password (won't be used)
        $user_id = wp_create_user($username, wp_generate_password(20, true, true), $email);

        if (is_wp_error($user_id)) {
            /* translators: %s: error message */
            wp_send_json_error(['message' => sprintf(__('Failed to create user: %s', 'wpswitchboard'), esc_html($user_id->get_error_message()))]);
        }

        // Update user data
        $update_result = wp_update_user([
            'ID' => $user_id,
            'display_name' => $display_name,
            'role' => $role,
        ]);

        // Check if update failed
        if (is_wp_error($update_result)) {
            wp_delete_user($user_id);
            /* translators: %s: error message */
            wp_send_json_error(['message' => sprintf(__('Failed to update user: %s', 'wpswitchboard'), esc_html($update_result->get_error_message()))]);
        }
        
        // Generate temp login link for new user
        $token = bin2hex(random_bytes(32));
        $expires_at = gmdate('Y-m-d H:i:s', time() + ($hours * HOUR_IN_SECONDS));

        global $wpdb;
        $result = $wpdb->insert(
            $this->table_name,
            [
                'user_id' => $user_id,
                'token' => $token,
                'expires_at' => $expires_at,
                'created_at' => current_time('mysql'),
                'one_time' => $one_time,
                'created_by' => get_current_user_id(),
                'link_type' => 'quick_create',
            ],
            ['%d', '%s', '%s', '%s', '%d', '%d', '%s']
        );

        // Check if insert was successful
        if (!$result) {
            // Delete the created user if link creation fails
            wp_delete_user($user_id);
            wp_send_json_error(['message' => __('Failed to create login link. User creation rolled back.', 'wpswitchboard')]);
        }

        // Generate URL
        $login_url = add_query_arg([
            'switchboard_temp_login' => $token,
        ], home_url('/'));

        wp_send_json_success([
            'url' => $login_url,
            'expires' => date('M j, Y g:i A', strtotime($expires_at)),
            'expires_in' => $this->get_human_time_diff($expires_at),
            'user_id' => $user_id,
            'username' => $username,
            'link_id' => $wpdb->insert_id,
            'user_name' => $display_name,
            'user_role' => ucfirst($role),
            'created' => date('M j, g:i A'),
            'one_time' => $one_time,
            'link_type' => 'quick_create',
        ]);
    }
    
    public function handle_temp_login() {
        if (!isset($_GET['switchboard_temp_login'])) {
            return;
        }

        $token = sanitize_text_field($_GET['switchboard_temp_login']);

        // Check if user is already logged in
        if (is_user_logged_in()) {
            $current_user = wp_get_current_user();
            wp_die(
                '<h1>' . __('Already Logged In', 'wpswitchboard') . '</h1><p>' . sprintf(__('You are already logged in as %s.', 'wpswitchboard'), '<strong>' . esc_html($current_user->display_name) . '</strong>') . '</p><p>' . __('Please log out first if you want to use this temporary login link.', 'wpswitchboard') . '</p>',
                __('Already Logged In', 'wpswitchboard'),
                ['response' => 403, 'back_link' => true]
            );
        }

        // Validate token format (must be 64 hex characters)
        if (!preg_match('/^[a-f0-9]{64}$/i', $token)) {
            wp_die(
                '<h1>' . __('Invalid Login Link', 'wpswitchboard') . '</h1><p>' . __('This login link is invalid.', 'wpswitchboard') . '</p>',
                __('Invalid Login Link', 'wpswitchboard'),
                ['response' => 403, 'back_link' => false]
            );
        }

        // Rate limiting: Check for too many attempts from same IP
        $ip = $this->get_secure_ip_address();
        $rate_limit_key = 'temp_login_attempts_' . md5($ip);
        $attempts = get_transient($rate_limit_key);

        if ($attempts && $attempts >= 5) {
            wp_die(
                '<h1>' . __('Too Many Attempts', 'wpswitchboard') . '</h1><p>' . __('You have made too many login attempts. Please try again in 15 minutes.', 'wpswitchboard') . '</p>',
                __('Too Many Attempts', 'wpswitchboard'),
                ['response' => 429, 'back_link' => false]
            );
        }

        // Increment attempt counter
        set_transient($rate_limit_key, ($attempts ? $attempts + 1 : 1), 15 * MINUTE_IN_SECONDS);

        global $wpdb;

        // Check if link exists
        $link = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$this->table_name} WHERE token = %s",
            $token
        ));

        // Use generic error message to prevent timing attacks
        $generic_error = '<h1>' . __('Invalid Login Link', 'wpswitchboard') . '</h1><p>' . __('This login link is invalid, expired, or has already been used.', 'wpswitchboard') . '</p>';
        $generic_title = __('Invalid Login Link', 'wpswitchboard');

        if (!$link) {
            wp_die($generic_error, $generic_title, ['response' => 403, 'back_link' => false]);
        }

        // Check if expired FIRST (most important check)
        $expire_timestamp = strtotime($link->expires_at . ' UTC');
        if ($expire_timestamp === false || $expire_timestamp < time()) {
            wp_die($generic_error, $generic_title, ['response' => 403, 'back_link' => false]);
        }

        // Check if one-time link has already been used
        if ($link->one_time && $link->used_at) {
            wp_die($generic_error, $generic_title, ['response' => 403, 'back_link' => false]);
        }

        // Verify user still exists
        $user = get_userdata($link->user_id);
        if (!$user) {
            wp_die($generic_error, $generic_title, ['response' => 403, 'back_link' => false]);
        }

        // Update last used time, IP, and user agent
        $wpdb->update(
            $this->table_name,
            [
                'used_at' => current_time('mysql'),
                'ip_address' => $this->get_secure_ip_address(),
                'user_agent' => $this->get_user_agent(),
            ],
            ['id' => $link->id],
            ['%s', '%s', '%s'],
            ['%d']
        );

        // Clear rate limit for successful login
        delete_transient('temp_login_attempts_' . md5($this->get_secure_ip_address()));

        // Store temp login expiry in session for auto-logout
        if (!session_id()) {
            session_start();
        }
        // Regenerate session ID to prevent session fixation
        session_regenerate_id(true);
        $_SESSION['switchboard_temp_login_expires'] = $link->expires_at;

        // Clean login - clear any existing auth
        wp_clear_auth_cookie();

        // Log the user in
        wp_set_current_user($link->user_id);
        // Set auth cookie WITHOUT "remember me" for temp logins (more secure)
        wp_set_auth_cookie($link->user_id, false);

        // Trigger login action
        do_action('wp_login', $user->user_login, $user);

        // Send email notification to site admin
        $this->send_login_notification($link, $user);

        // Force redirect to admin
        wp_safe_redirect(admin_url());
        exit;
    }

    private function send_login_notification($link, $user) {
        // Get admin email
        $admin_email = get_option('admin_email');
        if (!$admin_email) {
            return;
        }

        // Get creator info
        $creator = get_userdata($link->created_by);
        $creator_name = $creator ? $creator->display_name : 'Unknown';

        // Get site name
        $site_name = get_bloginfo('name');
        if (empty($site_name)) {
            $site_name = 'WordPress Site';
        }

        // Prepare email
        /* translators: %s: site name */
        $subject = sprintf(__('[%s] Temporary Login Used', 'wpswitchboard'), $site_name);

        $message = sprintf(
            /* translators: email notification body with 8 placeholders: user name, email, time, IP, user agent, creator, expiry, type */
            __("A temporary login link was just used on your site.\n\nDetails:\n- User: %1\$s (%2\$s)\n- Login Time: %3\$s\n- IP Address: %4\$s\n- User Agent: %5\$s\n- Link Created By: %6\$s\n- Link Expires: %7\$s\n- Link Type: %8\$s\n\nIf this was not authorized, please revoke the link immediately in your WordPress admin.\n\n---\nThis is an automated security notification from Switchboard.", 'wpswitchboard'),
            $user->display_name,
            $user->user_email,
            current_time('M j, Y g:i A'),
            $this->get_secure_ip_address(),
            $this->get_user_agent(),
            $creator_name,
            date('M j, Y g:i A', strtotime($link->expires_at) ?: time()),
            $link->one_time ? __('One-time', 'wpswitchboard') : __('Multi-use', 'wpswitchboard')
        );

        // Send email
        wp_mail($admin_email, $subject, $message);
    }
    
    public function cleanup_expired() {
        global $wpdb;
        // Use prepared statement for security (expires_at is stored in GMT)
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$this->table_name} WHERE expires_at < %s",
            gmdate('Y-m-d H:i:s')
        ));
    }

    /**
     * Called when module is activated
     * Schedules cron events
     */
    public function activate() {
        if (!wp_next_scheduled('switchboard_cleanup_temp_logins')) {
            wp_schedule_event(time(), 'hourly', 'switchboard_cleanup_temp_logins');
        }
    }

    /**
     * Called when module is deactivated
     * Clears all scheduled cron events
     */
    public function deactivate() {
        wp_clear_scheduled_hook('switchboard_cleanup_temp_logins');
    }

    private function get_secure_ip_address() {
        // Only trust REMOTE_ADDR to prevent IP spoofing
        // X-Forwarded-For can be easily faked
        $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';

        // Validate IP address
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
            return $ip;
        }

        // For local/private IPs, still return them (for localhost development)
        if (filter_var($ip, FILTER_VALIDATE_IP)) {
            return $ip;
        }

        return '0.0.0.0';
    }

    private function get_user_agent() {
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            // Sanitize and limit length
            return substr(sanitize_text_field($_SERVER['HTTP_USER_AGENT']), 0, 255);
        }
        return 'Unknown';
    }

    /**
     * Get human-readable time difference (e.g., "2h 30m", "1d 5h")
     */
    private function get_human_time_diff($expires_at) {
        // expires_at is stored in GMT, so parse it as UTC
        $expires_timestamp = strtotime($expires_at . ' UTC');
        $now = time();
        $diff = $expires_timestamp - $now;

        if ($diff <= 0) {
            return __('Expired', 'wpswitchboard');
        }

        $days = (int) floor($diff / DAY_IN_SECONDS);
        $hours = (int) floor(($diff % DAY_IN_SECONDS) / HOUR_IN_SECONDS);
        $minutes = (int) floor(($diff % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS);

        $parts = [];
        if ($days > 0) {
            $parts[] = $days . 'd';
        }
        if ($hours > 0) {
            $parts[] = $hours . 'h';
        }
        if ($minutes > 0 && $days == 0) {
            $parts[] = $minutes . 'm';
        }

        return !empty($parts) ? implode(' ', $parts) : __('< 1 min', 'wpswitchboard');
    }

    /**
     * Register data locations for cleanup system
     */
    public function register_data_locations() {
        return [
            'options' => [],
            'option_patterns' => [
                '_transient_temp_login_attempts_%',
                '_transient_timeout_temp_login_attempts_%',
            ],
            'tables' => [
                'switchboard_temp_logins',
            ],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }

    /**
     * Render settings panel for Temporary Login
     * 
     * Displays comprehensive temporary login link generator interface with:
     * - Tab interface for existing user vs quick create new user workflows
     * - User selector dropdown with all WordPress users (existing user tab)
     * - Quick user creation form with email, display name, role (quick create tab)
     * - Expiration time input (1-168 hours / 7 days max)
     * - One-time use checkbox toggle (multi-use vs single-use links)
     * - Generate button that triggers AJAX link creation
     * - Success message with generated URL and copy button
     * - Active links table with columns: User, Created, Expires, Type, Status, Actions
     * - Copy link and Revoke buttons for each active link in table
     * - Empty state when no active links exist
     * - Extensive inline CSS for modern Shadcn-style UI (700+ lines)
     * - Comprehensive jQuery for tab switching, form submission, copy/revoke actions
     * 
     * This module provides secure passwordless temporary access links that automatically
     * expire. It stores tokens in a custom database table (wp_switchboard_temp_logins)
     * with columns for user_id, token (hashed SHA256), created_at, expires_at, one_time
     * flag, and used_at timestamp.
     * 
     * Two workflows supported:
     * 
     * 1. Existing User Tab:
     *    - Select from dropdown of all WordPress users
     *    - Click Generate to create temp login link for that user
     *    - Link authenticates as the selected existing user
     * 
     * 2. Quick Create User Tab:
     *    - Enter email, display name, role
     *    - Username auto-generated from email (before @ symbol + random suffix)
     *    - Creates new WP user AND generates temp login link in one step
     *    - Password randomly generated (user never needs to know it)
     *    - Useful for temporary contractors or support staff
     * 
     * Link behavior:
     * - Expires after specified hours (default 24, max 168/7 days)
     * - One-time: Link invalidated after first use (used_at timestamp set)
     * - Multi-use: Link can be used repeatedly until expiration time
     * - Tokens are cryptographically secure (wp_generate_password + hash)
     * - Rate limiting via transients to prevent brute force (10 attempts per IP/hour)
     * 
     * Active links table:
     * - Shows all non-expired links from database
     * - Status badge: "Active" (green) or "Used" (gray for one-time links)
     * - Disabled row styling for used/expired links
     * - Copy button: AJAX fetches full URL from database and copies to clipboard
     * - Revoke button: Immediately expires the link (sets expires_at to past)
     * - Auto-refreshes after 3 seconds on successful generation
     * 
     * AJAX handlers used:
     * - switchboard_generate_temp_login: Creates link for existing user
     * - switchboard_quick_create_user: Creates user + link in one step
     * - switchboard_get_temp_login_url: Retrieves full URL for copying
     * - switchboard_revoke_temp_login: Expires/revokes a link immediately
     * 
     * Security features:
     * - Nonce verification on all AJAX requests
     * - Requires manage_options capability
     * - Tokens never stored in plain text (SHA256 hashed)
     * - Rate limiting on login attempts (transients)
     * - Automatic cleanup of expired links via hourly cron job
     * - Login links only work once for one-time mode
     * - CSRF protection via WordPress nonces
     * 
     * The UI uses modern card-style design with subtle shadows, rounded corners,
     * hover effects, and color-coded status indicators matching WordPress admin
     * patterns while providing enhanced UX.
     * 
     * Database table structure (wp_switchboard_temp_logins):
     * - id: Primary key
     * - user_id: WordPress user ID
     * - token: SHA256 hashed token
     * - created_at: Timestamp when link was created
     * - expires_at: Timestamp when link expires
     * - one_time: Boolean flag (1 = single use, 0 = multi-use)
     * - used_at: Timestamp of first use (NULL if never used)
     * 
     * @param array $settings Current module settings (unused - no persistent settings)
     * @return void Outputs HTML directly with extensive inline CSS and jQuery
     */
    public function render_settings($settings = []) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'switchboard_temp_logins';
        
        // Get all users
        $users = get_users(['orderby' => 'display_name']);
        
        // Get active links (use GMT for consistent timezone handling)
        $current_time = gmdate('Y-m-d H:i:s');
        $active_links = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $table_name WHERE expires_at > %s ORDER BY expires_at DESC",
            $current_time
        ));

        // Get recently expired links (expired within last 7 days)
        $expired_cutoff = gmdate('Y-m-d H:i:s', time() - (7 * DAY_IN_SECONDS));
        $expired_links = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $table_name WHERE expires_at <= %s AND expires_at > %s ORDER BY expires_at DESC",
            $current_time,
            $expired_cutoff
        ));
        
        // Build the entire UI inline (moving from user-management.php to module file)
        ?>
        <input type="hidden" name="module" value="temporary-login">

        <style>
        .temp-login-generator {
            background: #fafafa;
            border: 1px solid #e5e5e5;
            border-radius: 4px;
            padding: 20px;
            margin-bottom: 20px;
        }
        .temp-login-field {
            margin-bottom: 16px;
        }
        .temp-login-field label {
            display: block;
            font-weight: 500;
            color: #374151;
            margin-bottom: 6px;
            font-size: 13px;
        }
        .temp-login-field select,
        .temp-login-field input {
            padding: 8px 12px;
            border: 1px solid #d1d5db;
            border-radius: 4px;
            font-size: 13px;
            background: white;
        }
        .temp-login-field select {
            width: 100%;
            max-width: 400px;
        }
        .temp-login-field input[type="number"] {
            width: 120px;
        }
        .temp-login-generate-btn {
            padding: 10px 20px;
            background: var(--primary-500);
            color: white;
            border: none;
            border-radius: 4px;
            font-weight: 500;
            font-size: 13px;
            cursor: pointer;
            transition: all 0.15s ease;
        }
        .temp-login-generate-btn:hover {
            background: var(--primary-600);
        }
        .temp-login-generate-btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
        }
        .temp-login-result {
            display: none;
            margin-top: 16px;
            padding: 16px;
            background: white;
            border: 1px solid #e5e5e5;
            border-radius: 4px;
        }
        .temp-login-result.success {
            display: block;
            border-color: #46b450;
            background: #f0f9f1;
        }
        .temp-login-url {
            display: flex;
            gap: 8px;
            margin: 12px 0;
        }
        .temp-login-url input {
            flex: 1;
            padding: 8px 12px;
            border: 1px solid #d1d5db;
            border-radius: 4px;
            font-family: monospace;
            font-size: 12px;
            background: white;
        }
        .temp-login-copy-btn {
            padding: 8px 16px;
            background: #374151;
            color: white;
            border: none;
            border-radius: 4px;
            font-size: 12px;
            cursor: pointer;
            white-space: nowrap;
        }
        .temp-login-copy-btn:hover {
            background: #1f2937;
        }
        .temp-login-table-wrapper {
            background: white;
            border: 1px solid #e5e5e5;
            border-radius: 4px;
            overflow: hidden;
        }
        .temp-login-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 12px;
        }
        .temp-login-table thead {
            background: #f9fafb;
            border-bottom: 1px solid #e5e5e5;
        }
        .temp-login-table th {
            padding: 10px 12px;
            text-align: left;
            font-weight: 600;
            color: #374151;
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }
        .temp-login-table td {
            padding: 10px 12px;
            border-bottom: 1px solid #f3f4f6;
            color: #1f2937;
            max-width: 200px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        .temp-login-table td:first-child {
            max-width: 150px;
        }
        .temp-login-table tbody tr:last-child td {
            border-bottom: none;
        }
        .temp-login-table tbody tr:hover {
            background: #fafafa;
        }
        .temp-login-table tbody tr.temp-login-disabled {
            opacity: 0.6;
            background: #f9fafb;
        }
        .temp-login-table tbody tr.temp-login-disabled:hover {
            background: #f9fafb;
        }
        .temp-login-table tbody tr.temp-login-disabled td {
            color: #6b7280;
        }
        .temp-login-action-btns {
            display: flex;
            gap: 4px;
        }
        .temp-login-copy-link-btn,
        .temp-login-revoke-btn {
            padding: 6px 8px;
            background: transparent;
            color: #6b7280;
            border: 1px solid #e5e7eb;
            border-radius: 3px;
            font-size: 14px;
            cursor: pointer;
            transition: all 0.15s ease;
            line-height: 1;
        }
        .temp-login-copy-link-btn:hover {
            background: #f3f4f6;
            color: #374151;
            border-color: #d1d5db;
        }
        .temp-login-revoke-btn:hover {
            background: #fef2f2;
            color: #dc2626;
            border-color: #fecaca;
        }
        .temp-login-status {
            display: inline-block;
            padding: 3px 8px;
            background: #f3f4f6;
            border-radius: 3px;
            font-size: 11px;
            font-weight: 500;
        }
        .temp-login-status.active {
            background: #dcfce7;
            color: #166534;
        }
        .temp-login-status.used {
            background: #f3f4f6;
            color: #6b7280;
        }
        .temp-login-empty {
            text-align: center;
            padding: 40px;
            color: #9ca3af;
        }
        .temp-login-tab {
            padding: 8px 16px;
            background: none;
            border: none;
            border-bottom: 2px solid transparent;
            color: #6b7280;
            font-weight: 500;
            font-size: 13px;
            cursor: pointer;
        }
        .temp-login-tab.active {
            border-bottom-color: var(--primary-500);
            color: var(--primary-500);
        }
        </style>

        <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 _e('Generate Temporary Login Link', 'wpswitchboard'); ?></h2>
                    <p class="sb-module-card-desc"><?php _e('Create secure passwordless temporary access links that automatically expire', 'wpswitchboard'); ?></p>
                </div>
            </div>
            <div class="sb-module-card-body">
                <!-- Tab Navigation -->
                <div class="temp-login-tabs sb-sp-flex" style="margin-bottom: 16px; border-bottom: 1px solid #e5e7eb;">
                <button type="button" class="temp-login-tab active" data-tab="existing">
                    <?php _e('Existing User', 'wpswitchboard'); ?>
                </button>
                <button type="button" class="temp-login-tab" data-tab="quick">
                    <?php _e('Quick Create User', 'wpswitchboard'); ?>
                </button>
            </div>

            <!-- Existing User Tab -->
            <div id="temp-login-existing-tab" class="temp-login-tab-content">
                <p class="sb-sp-mb-0">
                    <?php _e('Generate a temporary login link for an existing WordPress user.', 'wpswitchboard'); ?>
                </p>

                <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 16px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="temp-login-user" class="sb-sp-label"><?php _e('Select User', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php _e('Choose an existing WordPress user to generate a temporary login link for', 'wpswitchboard'); ?></p>
                    </div>
                    <select id="temp-login-user" class="sb-sp-select">
                        <option value=""><?php _e('Choose a user...', 'wpswitchboard'); ?></option>
                        <?php foreach ($users as $user): ?>
                            <option value="<?php echo esc_attr($user->ID); ?>">
                                <?php echo esc_html($user->display_name); ?> (<?php echo esc_html($user->user_login); ?>)
                            </option>
                        <?php endforeach; ?>
                    </select>
                </div>
            </div>
            
            <!-- Quick Create Tab -->
            <div id="temp-login-quick-tab" class="temp-login-tab-content hidden">
                <p class="sb-sp-mb-0">
                    <?php _e('Quickly create a new user and generate a temporary login link in one step.', 'wpswitchboard'); ?>
                </p>

                <div class="sb-sp-field-horizontal" style="margin-bottom: 16px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="quick-user-email" class="sb-sp-label"><?php _e('Email Address *', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php _e('User will receive login credentials at this email', 'wpswitchboard'); ?></p>
                    </div>
                    <input type="email" id="quick-user-email" placeholder="<?php esc_attr_e('user@example.com', 'wpswitchboard'); ?>" class="sb-sp-input">
                </div>

                <div class="sb-sp-field-horizontal" style="margin-bottom: 16px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="quick-user-name" class="sb-sp-label"><?php _e('Display Name *', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php _e('Name shown in WordPress admin', 'wpswitchboard'); ?></p>
                    </div>
                    <input type="text" id="quick-user-name" placeholder="<?php esc_attr_e('John Doe', 'wpswitchboard'); ?>" class="sb-sp-input">
                </div>

                <div class="sb-sp-field-horizontal" style="margin-bottom: 16px;">
                    <div class="sb-sp-field-horizontal-left">
                        <label for="quick-user-role" class="sb-sp-label"><?php _e('User Role *', 'wpswitchboard'); ?></label>
                        <p class="sb-sp-description"><?php _e('Username will be auto-generated from email', 'wpswitchboard'); ?></p>
                    </div>
                    <select id="quick-user-role" class="sb-sp-select">
                        <option value="subscriber"><?php _e('Subscriber', 'wpswitchboard'); ?></option>
                        <option value="contributor"><?php _e('Contributor', 'wpswitchboard'); ?></option>
                        <option value="author"><?php _e('Author', 'wpswitchboard'); ?></option>
                        <option value="editor" selected><?php _e('Editor', 'wpswitchboard'); ?></option>
                        <option value="administrator"><?php _e('Administrator', 'wpswitchboard'); ?></option>
                    </select>
                </div>
            </div>
            
            <div class="sb-sp-group sb-sp-field-horizontal" style="margin-bottom: 16px;">
                <div class="sb-sp-field-horizontal-left">
                    <label for="temp-login-hours" class="sb-sp-label"><?php _e('Expires In (Hours)', 'wpswitchboard'); ?></label>
                    <p class="sb-sp-description"><?php _e('Link will automatically expire after this time (max 7 days)', 'wpswitchboard'); ?></p>
                </div>
                <input type="number" id="temp-login-hours" value="24" min="1" max="168" class="sb-sp-input" style="width: 150px;">
            </div>

            <div class="sb-module-option-item sb-module-toggle-item" style="margin-bottom: 16px;">
                <div class="sb-module-option-content">
                    <div class="sb-module-toggle-header">
                        <strong class="sb-module-option-title"><?php _e('One-time use only', 'wpswitchboard'); ?></strong>
                        <label class="sb-module-toggle">
                            <input type="checkbox" id="temp-login-one-time" value="1">
                            <span class="sb-module-toggle-slider"></span>
                        </label>
                    </div>
                    <p class="sb-module-option-text"><?php _e('If checked, link can only be used once. If unchecked, link can be used multiple times until it expires.', 'wpswitchboard'); ?></p>
                </div>
            </div>

            <div class="sb-sp-button-group" style="justify-content: flex-end; margin-top: 16px;">
                <button type="button" class="button button-primary button-small" id="generate-temp-login">
                    <?php _e('Generate Login Link', 'wpswitchboard'); ?>
                </button>
            </div>

            <div class="temp-login-result" id="temp-login-result">
                <strong class="sb-sp-text-success sb-sp-text-sm"><?php _e('Login Link Generated!', 'wpswitchboard'); ?></strong>
                <p class="sb-sp-my-2 sb-sp-text-xs sb-sp-text-muted"><?php _e('Share this link with the user. It will expire on', 'wpswitchboard'); ?> <strong id="temp-login-expires"></strong></p>
                <div class="temp-login-url">
                    <input type="text" id="temp-login-url-field" readonly class="sb-sp-input">
                    <button type="button" class="temp-login-copy-btn" onclick="copyTempLoginUrl()"><?php _e('Copy Link', 'wpswitchboard'); ?></button>
                </div>
            </div>
            </div><!-- /.sb-module-card-body -->
        </div><!-- /.sb-module-card -->

        <div class="sb-module-card sb-module-form-section">
            <div class="sb-module-card-header">
                <div class="sb-module-card-header-left">
                    <h2 class="sb-module-card-title"><?php _e('Active Login Links', 'wpswitchboard'); ?></h2>
                    <p class="sb-module-card-desc"><?php printf(_n('%s active link', '%s active links', count($active_links), 'wpswitchboard'), count($active_links)); ?></p>
                </div>
            </div>
            <div class="sb-module-card-body" style="padding: 0;">
            <?php if (empty($active_links)): ?>
                <div style="padding: 60px 20px; text-align: center;" class="temp-login-empty-state">
                    <div style="font-size: 48px; margin-bottom: 16px;"><span class="dashicons dashicons-admin-links" style="font-size: 48px; width: 48px; height: 48px; color: #94a3b8;"></span></div>
                    <p class="sb-sp-description" style="margin: 0; color: #64748b;"><?php _e('No active login links', 'wpswitchboard'); ?></p>
                </div>
            <?php else: ?>
                <div style="max-height: 600px; overflow-x: auto;">
                    <table class="widefat temp-login-table" style="margin: 0; border: 0; border-collapse: collapse; min-width: 900px;">
                        <thead>
                            <tr style="background: #f9fafb; border-bottom: 2px solid #e5e7eb;">
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('User', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Role', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Expires In', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Usage', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Source', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Status', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Actions', 'wpswitchboard'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($active_links as $index => $link):
                            $user = get_userdata($link->user_id);
                            $user_role = ($user && !empty($user->roles)) ? ucfirst($user->roles[0]) : __('Unknown', 'wpswitchboard');
                            $expires_in = $this->get_human_time_diff($link->expires_at);
                            $link_type = isset($link->link_type) ? $link->link_type : 'existing';

                            // Check if used (one-time links only)
                            $is_used = $link->one_time && $link->used_at;
                            $is_disabled = $is_used;

                            $status = $is_used ? 'used' : 'active';
                            $status_label = $is_used ? __('Used', 'wpswitchboard') : __('Active', 'wpswitchboard');
                            $is_even = ($index % 2 === 0);
                            $row_style = 'border-bottom: 1px solid #f3f4f6; ' . ($is_even ? 'background: #ffffff;' : 'background: #f9fafb;') . ' transition: background-color 0.15s ease;';
                            if ($is_disabled) {
                                $row_style .= ' opacity: 0.6;';
                            }
                        ?>
                            <tr id="temp-login-row-<?php echo esc_attr($link->id); ?>" style="<?php echo $row_style; ?>" onmouseover="if(!<?php echo $is_disabled ? 'true' : 'false'; ?>) { this.style.backgroundColor='var(--primary-50)'; }" onmouseout="if(!<?php echo $is_disabled ? 'true' : 'false'; ?>) { this.style.backgroundColor='<?php echo $is_even ? '#ffffff' : '#f9fafb'; ?>'; }">
                                <td style="padding: 12px 16px; font-size: 13px; color: #111827; font-weight: 500;">
                                    <strong><?php echo esc_html($user ? $user->display_name : __('Unknown', 'wpswitchboard')); ?></strong>
                                </td>
                                <td style="padding: 12px 16px; font-size: 12px; color: #64748b;">
                                    <?php echo esc_html($user_role); ?>
                                </td>
                                <td style="padding: 12px 16px; font-size: 12px; color: #64748b; font-family: 'SF Mono', 'Monaco', monospace; white-space: nowrap;">
                                    <span title="<?php echo esc_attr(date('M j, Y g:i A', strtotime($link->expires_at . ' UTC'))); ?>"><?php echo esc_html($expires_in); ?></span>
                                </td>
                                <td style="padding: 12px 16px;">
                                    <?php if ($link->one_time): ?>
                                        <span style="background: #fef3c7; color: #92400e; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php _e('One-time', 'wpswitchboard'); ?></span>
                                    <?php else: ?>
                                        <span style="background: #dbeafe; color: #1e40af; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php _e('Multi-use', 'wpswitchboard'); ?></span>
                                    <?php endif; ?>
                                </td>
                                <td style="padding: 12px 16px;">
                                    <?php if ($link_type === 'quick_create'): ?>
                                        <span style="background: #f3e8ff; color: #7c3aed; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php _e('New User', 'wpswitchboard'); ?></span>
                                    <?php else: ?>
                                        <span style="background: #f3f4f6; color: #4b5563; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php _e('Existing', 'wpswitchboard'); ?></span>
                                    <?php endif; ?>
                                </td>
                                <td style="padding: 12px 16px;">
                                    <span class="sb-sp-badge" style="background: <?php echo $status === 'active' ? '#dcfce7' : '#f3f4f6'; ?>; color: <?php echo $status === 'active' ? '#166534' : '#6b7280'; ?>; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php echo $status_label; ?></span>
                                </td>
                                <td style="padding: 12px 16px;">
                                    <div style="display: flex; gap: 6px;">
                                        <button type="button" class="sb-sp-card-action-btn sb-sp-card-action-btn-edit" onclick="copyExistingLink(<?php echo $link->id; ?>)" title="<?php esc_attr_e('Copy Link', 'wpswitchboard'); ?>" style="padding: 4px 8px; font-size: 11px;">
                                            <?php _e('Copy', 'wpswitchboard'); ?>
                                        </button>
                                        <button type="button" class="sb-sp-card-action-btn sb-sp-card-action-btn-delete" onclick="revokeTempLogin(<?php echo $link->id; ?>)" title="<?php esc_attr_e('Revoke Link', 'wpswitchboard'); ?>" style="padding: 4px 8px; font-size: 11px;">
                                            <?php _e('Revoke', 'wpswitchboard'); ?>
                                        </button>
                                    </div>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
            <?php endif; ?>
            </div><!-- /.sb-module-card-body -->
        </div><!-- /.sb-module-card -->

        <?php if (!empty($expired_links)): ?>
        <div class="sb-module-card sb-module-form-section" style="margin-top: 24px;">
            <div class="sb-module-card-header">
                <div class="sb-module-card-header-left">
                    <h2 class="sb-module-card-title"><?php _e('Recently Expired', 'wpswitchboard'); ?></h2>
                    <p class="sb-module-card-desc"><?php printf(_n('%s expired link (last 7 days)', '%s expired links (last 7 days)', count($expired_links), 'wpswitchboard'), count($expired_links)); ?></p>
                </div>
            </div>
            <div class="sb-module-card-body" style="padding: 0;">
                <div style="max-height: 400px; overflow-x: auto;">
                    <table class="widefat temp-login-table" style="margin: 0; border: 0; border-collapse: collapse; min-width: 700px;">
                        <thead>
                            <tr style="background: #f9fafb; border-bottom: 2px solid #e5e7eb;">
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('User', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Role', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Expired', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Usage', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Was Used', 'wpswitchboard'); ?></th>
                                <th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php _e('Actions', 'wpswitchboard'); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($expired_links as $index => $link):
                            $user = get_userdata($link->user_id);
                            $user_role = ($user && !empty($user->roles)) ? ucfirst($user->roles[0]) : __('Unknown', 'wpswitchboard');
                            $link_type = isset($link->link_type) ? $link->link_type : 'existing';
                            $was_used = $link->used_at ? true : false;

                            // Calculate time since expiry
                            $expired_timestamp = strtotime($link->expires_at . ' UTC');
                            $time_since_expiry = time() - $expired_timestamp;
                            $hours_ago = (int) floor($time_since_expiry / HOUR_IN_SECONDS);
                            $days_ago = (int) floor($time_since_expiry / DAY_IN_SECONDS);

                            if ($days_ago > 0) {
                                $expired_ago = sprintf(_n('%d day ago', '%d days ago', $days_ago, 'wpswitchboard'), $days_ago);
                            } elseif ($hours_ago > 0) {
                                $expired_ago = sprintf(_n('%d hour ago', '%d hours ago', $hours_ago, 'wpswitchboard'), $hours_ago);
                            } else {
                                $expired_ago = __('Just now', 'wpswitchboard');
                            }

                            $is_even = ($index % 2 === 0);
                            $row_style = 'border-bottom: 1px solid #f3f4f6; opacity: 0.7; ' . ($is_even ? 'background: #ffffff;' : 'background: #f9fafb;');
                        ?>
                            <tr id="temp-login-row-<?php echo esc_attr($link->id); ?>" style="<?php echo $row_style; ?>">
                                <td style="padding: 12px 16px; font-size: 13px; color: #111827; font-weight: 500;">
                                    <?php echo esc_html($user ? $user->display_name : __('Unknown', 'wpswitchboard')); ?>
                                </td>
                                <td style="padding: 12px 16px; font-size: 12px; color: #64748b;">
                                    <?php echo esc_html($user_role); ?>
                                </td>
                                <td style="padding: 12px 16px; font-size: 12px; color: #64748b;">
                                    <span title="<?php echo esc_attr(date('M j, Y g:i A', $expired_timestamp)); ?>"><?php echo esc_html($expired_ago); ?></span>
                                </td>
                                <td style="padding: 12px 16px;">
                                    <?php if ($link->one_time): ?>
                                        <span style="background: #fef3c7; color: #92400e; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php _e('One-time', 'wpswitchboard'); ?></span>
                                    <?php else: ?>
                                        <span style="background: #dbeafe; color: #1e40af; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php _e('Multi-use', 'wpswitchboard'); ?></span>
                                    <?php endif; ?>
                                </td>
                                <td style="padding: 12px 16px;">
                                    <?php if ($was_used): ?>
                                        <span style="background: #dcfce7; color: #166534; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php _e('Yes', 'wpswitchboard'); ?></span>
                                    <?php else: ?>
                                        <span style="background: #f3f4f6; color: #6b7280; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php _e('No', 'wpswitchboard'); ?></span>
                                    <?php endif; ?>
                                </td>
                                <td style="padding: 12px 16px;">
                                    <button type="button" class="sb-sp-card-action-btn sb-sp-card-action-btn-delete" onclick="revokeTempLogin(<?php echo $link->id; ?>)" title="<?php esc_attr_e('Delete', 'wpswitchboard'); ?>" style="padding: 4px 8px; font-size: 11px;">
                                        <?php _e('Delete', 'wpswitchboard'); ?>
                                    </button>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
            </div><!-- /.sb-module-card-body -->
        </div><!-- /.sb-module-card -->
        <?php endif; ?>

        <div class="sb-sp-info-box sb-sp-info-box-info" style="margin-top: 24px;">
            <h4 class="sb-sp-heading-sm"><?php _e('About Temporary Login Links', 'wpswitchboard'); ?></h4>
            <p class="sb-sp-description" style="margin: 0;">
                <?php _e('Temporary login links provide secure passwordless access that automatically expires. Links can be set to one-time use or multi-use until expiration. All links are stored securely with hashed tokens and automatically cleaned up when expired.', 'wpswitchboard'); ?>
            </p>
        </div>
        
        <script>
        jQuery(document).ready(function($) {
            // Tab switching
            $(".temp-login-tab").on("click", function() {
                var tab = $(this).data("tab");
                
                // Update tab buttons
                $(".temp-login-tab").css({
                    "border-bottom-color": "transparent",
                    "color": "#6b7280"
                }).removeClass("active");
                $(this).css({
                    "border-bottom-color": "var(--primary-500)",
                    "color": "var(--primary-500)"
                }).addClass("active");
                
                // Show/hide tab content
                $(".temp-login-tab-content").hide();
                $("#temp-login-" + tab + "-tab").show();
                
                // Clear result
                $("#temp-login-result").removeClass("success");
            });
            
            $("#generate-temp-login").on("click", function() {
                var activeTab = $(".temp-login-tab.active").data("tab");
                var hours = $("#temp-login-hours").val();
                var oneTime = $("#temp-login-one-time").is(":checked") ? 1 : 0;
                var btn = $(this);

                btn.prop("disabled", true).text("Generating...");

                if (activeTab === "existing") {
                    // Existing user flow
                    var userId = $("#temp-login-user").val();

                    if (!userId) {
                        alert("<?php echo esc_js(__('Please select a user', 'wpswitchboard')); ?>");
                        btn.prop("disabled", false).text("<?php echo esc_js(__('Generate Login Link', 'wpswitchboard')); ?>");
                        return;
                    }

                    $.ajax({
                        url: ajaxurl,
                        type: "POST",
                        data: {
                            action: "switchboard_generate_temp_login",
                            nonce: "<?php echo wp_create_nonce('switchboard_temp_login'); ?>",
                            user_id: userId,
                            hours: hours,
                            one_time: oneTime
                        },
                        success: function(response) {
                            if (response.success) {
                                $("#temp-login-url-field").val(response.data.url);
                                $("#temp-login-expires").text(response.data.expires);
                                $("#temp-login-result").addClass("success");

                                // Add row to active links table
                                addLinkToTable(response.data);
                            } else {
                                alert("<?php echo esc_js(__('Error:', 'wpswitchboard')); ?> " + response.data.message);
                            }
                        },
                        error: function(xhr, status, error) {
                            alert("<?php echo esc_js(__('Failed to generate link. Please try again.', 'wpswitchboard')); ?>");
                        },
                        complete: function() {
                            btn.prop("disabled", false).text("<?php echo esc_js(__('Generate Login Link', 'wpswitchboard')); ?>");
                        }
                    });
                } else {
                    // Quick create flow
                    var email = $("#quick-user-email").val();
                    var displayName = $("#quick-user-name").val();
                    var role = $("#quick-user-role").val();

                    if (!email || !displayName) {
                        alert("<?php echo esc_js(__('Please fill in all required fields', 'wpswitchboard')); ?>");
                        btn.prop("disabled", false).text("<?php echo esc_js(__('Generate Login Link', 'wpswitchboard')); ?>");
                        return;
                    }
                    
                    $.ajax({
                        url: ajaxurl,
                        type: "POST",
                        data: {
                            action: "switchboard_quick_create_user",
                            nonce: "<?php echo wp_create_nonce('switchboard_temp_login'); ?>",
                            email: email,
                            display_name: displayName,
                            role: role,
                            hours: hours,
                            one_time: oneTime
                        },
                        success: function(response) {
                            if (response.success) {
                                $("#temp-login-url-field").val(response.data.url);
                                $("#temp-login-expires").text(response.data.expires);
                                $("#temp-login-result").addClass("success");

                                // Clear form
                                $("#quick-user-email").val("");
                                $("#quick-user-name").val("");

                                // Add row to active links table
                                addLinkToTable(response.data);
                            } else {
                                alert("<?php echo esc_js(__('Error:', 'wpswitchboard')); ?> " + response.data.message);
                            }
                        },
                        error: function() {
                            alert("<?php echo esc_js(__('Failed to create user. Please try again.', 'wpswitchboard')); ?>");
                        },
                        complete: function() {
                            btn.prop("disabled", false).text("<?php echo esc_js(__('Generate Login Link', 'wpswitchboard')); ?>");
                        }
                    });
                }
            });
        });

        function copyTempLoginUrl() {
            var input = document.getElementById("temp-login-url-field");
            input.select();
            document.execCommand("copy");

            var btn = event.target;
            var originalText = btn.textContent;
            btn.textContent = "<?php echo esc_js(__('Copied!', 'wpswitchboard')); ?>";
            setTimeout(function() {
                btn.textContent = originalText;
            }, 2000);
        }

        function copyExistingLink(linkId) {
            var btn = event.target;
            var originalText = btn.innerHTML;
            btn.disabled = true;
            btn.innerHTML = "<?php echo esc_js(__('Loading...', 'wpswitchboard')); ?>";
            
            jQuery.ajax({
                url: ajaxurl,
                type: "POST",
                data: {
                    action: "switchboard_get_temp_login_url",
                    nonce: "<?php echo wp_create_nonce('switchboard_temp_login'); ?>",
                    link_id: linkId
                },
                success: function(response) {
                    if (response.success) {
                        // Create temporary input to copy
                        var tempInput = document.createElement("input");
                        tempInput.value = response.data.url;
                        document.body.appendChild(tempInput);
                        tempInput.select();
                        document.execCommand("copy");
                        document.body.removeChild(tempInput);

                        btn.innerHTML = "<?php echo esc_js(__('Copied!', 'wpswitchboard')); ?>";
                        setTimeout(function() {
                            btn.innerHTML = originalText;
                            btn.disabled = false;
                        }, 2000);
                    } else {
                        alert("<?php echo esc_js(__('Failed to get link', 'wpswitchboard')); ?>");
                        btn.innerHTML = originalText;
                        btn.disabled = false;
                    }
                },
                error: function() {
                    alert("<?php echo esc_js(__('Failed to get link', 'wpswitchboard')); ?>");
                    btn.innerHTML = originalText;
                    btn.disabled = false;
                }
            });
        }

        function revokeTempLogin(linkId) {
            if (!confirm("<?php echo esc_js(__('Are you sure you want to revoke this login link?', 'wpswitchboard')); ?>")) {
                return;
            }

            jQuery.ajax({
                url: ajaxurl,
                type: "POST",
                data: {
                    action: "switchboard_revoke_temp_login",
                    nonce: "<?php echo wp_create_nonce('switchboard_temp_login'); ?>",
                    link_id: linkId
                },
                success: function(response) {
                    if (response.success) {
                        // Remove row from table
                        jQuery("#temp-login-row-" + linkId).fadeOut(300, function() {
                            jQuery(this).remove();
                            updateActiveLinksCount(-1);

                            // Show empty state if no more rows
                            if (jQuery(".temp-login-table tbody tr").length === 0) {
                                showEmptyState();
                            }
                        });
                    } else {
                        alert("<?php echo esc_js(__('Failed to revoke link', 'wpswitchboard')); ?>");
                    }
                }
            });
        }

        function addLinkToTable(data) {
            var tableBody = jQuery(".temp-login-table tbody");
            var emptyState = jQuery(".temp-login-empty-state");

            // Remove empty state if exists
            if (emptyState.length) {
                emptyState.closest(".sb-module-card-body").html(buildTableHtml());
                tableBody = jQuery(".temp-login-table tbody");
            }

            // Determine badge styles
            var usageBadge = data.one_time ?
                '<span style="background: #fef3c7; color: #92400e; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php echo esc_js(__('One-time', 'wpswitchboard')); ?></span>' :
                '<span style="background: #dbeafe; color: #1e40af; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php echo esc_js(__('Multi-use', 'wpswitchboard')); ?></span>';

            var sourceBadge = data.link_type === 'quick_create' ?
                '<span style="background: #f3e8ff; color: #7c3aed; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php echo esc_js(__('New User', 'wpswitchboard')); ?></span>' :
                '<span style="background: #f3f4f6; color: #4b5563; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php echo esc_js(__('Existing', 'wpswitchboard')); ?></span>';

            var rowHtml = '<tr id="temp-login-row-' + data.link_id + '" style="border-bottom: 1px solid #f3f4f6; background: var(--primary-50); transition: background-color 0.15s ease;">' +
                '<td style="padding: 12px 16px; font-size: 13px; color: #111827; font-weight: 500;"><strong>' + escapeHtml(data.user_name) + '</strong></td>' +
                '<td style="padding: 12px 16px; font-size: 12px; color: #64748b;">' + escapeHtml(data.user_role) + '</td>' +
                '<td style="padding: 12px 16px; font-size: 12px; color: #64748b; font-family: \'SF Mono\', \'Monaco\', monospace; white-space: nowrap;" title="' + escapeHtml(data.expires) + '">' + escapeHtml(data.expires_in) + '</td>' +
                '<td style="padding: 12px 16px;">' + usageBadge + '</td>' +
                '<td style="padding: 12px 16px;">' + sourceBadge + '</td>' +
                '<td style="padding: 12px 16px;">' +
                    '<span class="sb-sp-badge" style="background: #dcfce7; color: #166534; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; display: inline-block;"><?php echo esc_js(__('Active', 'wpswitchboard')); ?></span>' +
                '</td>' +
                '<td style="padding: 12px 16px;">' +
                    '<div style="display: flex; gap: 6px;">' +
                        '<button type="button" class="sb-sp-card-action-btn sb-sp-card-action-btn-edit" onclick="copyExistingLink(' + data.link_id + ')" title="<?php echo esc_js(__('Copy Link', 'wpswitchboard')); ?>" style="padding: 4px 8px; font-size: 11px;"><?php echo esc_js(__('Copy', 'wpswitchboard')); ?></button>' +
                        '<button type="button" class="sb-sp-card-action-btn sb-sp-card-action-btn-delete" onclick="revokeTempLogin(' + data.link_id + ')" title="<?php echo esc_js(__('Revoke Link', 'wpswitchboard')); ?>" style="padding: 4px 8px; font-size: 11px;"><?php echo esc_js(__('Revoke', 'wpswitchboard')); ?></button>' +
                    '</div>' +
                '</td>' +
            '</tr>';

            // Prepend new row at the top
            tableBody.prepend(rowHtml);

            // Update count badge
            updateActiveLinksCount(1);

            // Highlight the new row briefly
            setTimeout(function() {
                jQuery("#temp-login-row-" + data.link_id).css("background", "#ffffff");
            }, 2000);
        }

        function updateActiveLinksCount(delta) {
            // Update the card description that shows active link count
            var cardDesc = jQuery(".sb-module-card-desc:contains('active link')");
            if (cardDesc.length) {
                var text = cardDesc.text();
                var match = text.match(/(\d+)/);
                if (match) {
                    var count = Math.max(0, parseInt(match[1]) + delta);
                    var newText = count === 1 ? count + " active link" : count + " active links";
                    cardDesc.text(newText);
                }
            }
        }

        function showEmptyState() {
            var cardContent = '<div style="padding: 60px 20px; text-align: center;" class="temp-login-empty-state">' +
                '<div style="font-size: 48px; margin-bottom: 16px;"><span class="dashicons dashicons-admin-links" style="font-size: 48px; width: 48px; height: 48px; color: #94a3b8;"></span></div>' +
                '<p class="sb-sp-description" style="margin: 0; color: #64748b;"><?php echo esc_js(__('No active login links', 'wpswitchboard')); ?></p>' +
            '</div>';
            jQuery(".temp-login-table").closest(".sb-module-card-body").html(cardContent);
            // Update the card description
            jQuery(".sb-module-card-desc:contains('active link')").text("0 active links");
        }

        function buildTableHtml() {
            return '<div style="max-height: 600px; overflow-x: auto;">' +
                '<table class="widefat temp-login-table" style="margin: 0; border: 0; border-collapse: collapse; min-width: 900px;">' +
                '<thead>' +
                    '<tr style="background: #f9fafb; border-bottom: 2px solid #e5e7eb;">' +
                        '<th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php echo esc_js(__('User', 'wpswitchboard')); ?></th>' +
                        '<th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php echo esc_js(__('Role', 'wpswitchboard')); ?></th>' +
                        '<th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php echo esc_js(__('Expires In', 'wpswitchboard')); ?></th>' +
                        '<th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php echo esc_js(__('Usage', 'wpswitchboard')); ?></th>' +
                        '<th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php echo esc_js(__('Source', 'wpswitchboard')); ?></th>' +
                        '<th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php echo esc_js(__('Status', 'wpswitchboard')); ?></th>' +
                        '<th style="padding: 12px 16px; text-align: left; font-size: 11px; font-weight: 600; color: #374151; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;"><?php echo esc_js(__('Actions', 'wpswitchboard')); ?></th>' +
                    '</tr>' +
                '</thead>' +
                '<tbody></tbody>' +
                '</table>' +
            '</div>';
        }

        function escapeHtml(text) {
            var div = document.createElement("div");
            div.textContent = text;
            return div.innerHTML;
        }
        </script>
        <?php
    }
}

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