<?php
namespace WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Http;

use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Providers\EmailProvider;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Providers\TOTPProvider;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Providers\HOTPProvider;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Services\TokenService;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Services\TrustedDeviceService;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\Utils;
use WPUltimateSecurity\UltimateSecurity\Modules\TestMode\TestModeService;
use WPUltimateSecurity\UltimateSecurity\Support\Validation\ProFeatureMiddleware;

/**
 * Login Handler for 2FA
 *
 * Intercepts login flow and initiates 2FA verification
 * Uses token-based authentication similar to Two Factor by WordPress.org
 * and WP 2FA by Melapress
 */

class LoginHandler
{
	/**
	 * The action parameter for 2FA verification on wp-login.php
	 */
	const LOGIN_ACTION = 'ultimate_security_2fa';

	/**
	 * Store the auth token during login flow (before logout)
	 *
	 * @var string|null
	 */
	private static $pending_auth_token = null;

	/**
	 * Store the user ID during login flow (before logout)
	 *
	 * @var int|null
	 */
	private static $pending_user_id = null;

	/**
	 * Store the 2FA provider during login flow
	 *
	 * @var string|null
	 */
	private static $pending_provider = null;

	/**
	 * Clean up 2FA pending data on authentication failure
	 *
	 * @param string $username Username
	 * @param \WP_Error $error Error object
	 */
	public static function cleanup_2fa_on_auth_failure($username, $error)
	{
		$user = get_user_by('login', $username);
		if ($user) {
			// Clean up any pending 2FA state if authentication fails
			delete_user_meta($user->ID, 'ultimate_security_2fa_pending');
			delete_user_meta($user->ID, '_ultimate_security_2fa_login_pending');
			delete_user_meta($user->ID, 'ultimate_security_2fa_code');
			delete_user_meta($user->ID, 'ultimate_security_2fa_code_expires');
			delete_transient('2fa_code_generation_' . $user->ID);
		}
	}

	/**
	 * Intercept login and initiate 2FA if needed
	 *
	 * @param \WP_User|\WP_Error $user User object or error
	 * @param string $username Username
	 * @param string $password Password
	 * @return \WP_User|\WP_Error
	 */
	public static function intercept_login($user, $username, $password)
	{
		if (is_wp_error($user)) {
			return $user;
		}

		$twofa_status = self::get_user_2fa_status($user->ID);

		// Only proceed if 2FA is required
		if ($twofa_status) {
			// Test Mode: Log simulation and skip 2FA requirement
			if (TestModeService::shouldSimulate($user->ID)) {
				TestModeService::logSimulation(
					'2fa_authentication',
					'2fa_required',
					sprintf('2FA (%s) would be required', $twofa_status),
					[
						'user_id' => $user->ID,
						'2fa_method' => $twofa_status,
					]
				);
				return $user; // Skip 2FA
			}

			// Check if device is trusted - skip 2FA if it is (Pro feature)
			if (ProFeatureMiddleware::hasProFeature('trusted_devices') && TrustedDeviceService::is_device_trusted($user->ID)) {
				return $user; // Skip 2FA for trusted device
			}

			// Check if this is WooCommerce login
			$is_woocommerce_login = self::detect_woocommerce_login();

			// Set pending flag for all 2FA methods
			update_user_meta($user->ID, 'ultimate_security_2fa_pending', true);

			// Generate authentication token (token-based approach like Two Factor plugin)
			$auth_token = TokenService::generate_auth_token($user->ID);

			// Store token and user data for use in redirect_to_verification
			self::$pending_auth_token = $auth_token;
			self::$pending_user_id = $user->ID;
			self::$pending_provider = $twofa_status;

			// If WooCommerce login, add WooCommerce-specific handling
			if ($is_woocommerce_login) {
				update_user_meta($user->ID, 'ultimate_security_2fa_woocommerce_login', true);
			}

			// Send verification code for email provider BEFORE redirect
			// This must happen here because wp_login action fires after redirect
			if ($twofa_status === 'email') {
				EmailProvider::send_verification_code($user->ID);
			}
		}

		return $user;
	}

	/**
	 * Detect if this is a WooCommerce login
	 *
	 * @return bool True if WooCommerce login detected
	 */
	protected static function detect_woocommerce_login()
	{
		// Check if WooCommerce is active
		if (!function_exists('is_plugin_active') || !is_plugin_active('woocommerce/woocommerce.php')) {
			return false;
		}

		// Check for our custom hidden field (most reliable)
		if (isset($_POST['ultimate_security_woocommerce_login'])) {
			return true;
		}

		// Check for WooCommerce login nonce
		if (isset($_POST['woocommerce-login-nonce']) || isset($_REQUEST['woocommerce-login-nonce'])) {
			return true;
		}

		// Check for WooCommerce AJAX login
		if (defined('DOING_AJAX') && DOING_AJAX && isset($_REQUEST['action']) && $_REQUEST['action'] === 'woocommerce_login') {
			return true;
		}

		// Check referer for WooCommerce pages
		$referer = isset($_SERVER['HTTP_REFERER']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_REFERER'])) : '';
		if (strpos($referer, 'my-account') !== false || strpos($referer, 'checkout') !== false) {
			return true;
		}

		// Check current URL for WooCommerce pages
		$current_url = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
		if (strpos($current_url, 'my-account') !== false || strpos($current_url, 'checkout') !== false) {
			return true;
		}

		// Check for page_id parameter matching WooCommerce pages
		if (isset($_POST['page_id']) || isset($_GET['page_id'])) {
			$page_id = isset($_POST['page_id']) ? intval(wp_unslash($_POST['page_id'])) : intval(wp_unslash($_GET['page_id']));
			$myaccount_page_id = (int) get_option('woocommerce_myaccount_page_id');
			$checkout_page_id = (int) get_option('woocommerce_checkout_page_id');

			if ($page_id === $myaccount_page_id || $page_id === $checkout_page_id) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Redirect to 2FA verification page after login
	 *
	 * Uses token-based approach: redirects to wp-login.php with action parameter
	 * and passes authentication via hidden form fields (not cookies)
	 *
	 * @param string $redirect_to Redirect URL
	 * @param string $requested_redirect_to Requested redirect URL
	 * @param \WP_User|\WP_Error $user User object
	 * @return string
	 */
	public static function redirect_to_verification($redirect_to, $requested_redirect_to, $user)
	{
		// Skip if login failed
		if (is_wp_error($user)) {
			return $redirect_to;
		}
		if (TestModeService::shouldSimulate($user->ID)) {
			return $redirect_to; // Skip 2FA
		}

		// Check if this user has 2FA pending (set in intercept_login)
		if ($user instanceof \WP_User) {
			$ultimate_security_2fa_pending = get_user_meta($user->ID, 'ultimate_security_2fa_pending', true);

			if ($ultimate_security_2fa_pending && self::$pending_auth_token) {
				// Store the intended redirect URL for after 2FA verification
				$intended_redirect = !empty($requested_redirect_to) ? $requested_redirect_to : $redirect_to;

				// For WooCommerce logins, ensure we redirect to My Account page
				$is_woocommerce = get_user_meta($user->ID, 'ultimate_security_2fa_woocommerce_login', true);
				if ($is_woocommerce && function_exists('wc_get_page_permalink')) {
					$intended_redirect = wc_get_page_permalink('myaccount');
				}

				if (!empty($intended_redirect)) {
					update_user_meta($user->ID, 'ultimate_security_2fa_redirect_to', $intended_redirect);
				}

				// Preserve LoginLimit bypass state during 2FA flow
				if (isset($_COOKIE['ultimate_security_bypass'])) {
					$bypass_value = sanitize_text_field(wp_unslash($_COOKIE['ultimate_security_bypass']));
					$bypass_transient = get_transient('ultimate_security_bypass_' . $bypass_value);
					if ($bypass_transient) {
						// Extend the bypass transient to match token timeout (30 min)
						set_transient('ultimate_security_bypass_' . $bypass_value, 1, 30 * MINUTE_IN_SECONDS);
					}
				}

				// Logout to prevent session creation (user must complete 2FA first)
				wp_logout();

				// Store a persistent marker in user meta to detect 2FA pending on wp-login.php
				// This marker survives page refreshes and doesn't rely on transients
				update_user_meta($user->ID, '_ultimate_security_2fa_login_pending', 'pending');
				
				// Build redirect URL to wp-login.php without query parameters
				// Store user data in a temporary option for clean URL detection
				$session_key = 'ultimate_security_2fa_session_' . wp_hash(self::$pending_user_id . self::$pending_auth_token . time());
				set_transient($session_key, [
					'user_id' => self::$pending_user_id,
					'token' => self::$pending_auth_token,
					'provider' => self::$pending_provider,
					'created_at' => time(),
				], 30 * MINUTE_IN_SECONDS);
				
				$verification_url = wp_login_url();

				// Clear static vars
				self::$pending_auth_token = null;
				self::$pending_user_id = null;
				self::$pending_provider = null;

				return $verification_url;
			}
		}

		return $redirect_to;
	}

	/**
	 * Handle WooCommerce-specific login redirect
	 * This is called by the woocommerce_login_redirect filter
	 *
	 * @param string $redirect Redirect URL
	 * @param \WP_User $user User object
	 * @return string Modified redirect URL
	 */
	public static function woocommerce_login_redirect($redirect, $user)
	{
		// Check if 2FA is pending for this user
		$is_pending = get_user_meta($user->ID, 'ultimate_security_2fa_pending', true);

		if ($is_pending) {
			// Get the pending auth token if set by intercept_login
			// If not set yet, generate one for WooCommerce login flow
			$auth_token = self::$pending_auth_token;
			$pending_user_id = self::$pending_user_id;
			$pending_provider = self::$pending_provider;
			
			// If intercept_login hasn't set these yet, we need to set them here
			if (!$auth_token) {
				$twofa_status = self::get_user_2fa_status($user->ID);
				if ($twofa_status) {
					$auth_token = TokenService::generate_auth_token($user->ID);
					$pending_user_id = $user->ID;
					$pending_provider = $twofa_status;
					
					// Store in static vars for potential later use
					self::$pending_auth_token = $auth_token;
					self::$pending_user_id = $pending_user_id;
					self::$pending_provider = $pending_provider;
					
					// Send verification code for email provider
					if ($twofa_status === 'email') {
						EmailProvider::send_verification_code($user->ID);
					}
				}
			}
			
			// If we have a valid token and pending 2FA, redirect to verification
			if ($auth_token && $pending_user_id) {
				// Store the intended WooCommerce redirect
				if (!empty($redirect)) {
					update_user_meta($user->ID, 'ultimate_security_2fa_redirect_to', $redirect);
				} else {
					// Default to My Account page
					if (function_exists('wc_get_page_permalink')) {
						update_user_meta($user->ID, 'ultimate_security_2fa_redirect_to', wc_get_page_permalink('myaccount'));
					}
				}

				// Logout to prevent session creation
				wp_logout();

				// Store a persistent marker in user meta to detect 2FA pending on wp-login.php
				// This marker survives page refreshes and doesn't rely on transients
				update_user_meta($user->ID, '_ultimate_security_2fa_login_pending', 'pending');
				
				// Build redirect URL to wp-login.php without query parameters
				// Store user data in a temporary option for clean URL detection
				$session_key = 'ultimate_security_2fa_session_' . wp_hash($pending_user_id . $auth_token . time());
				set_transient($session_key, [
					'user_id' => $pending_user_id,
					'token' => $auth_token,
					'provider' => $pending_provider,
					'created_at' => time(),
				], 30 * MINUTE_IN_SECONDS);
				
				$verification_url = wp_login_url();

				// Clear static vars
				self::$pending_auth_token = null;
				self::$pending_user_id = null;
				self::$pending_provider = null;

				return $verification_url;
			}
		}

		return $redirect;
	}

	/**
	 * Check if this is a 2FA verification request
	 *
	 * @return bool
	 * @deprecated 2FA is now detected without query parameters
	 */
	public static function is_2fa_request()
	{
		return isset($_REQUEST['action']) && $_REQUEST['action'] === self::LOGIN_ACTION;
	}

	/**
	 * Get 2FA verification data from URL parameters
	 *
	 * @return array|false Array with user_id, token, provider or false
	 * @deprecated 2FA verification now works without query parameters
	 */
	public static function get_verification_data_from_url()
	{
		if (!self::is_2fa_request()) {
			return false;
		}

		$session_key = isset($_GET['session']) ? sanitize_text_field(wp_unslash($_GET['session'])) : '';

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

		// Get data from transient
		$verification_data = get_transient($session_key);
		if (!$verification_data) {
			return false;
		}

		return $verification_data;
	}

	/**
	 * Get user's 2FA status
	 *
	 * Checks both:
	 * 1. User-selected provider (user meta)
	 * 2. Admin-enforced 2FA (role-based)
	 *
	 * Admin enforcement takes precedence - if a role has 2FA enabled,
	 * users with that role must use 2FA even if they haven't selected a provider.
	 *
	 * @param int $user_id User ID
	 * @return string|false 'email', 'totp', 'hotp', or false
	 */
	public static function get_user_2fa_status($user_id)
	{
		$user = get_userdata($user_id);
		if (!$user) {
			return false;
		}

		$options = Utils::ultimate_security_get_option();
		$selected_2fa_provider = get_user_meta($user->ID, 'ultimate-security-2fa-provider', true);

		// First check if user has selected a provider
		if ($selected_2fa_provider) {
			// Case-insensitive comparison for provider names
			$provider_lower = strtolower($selected_2fa_provider);

			if ($provider_lower === 'email') {
				return EmailProvider::is_enabled_for_user($user_id) ? 'email' : false;
			} elseif ($provider_lower === 'authenticator application') {
				$enable_application_authentication = isset($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_enable'])
					? sanitize_text_field(wp_unslash($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_enable']))
					: 0;

				if (!$enable_application_authentication) {
					return false;
				}

				$application_authentication_enable_for = isset($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_enable_for'])
					? wp_unslash($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_enable_for'])
					: [];

				$application_authentication_type = isset($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_type'])
					? sanitize_text_field(wp_unslash($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_type']))
					: 'TOTP';

				$application_allowed_user_roles = [];
				if ($application_authentication_enable_for) {
					foreach ($application_authentication_enable_for as $role) {
						$application_allowed_user_roles[] = $role;
					}
				}

				$application_allowed_user_roles = array_intersect($application_allowed_user_roles, $user->roles);
				if (!empty($application_allowed_user_roles)) {
					if ($application_authentication_type == 'TOTP' && TOTPProvider::is_configured($user_id)) {
						return 'totp';
					} elseif ($application_authentication_type == 'HOTP' && HOTPProvider::is_configured($user_id)) {
						return 'hotp';
					}
				}
			} elseif ($provider_lower === 'sms') {
				// Pro provider: SMS Authentication
				// Keep this guarded so the free plugin works without Pro installed.
				if (class_exists('\WPUltimateSecurity\UltimateSecurityPro\Modules\Authentication\Authentication2FASMS')) {
					if (\WPUltimateSecurity\UltimateSecurityPro\Modules\Authentication\Authentication2FASMS::is_sms_enabled($user_id)) {
						return 'sms';
					}
				}
			}
		}

		// If no provider selected, check for admin-enforced 2FA
		// This ensures users with enforced roles must set up 2FA
		return self::get_enforced_2fa_for_user($user);
	}

	/**
	 * Check if user has admin-enforced 2FA based on their role
	 *
	 * Returns the 2FA method that should be enforced for this user,
	 * even if they haven't selected a provider yet.
	 *
	 * @param \WP_User $user User object
	 * @return string|false 'email' or false (email is default enforced method)
	 */
	private static function get_enforced_2fa_for_user($user)
	{
		$options = Utils::ultimate_security_get_option();

		// Check Email 2FA enforcement
		$enable_email = isset($options['security']['2fa_authentication']['email_authentication']['2fa_email_verification_enable'])
			? sanitize_text_field(wp_unslash($options['security']['2fa_authentication']['email_authentication']['2fa_email_verification_enable']))
			: 0;

		if ($enable_email) {
			$email_enable_for = isset($options['security']['2fa_authentication']['email_authentication']['2fa_email_verification_enable_for'])
				? wp_unslash($options['security']['2fa_authentication']['email_authentication']['2fa_email_verification_enable_for'])
				: [];

			$email_allowed_roles = [];
			if ($email_enable_for) {
				foreach ($email_enable_for as $role) {
					$email_allowed_roles[] = $role;
				}
			}

			$user_email_roles = array_intersect($email_allowed_roles, $user->roles);
			if (!empty($user_email_roles)) {
				return 'email';
			}
		}

		// Check Application 2FA enforcement (TOTP/HOTP)
		$enable_app = isset($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_enable'])
			? sanitize_text_field(wp_unslash($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_enable']))
			: 0;

		if ($enable_app) {
			$app_enable_for = isset($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_enable_for'])
				? wp_unslash($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_enable_for'])
				: [];

			$app_allowed_roles = [];
			if ($app_enable_for) {
				foreach ($app_enable_for as $role) {
					$app_allowed_roles[] = $role;
				}
			}

			$user_app_roles = array_intersect($app_allowed_roles, $user->roles);
			if (!empty($user_app_roles)) {
				$app_type = isset($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_type'])
					? sanitize_text_field(wp_unslash($options['security']['2fa_authentication']['application_authentication']['2fa_application_authentication_type']))
					: 'TOTP';

				// For TOTP/HOTP, user must have configured it first
				// Return false here so they can set it up
				if ($app_type == 'TOTP' && TOTPProvider::is_configured($user->ID)) {
					return 'totp';
				} elseif ($app_type == 'HOTP' && HOTPProvider::is_configured($user->ID)) {
					return 'hotp';
				}
				// If not configured, don't enforce - let them set it up first
			}
		}

		return false;
	}

	/**
	 * Register URL rewrite rules for 2FA verification page
	 */
	public static function register_rewrite_rules()
	{
		add_rewrite_rule(
			'^ultimate-security-2fa-verification/?$',
			'index.php?ultimate_security_2fa_code=1',
			'top'
		);
	}

	/**
	 * Add query vars for 2FA
	 *
	 * @param array $query_vars Query vars
	 * @return array
	 */
	public static function add_query_vars($query_vars)
	{
		$query_vars[] = 'ultimate_security_2fa_code';
		return $query_vars;
	}

	/**
	 * Flush rewrite rules on activation
	 */
	public static function flush_rewrite_rules()
	{
		self::register_rewrite_rules();
		flush_rewrite_rules();
	}
}
