<?php
/**
 * 2FA User Profile Section
 *
 * @package TwoFactorAuthentication
 * @since 1.2.0
 */

namespace TwoFactorAuthentication;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Renders and handles the 2FA section in user profile.
 */
class UserProfile {

	private const USER_META_METHOD    = 'wpps_2fa_method';
	private const USER_META_USE_EMAIL = 'wpps_2fa_use_email';
	private const USER_META_USE_AUTH  = 'wpps_2fa_use_authenticator';
	private const USER_META_SECRET    = 'wpps_2fa_totp_secret';
	private const USER_META_BACKUP    = 'wpps_2fa_backup_codes';
	private const USER_META_TOTP_STEP = 'wpps_2fa_totp_last_step';
	private const PENDING_SECRET_TTL  = 900;

	public static function init( Module $module ): void {
		$self = new self();
		add_action( 'show_user_profile', [ $self, 'renderSection' ], 20 );
		add_action( 'edit_user_profile', [ $self, 'renderSection' ], 20 );
		add_action( 'personal_options_update', [ $self, 'saveSection' ], 10 );
		add_action( 'edit_user_profile_update', [ $self, 'saveSection' ], 10 );
		add_action( 'admin_enqueue_scripts', [ $self, 'enqueueAssets' ] );
		add_action( 'wp_ajax_wpps_2fa_setup_authenticator', [ $self, 'ajaxSetupAuthenticator' ] );
		add_action( 'wp_ajax_wpps_2fa_verify_authenticator', [ $self, 'ajaxVerifyAuthenticator' ] );
		add_action( 'wp_ajax_wpps_2fa_switch_method', [ $self, 'ajaxSwitchMethod' ] );
		add_action( 'wp_ajax_wpps_2fa_regenerate_backup_codes', [ $self, 'ajaxRegenerateBackupCodes' ] );
		add_action( 'wp_ajax_wpps_2fa_remove_authenticator', [ $self, 'ajaxRemoveAuthenticator' ] );
		add_action( 'wp_ajax_wpps_2fa_disable_for_user', [ $self, 'ajaxDisable2FAForUser' ] );
		add_action( 'wp_ajax_wpps_2fa_enable_for_user', [ $self, 'ajaxEnable2FAForUser' ] );
	}

	public function renderSection( \WP_User $user ): void {
		$settings = $this->getModule()->getSettings();
		$roles    = $settings['roles'] ?? [];
		$user_roles = (array) $user->roles;
		$applicable = array_intersect( $user_roles, $roles );
		if ( empty( $applicable ) ) {
			return;
		}

		$enable_email = ! empty( $settings['enable_email'] );
		$enable_auth  = ! empty( $settings['enable_authenticator'] );
		if ( ! $enable_email && ! $enable_auth ) {
			return;
		}

		$has_totp   = self::getTotpSecret( $user->ID ) !== '';
		$method_raw = get_user_meta( $user->ID, self::USER_META_METHOD, true ) ?: 'email';
		$use_email  = get_user_meta( $user->ID, self::USER_META_USE_EMAIL, true );
		$use_auth   = get_user_meta( $user->ID, self::USER_META_USE_AUTH, true );
		// Migrate from old single-method: if no checkboxes set, derive from method
		if ( $use_email === '' && $use_auth === '' ) {
			$use_email = ( $method_raw === 'email' ) ? '1' : '0';
			$use_auth  = ( $method_raw === 'authenticator' && $has_totp ) ? '1' : '0';
		}
		$use_email = ( $use_email === '1' || $use_email === true );
		$use_auth  = ( $use_auth === '1' || $use_auth === true );
		$primary   = in_array( $method_raw, [ 'email', 'authenticator' ], true ) ? $method_raw : 'email';
		$backup    = get_user_meta( $user->ID, self::USER_META_BACKUP, true );
		$backup_codes = is_string( $backup ) ? json_decode( $backup, true ) : [];
		$backup_count = is_array( $backup_codes ) ? count( $backup_codes ) : 0;

		$is_own_profile   = (int) $user->ID === (int) get_current_user_id();
		$can_edit_other   = ! $is_own_profile && current_user_can( 'edit_user', $user->ID );
		$two_fa_disabled  = get_user_meta( $user->ID, 'wpps_2fa_disabled', true ) === '1';
		$allow_edit_2fa   = $is_own_profile || $can_edit_other;
		$show_backup_row  = $enable_auth && $has_totp && ( $is_own_profile || $can_edit_other );
		$show_setup_auth  = ! $has_totp && $is_own_profile;
		$both_checked     = $use_email && $use_auth;
		?>
		<h2 id="wpps-2fa-section"><?php esc_html_e( 'Two-Factor Authentication', 'wp-powersuite' ); ?></h2>
		<div class="wpps-2fa-profile">
		<?php if ( $two_fa_disabled && $allow_edit_2fa ) : ?>
		<div class="notice notice-warning inline wpps-2fa-profile__banner">
			<p class="wpps-2fa-profile__banner-text"><?php esc_html_e( '2FA is disabled for this user. They will not be prompted for a second factor at login.', 'wp-powersuite' ); ?></p>
			<p class="wpps-2fa-profile__banner-actions">
				<button type="button" class="button button-primary" id="wpps-2fa-enable-for-user"><?php esc_html_e( 'Re-enable 2FA', 'wp-powersuite' ); ?></button>
			</p>
		</div>
		<?php endif; ?>
		<?php if ( ! $two_fa_disabled ) : ?>
		<section class="wpps-2fa-profile__card" aria-labelledby="wpps-2fa-methods-heading">
			<h3 class="wpps-2fa-profile__card-title" id="wpps-2fa-methods-heading"><?php esc_html_e( 'Verification methods', 'wp-powersuite' ); ?></h3>
			<p class="wpps-2fa-profile__lead description"><?php esc_html_e( 'Check each method you want to use. If both are checked, you can choose which to use first at login and switch on the login page.', 'wp-powersuite' ); ?></p>
			<fieldset id="wpps-2fa-profile-section" class="wpps-2fa-profile__fieldset">
				<legend class="screen-reader-text"><?php esc_html_e( 'Verification methods', 'wp-powersuite' ); ?></legend>
				<div class="wpps-2fa-profile__methods-group">
				<?php if ( $enable_email ) : ?>
				<div class="wpps-2fa-profile__method">
					<label class="wpps-2fa-profile__method-label">
						<input type="checkbox" name="wpps_2fa_use_email" value="1" <?php checked( $use_email ); ?> <?php echo ( $allow_edit_2fa && ! $two_fa_disabled ) ? '' : 'disabled'; ?> />
						<?php esc_html_e( 'Email code', 'wp-powersuite' ); ?>
					</label>
					<p class="description wpps-2fa-profile__method-desc"><?php esc_html_e( 'Receive an 8-digit code by email each time you log in.', 'wp-powersuite' ); ?></p>
				</div>
				<?php endif; ?>
				<?php if ( $enable_auth ) : ?>
				<div class="wpps-2fa-profile__method">
					<label class="wpps-2fa-profile__method-label">
						<input type="checkbox" name="wpps_2fa_use_authenticator" value="1" <?php checked( $use_auth ); ?> <?php echo ( ( $has_totp && $allow_edit_2fa ) ? '' : 'disabled' ); ?> />
						<?php esc_html_e( 'Authenticator app', 'wp-powersuite' ); ?>
					</label>
					<p class="description wpps-2fa-profile__method-desc"><?php esc_html_e( 'Use Google Authenticator, Authy, or similar app.', 'wp-powersuite' ); ?></p>
					<?php if ( $show_setup_auth ) : ?>
					<p class="wpps-2fa-profile__method-actions">
						<button type="button" class="button button-secondary" id="wpps-2fa-setup-auth"><?php esc_html_e( 'Set up authenticator app', 'wp-powersuite' ); ?></button>
					</p>
					<?php elseif ( $has_totp && $allow_edit_2fa ) : ?>
					<div class="wpps-2fa-profile__status-row">
						<span class="wpps-2fa-profile__badge wpps-2fa-profile__badge--success" aria-live="polite">
							<span class="wpps-2fa-profile__badge-icon" aria-hidden="true">✓</span>
							<?php esc_html_e( 'Authenticator configured', 'wp-powersuite' ); ?>
						</span>
						<button type="button" class="button button-small" id="wpps-2fa-remove-auth"><?php esc_html_e( 'Remove', 'wp-powersuite' ); ?></button>
					</div>
					<?php endif; ?>
				</div>
				<?php endif; ?>
				</div>
				<?php if ( $enable_email && $enable_auth && $allow_edit_2fa && ! $two_fa_disabled ) : ?>
				<div id="wpps-2fa-primary-wrap" class="wpps-2fa-profile__default-method wpps-2fa-primary-wrap" <?php echo $both_checked ? '' : 'style="display:none;"'; ?>>
					<p class="wpps-2fa-profile__default-method-title"><?php esc_html_e( 'Default method at login', 'wp-powersuite' ); ?></p>
					<p class="description wpps-2fa-profile__default-method-hint"><?php esc_html_e( 'Used first when both are enabled. You can switch on the login page.', 'wp-powersuite' ); ?></p>
					<div class="wpps-2fa-profile__radio-group">
						<label class="wpps-2fa-profile__radio">
							<input type="radio" name="wpps_2fa_method" value="email" <?php checked( $primary, 'email' ); ?> />
							<?php esc_html_e( 'Email code', 'wp-powersuite' ); ?>
						</label>
						<label class="wpps-2fa-profile__radio">
							<input type="radio" name="wpps_2fa_method" value="authenticator" <?php checked( $primary, 'authenticator' ); ?> />
							<?php esc_html_e( 'Authenticator app', 'wp-powersuite' ); ?>
						</label>
					</div>
				</div>
				<?php endif; ?>
				<?php if ( $allow_edit_2fa && ! $two_fa_disabled ) : ?>
				<div class="wpps-2fa-profile__danger-zone">
					<button type="button" class="button button-secondary" id="wpps-2fa-disable-for-user"><?php esc_html_e( 'Disable 2FA for this user', 'wp-powersuite' ); ?></button>
				</div>
				<?php endif; ?>
			</fieldset>
		</section>
		<?php if ( $show_backup_row ) : ?>
		<section class="wpps-2fa-profile__card" aria-labelledby="wpps-2fa-backup-heading">
			<h3 class="wpps-2fa-profile__card-title" id="wpps-2fa-backup-heading"><?php esc_html_e( 'Backup codes', 'wp-powersuite' ); ?></h3>
			<p class="description wpps-2fa-profile__lead"><?php esc_html_e( 'Use a backup code if you lose access to your authenticator app.', 'wp-powersuite' ); ?></p>
			<div class="wpps-2fa-profile__backup-row">
				<p class="wpps-2fa-profile__backup-count">
					<?php
					echo esc_html(
						sprintf(
							/* translators: %d: number of unused backup codes */
							__( '%d codes remaining', 'wp-powersuite' ),
							$backup_count
						)
					);
					?>
				</p>
				<p class="wpps-2fa-profile__backup-actions">
					<button type="button" class="button button-secondary" id="wpps-2fa-regenerate-backup"><?php esc_html_e( 'Regenerate backup codes', 'wp-powersuite' ); ?></button>
				</p>
			</div>
		</section>
		<?php endif; ?>
		<?php endif; ?>
		</div>

		<!-- Authenticator setup modal -->
		<div id="wpps-2fa-setup-modal" class="wpps-2fa-modal" style="display:none;">
			<div class="wpps-2fa-modal-content">
				<h3><?php esc_html_e( 'Set up authenticator app', 'wp-powersuite' ); ?></h3>
				<div id="wpps-2fa-setup-loading" class="wpps-2fa-setup-loading" style="display:none;min-height:220px;margin:24px 0;">
					<p class="description" style="margin:0;"><?php esc_html_e( 'Loading…', 'wp-powersuite' ); ?></p>
				</div>
				<div id="wpps-2fa-setup-body" style="display:none;">
					<p><?php esc_html_e( 'Scan the QR code with your authenticator app, or enter the key manually.', 'wp-powersuite' ); ?></p>
					<div id="wpps-2fa-qr-wrap" class="wpps-2fa-qr-wrap" style="margin:16px 0;">
						<img id="wpps-2fa-qr-img" src="" alt="" style="max-width:200px;height:auto;display:none;" />
						<p id="wpps-2fa-qr-fallback" class="description" style="display:none;margin:0;"><?php esc_html_e( 'QR image could not be generated on this server (PHP GD may be missing). Use the manual key below to add the account in your app.', 'wp-powersuite' ); ?></p>
					</div>
					<p>
						<label><?php esc_html_e( 'Manual key:', 'wp-powersuite' ); ?></label><br>
						<code id="wpps-2fa-secret-key" style="font-size:14px;letter-spacing:1px;word-break:break-all;" aria-label="<?php esc_attr_e( 'Secret key for manual entry', 'wp-powersuite' ); ?>"></code>
						<button type="button" class="button" id="wpps-2fa-copy-secret"><?php esc_html_e( 'Copy', 'wp-powersuite' ); ?></button>
						<span class="description" style="display:block;margin-top:6px;"><?php esc_html_e( 'If you can\'t scan the QR code, paste or type this key into your authenticator app when adding an account manually.', 'wp-powersuite' ); ?></span>
					</p>
					<p>
						<label for="wpps-2fa-verify-code"><?php esc_html_e( 'Enter the 6-digit code from your app to verify:', 'wp-powersuite' ); ?></label><br>
						<input type="text" id="wpps-2fa-verify-code" maxlength="6" pattern="[0-9]{6}" inputmode="numeric" autocomplete="one-time-code" style="width:120px;" />
					</p>
					<p>
						<button type="button" class="button button-primary" id="wpps-2fa-confirm-setup"><?php esc_html_e( 'Verify and enable', 'wp-powersuite' ); ?></button>
						<button type="button" class="button" id="wpps-2fa-cancel-setup"><?php esc_html_e( 'Cancel', 'wp-powersuite' ); ?></button>
					</p>
				</div>
				<div id="wpps-2fa-setup-error" class="notice notice-error" style="display:none;margin-top:12px;"></div>
			</div>
		</div>

		<!-- Backup codes modal -->
		<div id="wpps-2fa-backup-modal" class="wpps-2fa-modal" style="display:none;">
			<div class="wpps-2fa-modal-content">
				<h3><?php esc_html_e( 'Backup codes', 'wp-powersuite' ); ?></h3>
				<p class="description"><?php esc_html_e( 'Save these codes in a secure place. Each can only be used once.', 'wp-powersuite' ); ?></p>
				<div id="wpps-2fa-backup-codes-list" style="font-family:monospace;margin:16px 0;line-height:1.8;"></div>
				<p style="margin:12px 0 0 0;">
					<button type="button" class="button" id="wpps-2fa-copy-backup"><?php esc_html_e( 'Copy all', 'wp-powersuite' ); ?></button>
					<button type="button" class="button" id="wpps-2fa-export-backup"><?php esc_html_e( 'Export as file', 'wp-powersuite' ); ?></button>
					<button type="button" class="button button-primary" id="wpps-2fa-close-backup"><?php esc_html_e( 'I have saved these codes', 'wp-powersuite' ); ?></button>
				</p>
			</div>
		</div>
		<?php
	}

	public function saveSection( int $user_id ): void {
		if ( ! current_user_can( 'edit_user', $user_id ) ) {
			return;
		}
		$user = get_user_by( 'ID', $user_id );
		if ( ! $user || ! $this->userHas2FARole( $user ) ) {
			return;
		}
		$use_email = isset( $_POST['wpps_2fa_use_email'] ) && $_POST['wpps_2fa_use_email'] === '1';
		$use_auth  = isset( $_POST['wpps_2fa_use_authenticator'] ) && $_POST['wpps_2fa_use_authenticator'] === '1';
		$has_totp  = self::getTotpSecret( $user_id ) !== '';
		if ( $use_auth && ! $has_totp ) {
			$use_auth = false;
		}
		if ( ! $use_email && ! $use_auth ) {
			$use_email = true;
		}
		update_user_meta( $user_id, self::USER_META_USE_EMAIL, $use_email ? '1' : '0' );
		update_user_meta( $user_id, self::USER_META_USE_AUTH, $use_auth ? '1' : '0' );
		$primary = isset( $_POST['wpps_2fa_method'] ) ? sanitize_text_field( wp_unslash( $_POST['wpps_2fa_method'] ) ) : '';
		if ( in_array( $primary, [ 'email', 'authenticator' ], true ) ) {
			update_user_meta( $user_id, self::USER_META_METHOD, $primary );
		} elseif ( $use_email && ! $use_auth ) {
			update_user_meta( $user_id, self::USER_META_METHOD, 'email' );
		} elseif ( $use_auth && ! $use_email ) {
			update_user_meta( $user_id, self::USER_META_METHOD, 'authenticator' );
		}
		update_user_meta( $user_id, 'wpps_2fa_profile_saved', '1' );
		delete_user_meta( $user_id, 'wpps_2fa_grace_started_at' );
	}

	public function enqueueAssets( string $hook ): void {
		if ( $hook !== 'profile.php' && $hook !== 'user-edit.php' ) {
			return;
		}
		$base = plugin_dir_url( __FILE__ );
		wp_enqueue_style( 'wpps-2fa-profile', $base . 'assets/two-factor-auth-profile.css', [], '1.3.2' );
		wp_enqueue_script( 'wpps-2fa-profile', $base . 'assets/two-factor-auth-profile.js', [ 'jquery' ], '1.3.2', true );
		wp_localize_script( 'wpps-2fa-profile', 'wpps2faProfile', [
			'ajaxUrl'  => admin_url( 'admin-ajax.php' ),
			'nonce'    => wp_create_nonce( 'wpps_2fa_profile' ),
			'userId'   => isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : get_current_user_id(),
		] );
	}

	private function canEditUser2FA( int $user_id ): bool {
		return ( (int) get_current_user_id() === $user_id ) || current_user_can( 'edit_user', $user_id );
	}

	public function ajaxSetupAuthenticator(): void {
		check_ajax_referer( 'wpps_2fa_profile', 'nonce' );
		$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
		if ( ! $user_id || ! $this->canEditUser2FA( $user_id ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid request.', 'wp-powersuite' ) ] );
		}
		if ( (int) get_current_user_id() !== $user_id ) {
			wp_send_json_error( [ 'message' => __( 'Only the user can set up their authenticator app.', 'wp-powersuite' ) ] );
		}
		$user = get_user_by( 'ID', $user_id );
		if ( ! $user || ! $this->userHas2FARole( $user ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid user.', 'wp-powersuite' ) ] );
		}
		$secret = TOTP::generateSecret();
		$uri    = TOTP::getProvisioningUri( $secret, $user->user_login, get_bloginfo( 'name' ) );
		set_transient(
			self::pendingSecretTransientKey( $user_id ),
			Crypto::encryptSecret( $secret ),
			self::PENDING_SECRET_TTL
		);
		delete_user_meta( $user_id, 'wpps_2fa_totp_secret_pending' );
		$payload = [
			'secret' => $secret,
			'uri'    => $uri,
		];
		$qr_data_uri = self::buildProvisioningQrDataUri( $uri );
		if ( $qr_data_uri !== null ) {
			$payload['qr_data_uri'] = $qr_data_uri;
		}
		wp_send_json_success( $payload );
	}

	/**
	 * Local QR PNG as a data URI (no third-party QR services; reuses Magic Login encoder when GD is available).
	 */
	private static function buildProvisioningQrDataUri( string $uri ): ?string {
		if ( ! function_exists( 'imagepng' ) || ! function_exists( 'imagecreatetruecolor' ) ) {
			return null;
		}
		$file = dirname( __DIR__ ) . '/magic-login/includes/class-qrcode.php';
		if ( ! is_readable( $file ) ) {
			return null;
		}
		require_once $file;
		if ( ! class_exists( 'WPPS_Magic_Login_QRCode', false ) ) {
			return null;
		}
		try {
			$qr = new \WPPS_Magic_Login_QRCode( $uri, [ 's' => 'qrm', 'w' => 200, 'h' => 200 ] );
			$im = $qr->render_image();
			if ( ! is_object( $im ) ) {
				return null;
			}
			ob_start();
			imagepng( $im );
			$png = (string) ob_get_clean();
			imagedestroy( $im );
			if ( $png === '' ) {
				return null;
			}
			return 'data:image/png;base64,' . base64_encode( $png );
		} catch ( \Throwable $e ) {
			return null;
		}
	}

	public function ajaxVerifyAuthenticator(): void {
		check_ajax_referer( 'wpps_2fa_profile', 'nonce' );
		$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
		$code = isset( $_POST['code'] ) ? preg_replace( '/\D/', '', wp_unslash( $_POST['code'] ) ) : '';
		if ( ! $user_id || ! $this->canEditUser2FA( $user_id ) || (int) get_current_user_id() !== $user_id || strlen( $code ) !== 6 ) {
			wp_send_json_error( [ 'message' => __( 'Invalid code.', 'wp-powersuite' ) ] );
		}
		$stored = get_transient( self::pendingSecretTransientKey( $user_id ) );
		if ( ! is_string( $stored ) || $stored === '' ) {
			$stored = get_user_meta( $user_id, 'wpps_2fa_totp_secret_pending', true );
		}
		$secret = is_string( $stored ) ? Crypto::decryptSecret( $stored ) : '';
		if ( ! $secret || ! self::verifyTotpCode( $user_id, $secret, $code ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid or expired code. Please try again.', 'wp-powersuite' ) ] );
		}
		delete_transient( self::pendingSecretTransientKey( $user_id ) );
		delete_user_meta( $user_id, 'wpps_2fa_totp_secret_pending' );
		update_user_meta( $user_id, self::USER_META_SECRET, Crypto::encryptSecret( $secret ) );
		update_user_meta( $user_id, self::USER_META_METHOD, 'authenticator' );
		$backup_codes = TOTP::generateBackupCodes( 10 );
		$hashed = array_map( 'wp_hash_password', $backup_codes );
		update_user_meta( $user_id, self::USER_META_BACKUP, wp_json_encode( $hashed ) );
		update_user_meta( $user_id, 'wpps_2fa_profile_saved', '1' );
		delete_user_meta( $user_id, 'wpps_2fa_grace_started_at' );
		wp_send_json_success( [ 'backup_codes' => $backup_codes ] );
	}

	public function ajaxSwitchMethod(): void {
		check_ajax_referer( 'wpps_2fa_profile', 'nonce' );
		$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
		$method = isset( $_POST['method'] ) ? sanitize_text_field( wp_unslash( $_POST['method'] ) ) : '';
		if ( ! $user_id || ! $this->canEditUser2FA( $user_id ) || ! in_array( $method, [ 'email', 'authenticator' ], true ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid request.', 'wp-powersuite' ) ] );
		}
		if ( $method === 'authenticator' && ! get_user_meta( $user_id, self::USER_META_SECRET, true ) ) {
			wp_send_json_error( [ 'message' => __( 'Set up authenticator first.', 'wp-powersuite' ) ] );
		}
		update_user_meta( $user_id, self::USER_META_METHOD, $method );
		wp_send_json_success();
	}

	public function ajaxRegenerateBackupCodes(): void {
		check_ajax_referer( 'wpps_2fa_profile', 'nonce' );
		$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
		if ( ! $user_id || ! $this->canEditUser2FA( $user_id ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid request.', 'wp-powersuite' ) ] );
		}
		if ( ! get_user_meta( $user_id, self::USER_META_SECRET, true ) ) {
			wp_send_json_error( [ 'message' => __( 'Authenticator not set up.', 'wp-powersuite' ) ] );
		}
		$backup_codes = TOTP::generateBackupCodes( 10 );
		$hashed = array_map( 'wp_hash_password', $backup_codes );
		update_user_meta( $user_id, self::USER_META_BACKUP, wp_json_encode( $hashed ) );
		wp_send_json_success( [ 'backup_codes' => $backup_codes ] );
	}

	public function ajaxRemoveAuthenticator(): void {
		check_ajax_referer( 'wpps_2fa_profile', 'nonce' );
		$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
		if ( ! $user_id || ! $this->canEditUser2FA( $user_id ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid request.', 'wp-powersuite' ) ] );
		}
		delete_user_meta( $user_id, self::USER_META_SECRET );
		delete_user_meta( $user_id, self::USER_META_BACKUP );
		update_user_meta( $user_id, self::USER_META_METHOD, 'email' );
		wp_send_json_success();
	}

	public function ajaxDisable2FAForUser(): void {
		check_ajax_referer( 'wpps_2fa_profile', 'nonce' );
		$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
		if ( ! $user_id || ! $this->canEditUser2FA( $user_id ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid request.', 'wp-powersuite' ) ] );
		}
		update_user_meta( $user_id, 'wpps_2fa_disabled', '1' );
		wp_send_json_success();
	}

	public function ajaxEnable2FAForUser(): void {
		check_ajax_referer( 'wpps_2fa_profile', 'nonce' );
		$user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0;
		if ( ! $user_id || ! $this->canEditUser2FA( $user_id ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid request.', 'wp-powersuite' ) ] );
		}
		delete_user_meta( $user_id, 'wpps_2fa_disabled' );
		wp_send_json_success();
	}

	private function getModule(): Module {
		$manager = \WP_PowerSuite\Core\ModuleManager::getInstance();
		$module = $manager->getModule( 'two-factor-authentication' );
		if ( ! $module instanceof Module ) {
			throw new \RuntimeException( '2FA module not loaded' );
		}
		return $module;
	}

	private function userHas2FARole( \WP_User $user ): bool {
		$settings = $this->getModule()->getSettings();
		$roles = $settings['roles'] ?? [];
		$user_roles = (array) $user->roles;
		return ! empty( array_intersect( $user_roles, $roles ) );
	}

	public static function userUsesEmail( int $user_id ): bool {
		$v = get_user_meta( $user_id, self::USER_META_USE_EMAIL, true );
		if ( $v === '' ) {
			$method = get_user_meta( $user_id, self::USER_META_METHOD, true );
			return ( $method === 'email' || $method === '' );
		}
		return ( $v === '1' || $v === true );
	}

	public static function userUsesAuthenticator( int $user_id ): bool {
		$v = get_user_meta( $user_id, self::USER_META_USE_AUTH, true );
		if ( $v === '' ) {
			$method = get_user_meta( $user_id, self::USER_META_METHOD, true );
			return ( $method === 'authenticator' && (bool) get_user_meta( $user_id, self::USER_META_SECRET, true ) );
		}
		return ( $v === '1' || $v === true );
	}

	public static function getUserMethod( int $user_id ): string {
		$use_email = self::userUsesEmail( $user_id );
		$use_auth  = self::userUsesAuthenticator( $user_id );
		if ( $use_email && $use_auth ) {
			$method = get_user_meta( $user_id, self::USER_META_METHOD, true );
			return in_array( $method, [ 'email', 'authenticator' ], true ) ? $method : 'email';
		}
		if ( $use_auth ) {
			return 'authenticator';
		}
		return 'email';
	}

	public static function getTotpSecret( int $user_id ): string {
		$stored = (string) get_user_meta( $user_id, self::USER_META_SECRET, true );
		if ( $stored === '' ) {
			return '';
		}
		$secret = Crypto::decryptSecret( $stored );
		if ( $secret !== '' && ! str_starts_with( $stored, Crypto::ENC_PREFIX ) ) {
			update_user_meta( $user_id, self::USER_META_SECRET, Crypto::encryptSecret( $secret ) );
		}

		return $secret;
	}

	/**
	 * Verify TOTP at login/profile and reject replay within the acceptance window.
	 */
	public static function verifyTotpCode( int $user_id, string $secret, string $code ): bool {
		$step = TOTP::verifyAndGetTimeStep( $secret, $code );
		if ( $step === null ) {
			return false;
		}
		$last = (int) get_user_meta( $user_id, self::USER_META_TOTP_STEP, true );
		if ( $last === $step ) {
			return false;
		}
		update_user_meta( $user_id, self::USER_META_TOTP_STEP, $step );

		return true;
	}

	private static function pendingSecretTransientKey( int $user_id ): string {
		return 'wpps_2fa_pending_' . $user_id;
	}

	public static function verifyBackupCode( int $user_id, string $code ): bool {
		$backup = get_user_meta( $user_id, self::USER_META_BACKUP, true );
		$hashed = is_string( $backup ) ? json_decode( $backup, true ) : [];
		if ( ! is_array( $hashed ) ) {
			return false;
		}
		$code = strtoupper( preg_replace( '/\s/', '', $code ) );
		foreach ( $hashed as $i => $hash ) {
			if ( wp_check_password( $code, $hash ) ) {
				unset( $hashed[ $i ] );
				update_user_meta( $user_id, self::USER_META_BACKUP, wp_json_encode( array_values( $hashed ) ) );
				return true;
			}
		}
		return false;
	}
}
