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

use OTPHP\TOTP;
use WPUltimateSecurity\UltimateSecurity\Contracts\TwoFactorProviderInterface;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Services\EncryptionService;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Helpers\QRCodeGenerator;

/**
 * TOTP (Time-based One-Time Password) Provider
 *
 * Handles TOTP-based two-factor authentication (Google Authenticator, etc.).
 * Implements TwoFactorProviderInterface for consistent 2FA provider API.
 *
 * @implements TwoFactorProviderInterface
 */
class TOTPProvider implements TwoFactorProviderInterface
{
	/**
	 * Provider slug for internal use
	 */
	private const PROVIDER_SLUG = 'totp';

	/**
	 * Settings key in plugin options
	 */
	private const SETTINGS_KEY = '2fa_totp';

	// =========================================================================
	// TwoFactorProviderInterface Implementation
	// =========================================================================

	/**
	 * {@inheritdoc}
	 */
	public function isEnabledForUser(int $userId): bool
	{
		return self::is_configured($userId);
	}

	/**
	 * {@inheritdoc}
	 */
	public function enableForUser(int $userId): bool
	{
		// Generate TOTP secret if not exists
		$data = self::get_otp_data($userId);
		return $data !== false;
	}

	/**
	 * {@inheritdoc}
	 */
	public function disableForUser(int $userId): bool
	{
		return self::disable_for_user($userId);
	}

	/**
	 * {@inheritdoc}
	 *
	 * TOTP codes are generated on the user's device, so this is a no-op.
	 * Returns true to indicate the provider is ready to verify codes.
	 */
	public function sendCode(int $userId): bool
	{
		// TOTP doesn't send codes - they're generated by the authenticator app
		return true;
	}

	/**
	 * {@inheritdoc}
	 */
	public function verifyCode(int $userId, string $code): bool
	{
		return self::verify_code($userId, $code);
	}

	/**
	 * {@inheritdoc}
	 */
	public function getProviderName(): string
	{
		return __('Authenticator App (TOTP)', 'ultimate-security');
	}

	/**
	 * {@inheritdoc}
	 */
	public function getProviderSlug(): string
	{
		return self::PROVIDER_SLUG;
	}

	/**
	 * {@inheritdoc}
	 */
	public function getSettingsKey(): string
	{
		return self::SETTINGS_KEY;
	}

	// =========================================================================
	// Static Methods (Backward Compatibility)
	// =========================================================================

	/**
	 * Get TOTP OTP code and provisioning data
	 *
	 * @param int $user_id User ID
	 * @return array|false Array with totp_secret, totp_code, totp_qr_code or false
	 */
	public static function get_otp_data($user_id)
	{
		$user = get_userdata($user_id);
		if ($user === false) {
			return false;
		}

		$is_encryption_enabled = EncryptionService::is_encryption_enabled();
		$totp_secret = get_user_meta($user_id, 'ultimate_security_2fa_totp_secret', true);

		if (!$totp_secret) {
			$totp = TOTP::create();
			$raw_totp_secret = $totp->getSecret();

			if ($is_encryption_enabled) {
				$totp_secret = EncryptionService::encrypt($raw_totp_secret);
			} else {
				$totp_secret = $raw_totp_secret;
			}

			update_user_meta($user_id, 'ultimate_security_2fa_totp_secret', $totp_secret);
		}

		$original_totp_secret = $is_encryption_enabled
			? EncryptionService::decrypt($totp_secret)
			: $totp_secret;

		if ($original_totp_secret === false) {
			// handle decrypt failure — force re-enroll or return an error
			return false;
		}

		$totp = TOTP::create($original_totp_secret);
		$totp->setLabel($user->user_login);
		$totp->setIssuer(get_bloginfo('name'));

		// Generate the QR code URL
		// $totp_qr_code_uri = sprintf(
		// 	'https://api.qrserver.com/v1/create-qr-code/?data=%s&size=200x200',
		// 	urlencode($totp->getProvisioningUri())
		// );

		// Generate the QR code locally
		$totp_qr_code_uri = QRCodeGenerator::generate($totp->getProvisioningUri());

		$totp_current_code = $totp->now(); // Get current code
		return [
			'totp_secret' => esc_html($original_totp_secret),
			'totp_code' => esc_html($totp_current_code),
			'totp_qr_code' => $totp_qr_code_uri,
		];
	}

	/**
	 * Verify TOTP code with clock drift tolerance
	 *
	 * Uses the OTPHP library's built-in verification which handles:
	 * - Timing-safe comparison
	 * - Clock drift tolerance (configurable window)
	 *
	 * @param int $user_id User ID
	 * @param string $entered_code Entered code
	 * @param int $window Clock drift window (default 1 = +/- 30 seconds)
	 * @return bool True if valid
	 */
	public static function verify_code($user_id, $entered_code, $window = 1)
	{
		$user = get_userdata($user_id);
		if ($user === false) {
			return false;
		}

		$is_encryption_enabled = EncryptionService::is_encryption_enabled();
		$totp_secret = get_user_meta($user_id, 'ultimate_security_2fa_totp_secret', true);

		if (!$totp_secret) {
			return false;
		}

		$original_totp_secret = $is_encryption_enabled
			? EncryptionService::decrypt($totp_secret)
			: $totp_secret;

		if ($original_totp_secret === false) {
			return false;
		}

		// Create TOTP instance with the user's secret
		$totp = TOTP::create($original_totp_secret);

		// Sanitize the entered code - remove any spaces and ensure it's a string
		$entered_code = preg_replace('/\s+/', '', (string)$entered_code);

		// Use OTPHP's verify method which handles:
		// - Timing-safe comparison internally
		// - Clock drift tolerance via window parameter
		// - The window parameter means +/- window periods (30 seconds each for TOTP)
		// Default window of 1 allows for +/- 30 seconds clock drift
		return $totp->verify($entered_code, null, $window);
	}

	/**
	 * Reset TOTP secret for user
	 *
	 * @param int $user_id User ID
	 * @return array|false New OTP data or false
	 */
	public static function reset_secret($user_id)
	{
		delete_user_meta($user_id, 'ultimate_security_2fa_totp_secret');
		return self::get_otp_data($user_id);
	}

	/**
	 * Check if TOTP is configured for user
	 *
	 * @param int $user_id User ID
	 * @return bool True if configured
	 */
	public static function is_configured($user_id)
	{
		$totp_secret = get_user_meta($user_id, 'ultimate_security_2fa_totp_secret', true);
		return !empty($totp_secret);
	}

	/**
	 * Disable TOTP for user
	 *
	 * @param int $user_id User ID
	 * @return bool Success status
	 */
	public static function disable_for_user($user_id)
	{
		delete_user_meta($user_id, 'ultimate_security_2fa_totp_secret');
		return true;
	}
}
