<?php
/**
 * VMPFence 2FA Enforcement Class
 * Enforces 2FA requirements based on role settings
 * Phase 3: Login Security Settings Implementation
 *
 * @package VMPFence
 * @since 2.1.0
 */

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

class VMPFence_2FA_Enforcement {

    /**
     * Initialize enforcement hooks
     */
    public static function init() {
        // Hook into WordPress authentication flow (priority 60 runs after login 2FA handler at 50)
        add_filter('authenticate', array(__CLASS__, 'check_2fa_requirement'), 60, 3);
        
        // Hook into login form to show grace period notice
        add_action('login_message', array(__CLASS__, 'show_grace_period_notice'));
        
        // Redirect after login if 2FA setup is required (priority 11 to run AFTER login tracking at 10)
        add_action('wp_login', array(__CLASS__, 'check_force_2fa_setup_redirect'), 11, 2);
        
        // Block access to admin pages if 2FA setup is required
        add_action('admin_init', array(__CLASS__, 'enforce_2fa_setup_lockdown'));
        
        // Show persistent admin notice for forced setup
        add_action('admin_notices', array(__CLASS__, 'show_forced_setup_notice'));
        
        // Block REST API access if 2FA setup is forced
        add_filter('rest_authentication_errors', array(__CLASS__, 'enforce_2fa_for_rest_api'), 99);
        
        // Track new user registration for grace period
        add_action('user_register', array(__CLASS__, 'handle_user_registration'), 10, 1);
        
        // Track role changes for grace period
        add_action('set_user_role', array(__CLASS__, 'handle_role_change'), 10, 3);
        add_action('add_user_role', array(__CLASS__, 'handle_role_change'), 10, 2);
        
        // Cleanup on logout
        add_action('wp_logout', array(__CLASS__, 'handle_logout'), 10, 1);
        
        // WooCommerce integration - block frontend authenticated pages during forced setup
        add_action('template_redirect', array(__CLASS__, 'enforce_woocommerce_lockdown'), 10);
    }

    /**
     * Check if user is required to use 2FA based on their role
     *
     * @param WP_User|WP_Error $user     User object or WP_Error
     * @param string           $username Username
     * @param string           $password Password
     * @return WP_User|WP_Error
     */
    public static function check_2fa_requirement($user, $username, $password) {
        // Skip if already an error or not a valid user
        if (is_wp_error($user) || !$user instanceof WP_User) {
            return $user;
        }

        // PHASE 7: Check IP allowlist FIRST - bypass all 2FA enforcement
        $user_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
        if (!empty($user_ip) && class_exists('VMPFence_IP_Allowlist') && VMPFence_IP_Allowlist::is_ip_allowlisted($user_ip)) {
            return $user; // Skip all 2FA enforcement for allowlisted IPs
        }

        // BUG FIX: Skip XML-RPC requests - they are handled by VMPFence_XMLRPC_Security class
        if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) {
            return $user;
        }

        // Get user's roles
        $user_roles = $user->roles;
        
        if (empty($user_roles)) {
            return $user; // No roles, allow login
        }

        // Check each role for 2FA requirement
        $is_required = false;
        $is_optional = false;
        
        foreach ($user_roles as $role) {
            $role_setting = VMPFence_Config::get('vmpfence_2fa_role_' . $role, 'disabled');
            
            if ($role_setting === 'required') {
                $is_required = true;
                break; // If any role requires 2FA, enforce it
            } elseif ($role_setting === 'optional') {
                $is_optional = true;
            }
        }

        // If 2FA is disabled for all user's roles, allow login
        if (!$is_required && !$is_optional) {
            return $user;
        }

        // Check if user has 2FA enabled
        $has_2fa = get_user_meta($user->ID, 'vmpfence_2fa_enabled', true);

        // If 2FA is optional and user hasn't enabled it, allow login
        if ($is_optional && !$is_required && !$has_2fa) {
            return $user;
        }

        // If 2FA is required but not enabled, check grace period
        if ($is_required && !$has_2fa) {
            $within_grace = self::is_within_grace_period($user->ID);
            
            if ($within_grace) {
                // Allow login but show reminder
                set_transient('vmpfence_2fa_grace_reminder_' . $user->ID, true, 60);
                return $user;
            } else {
                // Grace period expired - allow login but force 2FA setup
                // Set flag to lock user into setup page until completion
                update_user_meta($user->ID, 'vmpfence_force_2fa_setup', true);
                set_transient('vmpfence_2fa_setup_required_' . $user->ID, true, 3600);
                return $user;
            }
        }

        // User has 2FA enabled or is within grace period
        return $user;
    }

    /**
     * Check if user is within grace period for 2FA setup
     *
     * @param int $user_id User ID
     * @return bool
     */
    private static function is_within_grace_period($user_id) {
        $grace_period_days = VMPFence_Config::get('vmpfence_2fa_grace_period', 10);
        
        // If grace period is 0, no grace period
        if ($grace_period_days === 0) {
            return false;
        }

        // Get user data
        $user = get_userdata($user_id);
        if (!$user) {
            return false;
        }

        // Check for role-specific grace period start time (when role changed to required)
        $grace_start_timestamp = null;
        foreach ($user->roles as $role) {
            $role_grace_start = get_user_meta($user_id, 'vmpfence_2fa_grace_start_' . $role, true);
            if ($role_grace_start) {
                // Use the OLDEST grace start time if user has multiple required roles
                // This prevents grace period bypass when new roles are added
                if ($grace_start_timestamp === null || $role_grace_start < $grace_start_timestamp) {
                    $grace_start_timestamp = $role_grace_start;
                }
            }
        }

        // If no role-specific grace start, use registration date
        if ($grace_start_timestamp === null) {
            $grace_start_timestamp = strtotime(get_date_from_gmt($user->user_registered));
        }

        $grace_end_timestamp = $grace_start_timestamp + ($grace_period_days * DAY_IN_SECONDS);
        
        // Check if current time is within grace period (use current_time for WP timezone)
        $current_timestamp = current_time('timestamp');
        return $current_timestamp <= $grace_end_timestamp;
    }

    /**
     * Redirect user to 2FA setup page after login if required
     *
     * @param string $user_login Username
     * @param WP_User $user User object
     */
    public static function check_force_2fa_setup_redirect($user_login, $user) {
        $force_setup = get_user_meta($user->ID, 'vmpfence_force_2fa_setup', true);
        
        if ($force_setup) {
            // Set transient to prevent double redirect from admin_init hook
            set_transient('vmpfence_redirected_' . $user->ID, true, 60);
            
            wp_safe_redirect(admin_url('admin.php?page=vmpfence-login-security&force_setup=1'));
            exit;
        }
    }

    /**
     * Block access to all admin pages except 2FA setup if setup is required
     */
    public static function enforce_2fa_setup_lockdown() {
        // Quick exit for heartbeat to optimize performance
        if (defined('DOING_AJAX') && DOING_AJAX && 
            isset($_POST['action']) && $_POST['action'] === 'heartbeat') {
            return;
        }
        
        $user_id = get_current_user_id();
        if (!$user_id) {
            return;
        }
        
        // Check if user can access admin (prevent lockout of users without capability)
        if (!current_user_can('edit_posts')) {
            return;
        }
        
        // Allow user switching plugin to work (for admins testing)
        if (function_exists('current_user_switched') && current_user_switched()) {
            return;
        }
        
        // If we just redirected from login hook, skip to prevent double redirect
        if (get_transient('vmpfence_redirected_' . $user_id)) {
            delete_transient('vmpfence_redirected_' . $user_id);
            return;
        }
        
        $force_setup = get_user_meta($user_id, 'vmpfence_force_2fa_setup', true);
        if (!$force_setup) {
            return;
        }
        
        // Check if already on setup page (prevent unnecessary redirects)
        $current_page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : '';
        $force_setup_param = isset($_GET['force_setup']) ? sanitize_text_field($_GET['force_setup']) : '';
        
        if ($current_page === 'vmpfence-login-security' && $force_setup_param === '1') {
            return; // Already on setup page
        }
        
        // Allow access to user's own profile page (they may need to update email, etc.)
        $script_name = isset($_SERVER['SCRIPT_NAME']) ? basename(sanitize_text_field($_SERVER['SCRIPT_NAME'])) : '';
        $allowed_scripts = array('profile.php');
        
        if (in_array($script_name, $allowed_scripts, true)) {
            return; // Allow profile access
        }
        
        // Whitelist specific AJAX actions only (prevent bypass via other AJAX endpoints)
        $is_ajax = defined('DOING_AJAX') && DOING_AJAX;
        $allowed_ajax_actions = array(
            'vmpfence_setup_2fa',
            'vmpfence_enable_2fa',
            'vmpfence_verify_code',
            'vmpfence_verify_login_2fa',
            'heartbeat' // WordPress heartbeat
        );
        
        $current_ajax_action = isset($_POST['action']) ? sanitize_text_field($_POST['action']) : 
                              (isset($_GET['action']) ? sanitize_text_field($_GET['action']) : '');
        $is_allowed_ajax = $is_ajax && in_array($current_ajax_action, $allowed_ajax_actions, true);
        
        if ($current_page !== 'vmpfence-login-security' && !$is_allowed_ajax) {
            wp_safe_redirect(admin_url('admin.php?page=vmpfence-login-security&force_setup=1'));
            exit;
        }
    }

    /**
     * Show persistent admin notice for forced 2FA setup
     */
    public static function show_forced_setup_notice() {
        $user_id = get_current_user_id();
        if (!$user_id) {
            return;
        }
        
        $force_setup = get_user_meta($user_id, 'vmpfence_force_2fa_setup', true);
        if (!$force_setup) {
            return;
        }
        
        echo '<div class="notice notice-error is-dismissible" style="border-left-color: #dc3232;">';
        echo '<p><strong>' . esc_html__('🔒 Two-Factor Authentication Setup Required', 'vmpfence-security') . '</strong></p>';
        echo '<p>' . esc_html__('Your account requires 2FA setup. You must complete the setup below before you can access other areas of WordPress admin.', 'vmpfence-security') . '</p>';
        echo '</div>';
    }

    /**
     * Show grace period notice on login page
     *
     * @param string $message Current login messages
     * @return string
     */
    public static function show_grace_period_notice($message) {
        // Check if we should show grace reminder for this user
        $user_id = get_current_user_id();
        
        if ($user_id && get_transient('vmpfence_2fa_grace_reminder_' . $user_id)) {
            $days_remaining = self::get_grace_period_days_remaining($user_id);
            
            $notice = sprintf(
                '<div class="message" style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 12px;">
                    <strong>⚠️ Two-Factor Authentication Required</strong><br>
                    Your account requires 2FA setup. You have <strong>%d day(s)</strong> remaining to enable it.<br>
                    <a href="%s">Setup 2FA Now</a>
                </div>',
                $days_remaining,
                admin_url('admin.php?page=vmpfence-login-security')
            );
            
            delete_transient('vmpfence_2fa_grace_reminder_' . $user_id);
            
            return $notice . $message;
        }
        
        return $message;
    }

    /**
     * Set remember device cookie after successful 2FA verification
     *
     * @param int $user_id User ID
     */
    public static function remember_device($user_id) {
        $remember_enabled = VMPFence_Config::get('vmpfence_2fa_remember_device', false);
        
        if (!$remember_enabled) {
            return;
        }

        // Generate secure random token
        $token = bin2hex(random_bytes(32));
        $token_hash = hash('sha256', $token);
        
        // Store hash AND user ID in user meta (so we can validate later)
        update_user_meta($user_id, 'vmpfence_device_token_hash', $token_hash);
        update_user_meta($user_id, 'vmpfence_device_token_user', $user_id);
        update_user_meta($user_id, 'vmpfence_device_token_created', current_time('timestamp'));
        
        // Set cookie with generic name (not exposing user ID)
        // Cookie value is: user_id|token
        $cookie_value = $user_id . '|' . $token;
        $cookie_name = 'vmpfence_remember_device';
        setcookie(
            $cookie_name,
            $cookie_value,
            current_time('timestamp') + (30 * DAY_IN_SECONDS),
            COOKIEPATH,
            COOKIE_DOMAIN,
            is_ssl(), // Secure flag
            true      // HttpOnly flag
        );
    }

    /**
     * Clear remembered device token
     *
     * @param int $user_id User ID
     */
    public static function forget_device($user_id) {
        // Remove user meta
        delete_user_meta($user_id, 'vmpfence_device_token_hash');
        delete_user_meta($user_id, 'vmpfence_device_token_user');
        delete_user_meta($user_id, 'vmpfence_device_token_created');
        
        // Clear cookie (use current_time for consistency)
        $cookie_name = 'vmpfence_remember_device';
        setcookie($cookie_name, '', current_time('timestamp') - 3600, COOKIEPATH, COOKIE_DOMAIN);
    }

    /**
     * Get days remaining in grace period for a user
     *
     * @param int $user_id User ID
     * @return int Days remaining (0 if expired)
     */
    public static function get_grace_period_days_remaining($user_id) {
        $grace_period_days = VMPFence_Config::get('vmpfence_2fa_grace_period', 10);
        
        if ($grace_period_days === 0) {
            return 0;
        }

        $user = get_userdata($user_id);
        if (!$user) {
            return 0;
        }

        // Check for role-specific grace period start time (when role changed to required)
        $grace_start_timestamp = null;
        foreach ($user->roles as $role) {
            $role_grace_start = get_user_meta($user_id, 'vmpfence_2fa_grace_start_' . $role, true);
            if ($role_grace_start) {
                // Use the OLDEST grace start time if user has multiple required roles (consistent with is_within_grace_period)
                if ($grace_start_timestamp === null || $role_grace_start < $grace_start_timestamp) {
                    $grace_start_timestamp = $role_grace_start;
                }
            }
        }

        // If no role-specific grace start, use registration date
        if ($grace_start_timestamp === null) {
            $grace_start_timestamp = strtotime(get_date_from_gmt($user->user_registered));
        }

        $grace_end_timestamp = $grace_start_timestamp + ($grace_period_days * DAY_IN_SECONDS);
        // Use current_time for consistency with is_within_grace_period
        $current_timestamp = current_time('timestamp');
        // Use floor() so "23 hours left" shows as "0 days" not "1 day"
        $days_remaining = max(0, floor(($grace_end_timestamp - $current_timestamp) / DAY_IN_SECONDS));
        
        return $days_remaining;
    }
    
    /**
     * Handle new user registration - initialize grace period tracking
     *
     * @param int $user_id User ID
     */
    public static function handle_user_registration($user_id) {
        $user = get_userdata($user_id);
        if (!$user) {
            return;
        }
        
        // Check if any of user's roles require 2FA
        $current_timestamp = current_time('timestamp');
        foreach ($user->roles as $role) {
            $role_setting = VMPFence_Config::get('vmpfence_2fa_role_' . $role, 'disabled');
            if ($role_setting === 'required') {
                // Set grace start timestamp for this role
                update_user_meta($user_id, 'vmpfence_2fa_grace_start_' . $role, $current_timestamp);
            }
        }
    }
    
    /**
     * Handle role changes - initialize grace period for newly required roles
     *
     * @param int $user_id User ID
     * @param string $role New role
     * @param array $old_roles Old roles (only for set_user_role)
     */
    public static function handle_role_change($user_id, $role, $old_roles = array()) {
        // Check if the new role requires 2FA
        $role_setting = VMPFence_Config::get('vmpfence_2fa_role_' . $role, 'disabled');
        if ($role_setting === 'required') {
            // Check if grace period not already started for this role
            $existing_grace_start = get_user_meta($user_id, 'vmpfence_2fa_grace_start_' . $role, true);
            if (!$existing_grace_start) {
                // Set grace start timestamp NOW (when role changed to required)
                update_user_meta($user_id, 'vmpfence_2fa_grace_start_' . $role, current_time('timestamp'));
            }
        }
    }
    
    /**
     * Handle user logout - cleanup forced setup flag and remember device
     *
     * @param int $user_id User ID
     */
    public static function handle_logout($user_id) {
        // Note: We do NOT clear force_setup flag on logout
        // If user was required to setup 2FA, they still are required on next login
        // Only successful 2FA activation clears this flag
        
        // Clear remember device cookie and meta if set
        $remember_enabled = VMPFence_Config::get('vmpfence_2fa_remember_device', false);
        if ($remember_enabled) {
            self::forget_device($user_id);
        }
    }
    
    /**
     * Enforce 2FA setup lockdown on WooCommerce frontend pages
     * Redirects users with forced setup to admin 2FA setup page
     */
    public static function enforce_woocommerce_lockdown() {
        // Only run for logged-in users
        $user_id = get_current_user_id();
        if (!$user_id) {
            return;
        }
        
        // Check if WooCommerce is active
        if (!function_exists('is_woocommerce')) {
            return;
        }
        
        // Check if user has forced setup flag
        $force_setup = get_user_meta($user_id, 'vmpfence_force_2fa_setup', true);
        if (!$force_setup) {
            return;
        }
        
        // Redirect from WooCommerce pages to 2FA setup
        if (is_account_page() || is_checkout() || is_cart()) {
            wp_safe_redirect(admin_url('admin.php?page=vmpfence-login-security&force_setup=1'));
            exit;
        }
    }
    
    /**
     * Block REST API access if 2FA setup is forced
     *
     * @param WP_Error|null|true $result Authentication result
     * @return WP_Error|null|true
     */
    public static function enforce_2fa_for_rest_api($result) {
        // If already an error, return it
        if (is_wp_error($result)) {
            return $result;
        }
        
        // BUG FIX #8: Skip XML-RPC requests (REST API filter shouldn't affect XML-RPC)
        if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) {
            return $result;
        }
        
        $user_id = get_current_user_id();
        if (!$user_id) {
            return $result;  // Not authenticated, let other auth handle it
        }
        
        $force_setup = get_user_meta($user_id, 'vmpfence_force_2fa_setup', true);
        if ($force_setup) {
            return new WP_Error(
                'vmpfence_2fa_required',
                __('You must complete Two-Factor Authentication setup before using the REST API. Please log in to the WordPress admin to complete setup.', 'vmpfence-security'),
                array('status' => 403)
            );
        }
        
        return $result;
    }
}

// Initialize enforcement hooks on plugins_loaded (priority 25 - after other systems)
add_action('plugins_loaded', array('VMPFence_2FA_Enforcement', 'init'), 25);
