<?php
declare(strict_types=1);

namespace WPUltimateSecurity\UltimateSecurity\Modules\Authentication;

use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Http\LoginHandler;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Http\VerificationHandler;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Http\AjaxHandlers;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Providers\TOTPProvider;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Providers\HOTPProvider;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\Utils;
use WPUltimateSecurity\UltimateSecurity\Modules\Base\AbstractModule;

/**
 * Two-Factor Authentication (2FA) Module
 *
 * Orchestrates all 2FA functionality through specialized components:
 * - Providers: Email, TOTP, HOTP authentication methods
 * - Services: Encryption, Backup Codes, Rate Limiting, Sessions, Trusted Devices
 * - Storage: Database tables, Audit logging
 * - HTTP: Login flow, Verification page, AJAX handlers
 * - Helpers: QR codes, Device fingerprinting
 */
class Authentication2FA extends AbstractModule
{
	/**
	 * Module identifier.
	 */
	public const MODULE_ID = 'auth.2fa';

	/**
	 * Module version.
	 */
	protected const VERSION = '2.0.0';

	/**
	 * Get the unique module identifier.
	 *
	 * @return string
	 */
	public static function getId(): string
	{
		return self::MODULE_ID;
	}

	/**
	 * Get the human-readable module name.
	 *
	 * @return string
	 */
	public static function getName(): string
	{
		return __('Two-Factor Authentication', 'ultimate-security');
	}

	/**
	 * Get the module domain.
	 *
	 * @return string
	 */
	public static function getDomain(): string
	{
		return 'authentication';
	}
	/**
	 * Boot the module (registers hooks and handlers).
	 *
	 * Token-based 2FA flow (similar to Two Factor by WordPress.org):
	 * 1. User logs in with username/password
	 * 2. authenticate filter intercepts and generates auth token
	 * 3. login_redirect redirects to wp-login.php?action=ultimate_security_2fa
	 * 4. login_init hook displays 2FA form on wp-login.php
	 * 5. Form submits with hidden fields (user ID, token, provider)
	 * 6. Token validated, user logged in on success
	 *
	 * @return void
	 */
	public static function boot(): void
	{
		// User profile hooks
		add_action('show_user_profile', [self::class, 'display_2fa_settings']);
		add_action('edit_user_profile', [self::class, 'display_2fa_settings']);

		// Login flow hooks (token-based approach)
		add_filter('authenticate', [LoginHandler::class, 'intercept_login'], 30, 3);
		add_filter('login_redirect', [LoginHandler::class, 'redirect_to_verification'], 10, 3);

		// Clean up 2FA pending data on authentication failure
		add_action('wp_login_failed', [LoginHandler::class, 'cleanup_2fa_on_auth_failure'], 10, 2);

		// Token-based 2FA verification on wp-login.php (like Two Factor plugin)
		add_action('login_init', [VerificationHandler::class, 'handle_verification_page']);



		// WooCommerce specific hooks
		add_filter('woocommerce_login_redirect', [LoginHandler::class, 'woocommerce_login_redirect'], 20, 2);
		add_action('woocommerce_login_form_end', [self::class, 'add_woocommerce_context_field']);

		// Allow WooCommerce customers to access profile.php for 2FA settings
		add_filter('woocommerce_prevent_admin_access', [self::class, 'allow_profile_access_for_2fa'], 10, 1);

		// AJAX handlers
		AjaxHandlers::register();

		// XML-RPC control
		add_action('init', [self::class, 'conditionally_disable_xmlrpc']);

		// Clean up old trusted device metadata (one-time cleanup)
		add_action('init', [self::class, 'cleanup_trusted_device_metadata'], 5);
	}

	/**
	 * Add hidden field to WooCommerce login form to identify WooCommerce login context
	 */
	public static function add_woocommerce_context_field()
	{
		echo '<input type="hidden" name="ultimate_security_woocommerce_login" value="1" />';
	}

	/**
	 * Allow WooCommerce customers to access profile.php for 2FA settings
	 *
	 * WooCommerce by default prevents customers (users without edit_posts capability)
	 * from accessing wp-admin. This filter allows access to profile.php so customers
	 * can manage their 2FA settings.
	 *
	 * @param bool $prevent_access Whether to prevent admin access
	 * @return bool Modified prevent_access value
	 */
	public static function allow_profile_access_for_2fa($prevent_access)
	{
		// Only modify if access would be prevented
		if (!$prevent_access) {
			return $prevent_access;
		}

		// Check if we're on profile.php or user-edit.php (for own profile)
		global $pagenow;

		if ($pagenow === 'profile.php') {
			// Allow customers to access their own profile
			return false;
		}

		if ($pagenow === 'user-edit.php') {
			// Only allow if editing own profile
			$user_id = isset($_GET['user_id']) ? absint($_GET['user_id']) : 0;
			if ($user_id === get_current_user_id()) {
				return false;
			}
		}

		return $prevent_access;
	}

	/**
	 * Display 2FA settings in user profile
	 *
	 * @param \WP_User $user User object
	 */
	public static function display_2fa_settings($user)
	{
		// TOTP data - handle decryption failure
		$totp_data = TOTPProvider::get_otp_data($user->ID);
		$totp_error = false;
		if ($totp_data === false) {
			// Decryption failed - secret may be corrupted or encryption key changed
			$totp_error = __('TOTP secret decryption failed. Please reset your authenticator app.', 'ultimate-security');
			$totp_secret = '';
			$totp_current_code = '';
			$totp_qr_code_uri = '#';
		} else {
			$totp_secret = isset($totp_data['totp_secret']) ? $totp_data['totp_secret'] : '';
			$totp_current_code = isset($totp_data['totp_code']) ? $totp_data['totp_code'] : '';
			$totp_qr_code_uri = isset($totp_data['totp_qr_code']) ? $totp_data['totp_qr_code'] : '#';
		}

		$time_now = time();
		$refresh_after = 30 - ($time_now % 30);

		// HOTP data - handle decryption failure
		$hotp_data = HOTPProvider::get_otp_data($user->ID);
		$hotp_error = false;
		if ($hotp_data === false) {
			// Decryption failed - secret may be corrupted or encryption key changed
			$hotp_error = __('HOTP secret decryption failed. Please reset your authenticator app.', 'ultimate-security');
			$hotp_secret = '';
			$hotp_current_code = '';
			$hotp_qr_code_uri = '#';
			$hotp_counter = 0;
		} else {
			$hotp_secret = isset($hotp_data['hotp_secret']) ? $hotp_data['hotp_secret'] : '';
			$hotp_current_code = isset($hotp_data['hotp_code']) ? $hotp_data['hotp_code'] : '';
			$hotp_qr_code_uri = isset($hotp_data['hotp_qr_code']) ? $hotp_data['hotp_qr_code'] : '#';
			$hotp_counter = isset($hotp_data['hotp_counter']) ? $hotp_data['hotp_counter'] : 0;
		}

		// Check if React version is available
		$react_template = ULTIMATE_SECURITY_PATH . 'templates/admin/ultimate-security-admin-display-2fa-react.php';
		if (file_exists($react_template)) {
			require_once $react_template;
		}
	}

	/**
	 * Conditionally disable XML-RPC based on settings
	 */
	public static function conditionally_disable_xmlrpc()
	{
		$options = Utils::ultimate_security_get_option();

		if (
			isset($options['security']['2fa_authentication']['application_authentication']['xmlrpc_requests']) &&
			$options['security']['2fa_authentication']['application_authentication']['xmlrpc_requests'] === 'require'
		) {
			add_filter('xmlrpc_enabled', '__return_false');
		}
	}

	/**
	 * Clean up old trusted device metadata (one-time migration)
	 *
	 * Removes legacy trusted device cookies and user meta that are no longer used
	 * after removing the grace period feature.
	 */
	public static function cleanup_trusted_device_metadata()
	{
		// Check if cleanup has already been performed
		$cleanup_done = get_option('ultimate_security_trusted_device_cleanup_done', false);
		if ($cleanup_done) {
			return;
		}

		global $wpdb;

		// Remove all trusted device user metadata
		$wpdb->query(
			"DELETE FROM {$wpdb->usermeta}
			WHERE meta_key IN (
				'ultimate_security_trusted_device_hash',
				'ultimate_security_trusted_device_expires',
				'ultimate_security_trusted_devices'
			)"
		);

		// Mark cleanup as done
		update_option('ultimate_security_trusted_device_cleanup_done', true);

	}
}
