<?php
/**
 * Name: Login As User
 * Description: Switch to another user account instantly for testing, troubleshooting, or support without needing their password while maintaining audit trail of role switching
 * Category: User Management
 * Icon: -
  * UseCases: [
 *   {
 *     "title": "Test User Permissions",
 *     "description": "Switch to any user account to test their permissions and see exactly what they can access without asking for passwords."
 *   },
 *   {
 *     "title": "Faster Support",
 *     "description": "Troubleshoot user-reported issues by logging in as them to see the exact problem they\'re experiencing."
 *   }
 * ]
 * Added: 2025-09-05
 * Tags: login, switch-user, testing, admin, impersonate
 * WhereToFind: {
 *   "location": "Users List (Row Actions)",
 *   "url": "users.php",
 *   "instructions": "Hover over user - click 'Login As' to switch accounts",
 *   "newTab": false
 * }
 */

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

class SwitchboardLoginAsUser {
    
    public function __construct() {
        add_filter('user_row_actions', [$this, 'add_login_as_link'], 10, 2);
        add_action('admin_init', [$this, 'handle_login_as']);
        add_action('admin_bar_menu', [$this, 'add_switch_back_admin_bar_node'], 999);

        // Register frontend UI early enough for wp_head/wp_footer to fire
        if (!is_admin()) {
            add_action('template_redirect', [$this, 'maybe_add_frontend_switch_back_ui']);
        }
    }
    
    public function add_login_as_link($actions, $user) {
        if (!current_user_can('manage_options') || get_current_user_id() === $user->ID) {
            return $actions;
        }

        $url = wp_nonce_url(
            add_query_arg(['action' => 'login_as_user', 'user_id' => $user->ID], admin_url()),
            'login_as_user_' . $user->ID
        );

        $actions['login_as'] = '<a href="' . esc_url($url) . '">' . esc_html__('Login As', 'wpswitchboard') . '</a>';

        return $actions;
    }
    
    public function handle_login_as() {
        if (!isset($_GET['action']) || $_GET['action'] !== 'login_as_user') {
            return;
        }
        
        if (!isset($_GET['user_id']) || !isset($_GET['_wpnonce'])) {
            return;
        }
        
        $user_id = intval($_GET['user_id']);
        
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'login_as_user_' . $user_id)) {
            wp_die(esc_html__('Security check failed', 'wpswitchboard'));
        }

        if (!current_user_can('manage_options')) {
            wp_die(esc_html__('Unauthorized', 'wpswitchboard'));
        }

        $target_user = get_userdata($user_id);
        if (!$target_user) {
            wp_die(esc_html__('User not found', 'wpswitchboard'));
        }

        // Prevent admin-to-admin impersonation
        if (in_array('administrator', $target_user->roles, true)) {
            wp_die(esc_html__('Cannot impersonate other administrators.', 'wpswitchboard'));
        }

        // Store original admin ID as user meta on the target user, keyed by admin ID
        // to prevent collision when multiple admins impersonate the same user
        $original_user = get_current_user_id();
        update_user_meta($user_id, 'switchboard_original_admin_' . $original_user, $original_user);
        update_user_meta($user_id, 'switchboard_impersonated_by', $original_user);
        update_user_meta($user_id, 'switchboard_impersonation_time', time());
        
        // Log the action
        $this->log_login_as($original_user, $user_id);
        
        // Switch user - clear current session first
        wp_clear_auth_cookie();
        wp_set_current_user($user_id);
        wp_set_auth_cookie($user_id);
        
        wp_safe_redirect(home_url());
        exit;
    }

    public function add_switch_back_admin_bar_node($wp_admin_bar) {
        $original_user = get_user_meta(get_current_user_id(), 'switchboard_impersonated_by', true);

        if (!$original_user) {
            return;
        }

        $wp_admin_bar->add_node([
            'id' => 'switch_back',
            'title' => __('← Switch Back', 'wpswitchboard'),
            'href' => wp_nonce_url(
                add_query_arg(['action' => 'switch_back'], admin_url()),
                'switch_back'
            ),
            'meta' => ['class' => 'switchboard-switch-back']
        ]);
    }

    public function maybe_add_frontend_switch_back_ui() {
        $original_user = get_user_meta(get_current_user_id(), 'switchboard_impersonated_by', true);

        if (!$original_user) {
            return;
        }

        $switch_back_url = wp_nonce_url(
            add_query_arg(['action' => 'switch_back'], admin_url()),
            'switch_back'
        );

        // Admin bar highlight CSS
        add_action('wp_head', function() {
            echo '<style>#wp-admin-bar-switch_back { background: #d63638 !important; }</style>';
        });

        // Top bar + floating button
        add_action('wp_footer', function() use ($switch_back_url, $original_user) {
            $original_user_data = get_userdata($original_user);
            $original_name = $original_user_data ? $original_user_data->display_name : 'Admin';

            $current_user = wp_get_current_user();
            $current_user_name = $current_user->display_name;
            $current_user_role = !empty($current_user->roles) ? ucfirst($current_user->roles[0]) : 'User';

            $is_admin_bar_showing = is_admin_bar_showing();
            $top_position = $is_admin_bar_showing ? '32px' : '0';
            ?>
            <div id="switchboard-login-as-bar" style="position: fixed; top: <?php echo esc_attr($top_position); ?>; left: 0; right: 0; background: #d63638; color: #fff; text-align: center; padding: 10px 50px; font-weight: 600; z-index: 99999; font-size: 13px; letter-spacing: 0.5px; box-shadow: 0 2px 8px rgba(0,0,0,0.3);">
                <span style="opacity: 0.9;"><?php esc_html_e('You are logged in as another user', 'wpswitchboard'); ?></span>
                <a href="<?php echo esc_url($switch_back_url); ?>" style="position: absolute; right: 12px; top: 50%; transform: translateY(-50%); background: rgba(255,255,255,0.25); color: #fff; text-decoration: none; padding: 6px 16px; border-radius: 4px; font-size: 12px; font-weight: 600; transition: all 0.2s; border: 1px solid rgba(255,255,255,0.3);" onmouseover="this.style.background='rgba(255,255,255,0.35)'" onmouseout="this.style.background='rgba(255,255,255,0.25)'">
                    <?php
                    /* translators: %s: original user's display name */
                    echo sprintf(esc_html__('← Switch Back to %s', 'wpswitchboard'), esc_html($original_name));
                    ?>
                </a>
            </div>

            <div id="switchboard-floating-switch-back" style="position: fixed; right: 20px; bottom: 20px; z-index: 999999;">
                <a href="<?php echo esc_url($switch_back_url); ?>"
                   style="background: #d63638; color: #fff; text-decoration: none; padding: 10px 16px; border-radius: 10px; font-size: 13px; font-weight: 600; box-shadow: 0 4px 12px rgba(214, 54, 56, 0.4); transition: all 0.3s; display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 160px; border: 2px solid rgba(255,255,255,0.3);"
                   onmouseover="this.style.background='#b52629'; this.style.boxShadow='0 6px 16px rgba(214, 54, 56, 0.6)'; this.style.transform='scale(1.02)';"
                   onmouseout="this.style.background='#d63638'; this.style.boxShadow='0 4px 12px rgba(214, 54, 56, 0.4)'; this.style.transform='scale(1)';">

                    <div style="font-size: 14px; font-weight: 700; line-height: 1.2;">
                        <?php echo esc_html($current_user_name); ?> <span style="opacity: 0.75; font-weight: 500; font-size: 12px;">(<?php echo esc_html($current_user_role); ?>)</span>
                    </div>

                    <div style="display: flex; align-items: center; gap: 6px; font-size: 12px; opacity: 0.9;">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
                            <path d="M19 12H5M12 19l-7-7 7-7"/>
                        </svg>
                        <span><?php esc_html_e('Switch Back', 'wpswitchboard'); ?></span>
                    </div>
                </a>
            </div>

            <style>
                body {
                    padding-top: <?php echo $is_admin_bar_showing ? '74px' : '42px'; ?> !important;
                }

                @media screen and (max-width: 782px) {
                    #switchboard-login-as-bar {
                        top: <?php echo $is_admin_bar_showing ? '46px' : '0'; ?> !important;
                        padding: 8px 40px 8px 10px;
                        font-size: 11px;
                    }
                    #switchboard-login-as-bar a {
                        position: static;
                        transform: none;
                        display: block;
                        margin-top: 6px;
                        padding: 5px 12px;
                        font-size: 11px;
                    }

                    #switchboard-floating-switch-back {
                        right: 10px;
                        bottom: 10px;
                    }
                    #switchboard-floating-switch-back a {
                        padding: 8px 12px !important;
                        min-width: 140px !important;
                        font-size: 11px !important;
                    }
                    #switchboard-floating-switch-back a > div:first-child {
                        font-size: 12px !important;
                    }
                    #switchboard-floating-switch-back a > div:last-child {
                        font-size: 11px !important;
                    }
                }

                #switchboard-floating-switch-back a:active {
                    transform: scale(0.95) !important;
                }
            </style>
            <?php
        }, 999);
    }
    
    private function log_login_as($from_user, $to_user) {
        $log = get_option('switchboard_login_as_log', []);
        $log[] = [
            'from' => $from_user,
            'to' => $to_user,
            'time' => current_time('mysql'),
            'ip' => filter_var(wp_unslash($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'), FILTER_VALIDATE_IP) ?: '0.0.0.0'
        ];
        
        // Keep only last 100 logs
        $log = array_slice($log, -100);
        update_option('switchboard_login_as_log', $log, false);
    }

    /**
     * Register data locations for cleanup system
     */
    public function register_data_locations() {
        return [
            'options' => [
                'switchboard_login_as_log',
            ],
            'option_patterns' => [],
            'tables' => [],
            'post_meta' => [],
            'user_meta' => [
                'switchboard_impersonated_by',
                'switchboard_impersonation_time',
            ],
            'user_meta_patterns' => [
                'switchboard_original_admin_%',
            ],
        ];
    }
}

// Handle switch back immediately — this file loads during 'init' priority 10,
// so wrapping in add_action('init', ...) would register too late and never fire.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified below
if (isset($_GET['action']) && $_GET['action'] === 'switch_back' && isset($_GET['_wpnonce'])) {
    if (wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'switch_back')) {
        $current_user_id = get_current_user_id();
        $original_user = get_user_meta($current_user_id, 'switchboard_impersonated_by', true);

        if ($original_user && get_userdata($original_user)) {
            // Check impersonation hasn't expired (24 hour safety limit)
            $impersonation_time = get_user_meta($current_user_id, 'switchboard_impersonation_time', true);
            if ($impersonation_time && (time() - intval($impersonation_time)) > DAY_IN_SECONDS) {
                // Stale impersonation — clean up and deny
                delete_user_meta($current_user_id, 'switchboard_impersonated_by');
                delete_user_meta($current_user_id, 'switchboard_original_admin_' . $original_user);
                delete_user_meta($current_user_id, 'switchboard_impersonation_time');
                wp_safe_redirect(home_url());
                exit;
            }
            // Clean up user meta
            delete_user_meta($current_user_id, 'switchboard_impersonated_by');
            delete_user_meta($current_user_id, 'switchboard_original_admin_' . $original_user);
            delete_user_meta($current_user_id, 'switchboard_impersonation_time');

            wp_clear_auth_cookie();
            wp_set_current_user($original_user);
            wp_set_auth_cookie($original_user);
            wp_safe_redirect(admin_url('users.php'));
            exit;
        }
    }
}

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