<?php
/**
 * Two-Factor Authentication (2FA) module (ASE Pro).
 *
 * Forked from the Two-Factor plugin v0.14.2 and embedded into ASE Pro.
 * Only Email, TOTP and Recovery Codes providers are included.
 *
 * @since 8.2.4
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Detect if the standalone Two-Factor plugin is active (single site or network) or already loaded.
 *
 * @since 8.2.4
 *
 * @return bool
 */
function asenha_is_standalone_two_factor_active__premium_only() {
	// If the class is already loaded, we must not load our fork to avoid fatal redeclarations.
	if ( class_exists( 'Two_Factor_Core', false ) || defined( 'TWO_FACTOR_VERSION' ) ) {
		return true;
	}

	$plugin_basename = 'two-factor/two-factor.php';

	$active_plugins = (array) get_option( 'active_plugins', array() );
	if ( in_array( $plugin_basename, $active_plugins, true ) ) {
		return true;
	}

	$network_active_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
	if ( isset( $network_active_plugins[ $plugin_basename ] ) ) {
		return true;
	}

	return false;
}

/**
 * Admin notice shown when a conflict is detected with the standalone Two-Factor plugin.
 *
 * @since 8.2.4
 *
 * @return void
 */
function asenha_two_factor_conflict_notice__premium_only() {
	if ( ! current_user_can( 'manage_options' ) ) {
		return;
	}

	$message = __(
		'ASE Pro: Two-Factor Authentication (2FA) is not loaded because the standalone “Two-Factor” plugin is active. Please deactivate the standalone plugin to avoid conflicts.',
		'admin-site-enhancements'
	);

	echo '<div class="notice notice-warning"><p>' . esc_html( $message ) . '</p></div>';
}

/**
 * Get the ASE 2FA provider map.
 *
 * @since 8.5.3
 *
 * @return array
 */
function asenha_two_factor_get_provider_map__premium_only() {
	return array(
		'totp'           => 'Two_Factor_Totp',
		'recovery_codes' => 'Two_Factor_Backup_Codes',
		'email'          => 'Two_Factor_Email',
	);
}

/**
 * Get the default ASE 2FA provider keys.
 *
 * @since 8.5.3
 *
 * @return array
 */
function asenha_two_factor_get_default_provider_keys__premium_only() {
	return array( 'totp', 'recovery_codes', 'email' );
}

/**
 * Get the allowed email token TTL values.
 *
 * @since 8.5.3
 *
 * @return array
 */
function asenha_two_factor_get_allowed_email_token_ttls__premium_only() {
	return array( 30, 60, 90, 180, 300, 600 );
}

/**
 * Get the allowed grace period values in days.
 *
 * @since 8.5.3
 *
 * @return array
 */
function asenha_two_factor_get_allowed_grace_days__premium_only() {
	return array( 0, 1, 3, 7, 14, 30, 60, 90 );
}

/**
 * Normalize 2FA provider keys.
 *
 * @since 8.5.3
 *
 * @param array $selected Selected provider keys.
 * @return array
 */
function asenha_two_factor_normalize_provider_keys__premium_only( $selected ) {
	$selected = is_array( $selected ) ? $selected : array();
	$allowed  = asenha_two_factor_get_default_provider_keys__premium_only();

	$selected = array_map(
		static function( $provider_key ) {
			return ( 'backup_codes' === $provider_key ) ? 'recovery_codes' : $provider_key;
		},
		$selected
	);

	$selected = array_values( array_intersect( $selected, $allowed ) );

	if ( empty( $selected ) ) {
		$selected = array( 'email' );
	}

	return $selected;
}

/**
 * Get the selected 2FA settings mode.
 *
 * @since 8.5.3
 *
 * @return string
 */
function asenha_two_factor_get_settings_mode__premium_only() {
	$options = get_option( ASENHA_SLUG_U, array() );
	$mode    = isset( $options['two_factor_settings_mode'] ) ? sanitize_key( $options['two_factor_settings_mode'] ) : 'same_for_all_roles';

	return in_array( $mode, array( 'same_for_all_roles', 'different_per_role' ), true ) ? $mode : 'same_for_all_roles';
}

/**
 * Get the shared 2FA settings.
 *
 * @since 8.5.3
 *
 * @param array|null $options Plugin options.
 * @return array
 */
function asenha_two_factor_get_shared_config__premium_only( $options = null ) {
	if ( ! is_array( $options ) ) {
		$options = get_option( ASENHA_SLUG_U, array() );
	}

	$ttl        = isset( $options['two_factor_email_token_ttl'] ) ? absint( $options['two_factor_email_token_ttl'] ) : 60;
	$grace_days = isset( $options['two_factor_enforce_grace_days'] ) ? absint( $options['two_factor_enforce_grace_days'] ) : 14;

	if ( ! in_array( $ttl, asenha_two_factor_get_allowed_email_token_ttls__premium_only(), true ) ) {
		$ttl = 60;
	}

	if ( ! in_array( $grace_days, asenha_two_factor_get_allowed_grace_days__premium_only(), true ) ) {
		$grace_days = 14;
	}

	return array(
		'role'                => '',
		'required'            => false,
		'available_providers' => asenha_two_factor_normalize_provider_keys__premium_only(
			isset( $options['two_factor_available_providers'] ) ? $options['two_factor_available_providers'] : array()
		),
		'email_token_ttl'     => $ttl,
		'grace_days'          => $grace_days,
		'email_auto_enable'   => ! empty( $options['two_factor_email_auto_enable'] ),
	);
}

/**
 * Calculate a privilege score for the role.
 *
 * @since 8.5.3
 *
 * @param string $role_slug Role slug.
 * @return float
 */
function asenha_two_factor_get_role_priority__premium_only( $role_slug ) {
	$role = get_role( $role_slug );

	if ( ! $role instanceof WP_Role ) {
		return 0;
	}

	$weighted_caps = array(
		'manage_network_options' => 10000,
		'manage_sites'           => 9500,
		'manage_network_plugins' => 9400,
		'manage_options'         => 9000,
		'edit_users'             => 8000,
		'list_users'             => 7900,
		'promote_users'          => 7800,
		'delete_users'           => 7700,
		'create_users'           => 7600,
		'edit_others_posts'      => 7000,
		'publish_posts'          => 6000,
		'edit_posts'             => 5000,
		'read'                   => 1000,
	);

	$score = 0;

	foreach ( $weighted_caps as $capability => $weight ) {
		if ( ! empty( $role->capabilities[ $capability ] ) ) {
			$score = max( $score, $weight );
		}
	}

	$score += count( array_filter( $role->capabilities ) ) / 1000;

	/**
	 * Filter the calculated priority score for a role in ASE 2FA per-role mode.
	 *
	 * @since 8.5.3
	 *
	 * @param float   $score    Role priority score.
	 * @param string  $role_slug Role slug.
	 * @param WP_Role $role     Role object.
	 */
	return (float) apply_filters( 'asenha_two_factor_role_priority', $score, $role_slug, $role );
}

/**
 * Get the most privileged matching role for the user.
 *
 * @since 8.5.3
 *
 * @param WP_User $user          User object.
 * @param array   $eligible_roles Eligible role slugs.
 * @return string
 */
function asenha_two_factor_get_most_privileged_role__premium_only( $user, $eligible_roles ) {
	$eligible_roles = array_values( array_intersect( (array) $user->roles, (array) $eligible_roles ) );

	if ( empty( $eligible_roles ) ) {
		return '';
	}

	usort(
		$eligible_roles,
		static function( $role_a, $role_b ) {
			$priority_a = asenha_two_factor_get_role_priority__premium_only( $role_a );
			$priority_b = asenha_two_factor_get_role_priority__premium_only( $role_b );

			if ( $priority_a === $priority_b ) {
				return strcasecmp( (string) $role_a, (string) $role_b );
			}

			return ( $priority_a > $priority_b ) ? -1 : 1;
		}
	);

	return (string) $eligible_roles[0];
}

/**
 * Resolve the effective 2FA configuration for the user.
 *
 * @since 8.5.3
 *
 * @param WP_User|int|null $user User object or ID.
 * @return array
 */
function asenha_two_factor_get_effective_config_for_user__premium_only( $user = null ) {
	if ( null === $user ) {
		$user = wp_get_current_user();
	} elseif ( is_numeric( $user ) ) {
		$user = get_user_by( 'id', absint( $user ) );
	}

	$options       = get_option( ASENHA_SLUG_U, array() );
	$shared_config = asenha_two_factor_get_shared_config__premium_only( $options );
	$mode          = asenha_two_factor_get_settings_mode__premium_only();

	if ( ! $user instanceof WP_User ) {
		return $shared_config;
	}

	if ( 'different_per_role' !== $mode ) {
		$roles_map = isset( $options['two_factor_enforce_for'] ) && is_array( $options['two_factor_enforce_for'] ) ? $options['two_factor_enforce_for'] : array();

		foreach ( (array) $user->roles as $role_slug ) {
			if ( ! empty( $roles_map[ $role_slug ] ) ) {
				$shared_config['required'] = true;
				break;
			}
		}

		return $shared_config;
	}

	$role_settings  = isset( $options['two_factor_role_settings'] ) && is_array( $options['two_factor_role_settings'] ) ? $options['two_factor_role_settings'] : array();
	$eligible_roles = array();

	foreach ( (array) $user->roles as $role_slug ) {
		if ( ! empty( $role_settings[ $role_slug ]['enabled'] ) ) {
			$eligible_roles[] = $role_slug;
		}
	}

	if ( empty( $eligible_roles ) ) {
		return $shared_config;
	}

	$effective_role = asenha_two_factor_get_most_privileged_role__premium_only( $user, $eligible_roles );

	if ( empty( $effective_role ) || empty( $role_settings[ $effective_role ] ) || ! is_array( $role_settings[ $effective_role ] ) ) {
		return $shared_config;
	}

	$effective_role_settings = $role_settings[ $effective_role ];
	$ttl                     = isset( $effective_role_settings['email_token_ttl'] ) ? absint( $effective_role_settings['email_token_ttl'] ) : 60;
	$grace_days              = isset( $effective_role_settings['grace_days'] ) ? absint( $effective_role_settings['grace_days'] ) : 14;

	if ( ! in_array( $ttl, asenha_two_factor_get_allowed_email_token_ttls__premium_only(), true ) ) {
		$ttl = 60;
	}

	if ( ! in_array( $grace_days, asenha_two_factor_get_allowed_grace_days__premium_only(), true ) ) {
		$grace_days = 14;
	}

	return array(
		'role'                => $effective_role,
		'required'            => true,
		'available_providers' => asenha_two_factor_normalize_provider_keys__premium_only(
			isset( $effective_role_settings['available_providers'] ) ? $effective_role_settings['available_providers'] : array()
		),
		'email_token_ttl'     => $ttl,
		'grace_days'          => $grace_days,
		'email_auto_enable'   => ! empty( $effective_role_settings['email_auto_enable'] ),
	);
}

/**
 * Filter Two-Factor providers to only the methods selected in ASE settings.
 *
 * @since 8.5.3
 *
 * @param array $providers A key-value array where the key is the class name, and the value is the path.
 * @return array
 */
function asenha_two_factor_filter_providers__premium_only( $providers ) {
	$providers = is_array( $providers ) ? $providers : array();

	if ( 'different_per_role' === asenha_two_factor_get_settings_mode__premium_only() ) {
		return $providers;
	}

	$selected = asenha_two_factor_get_shared_config__premium_only()['available_providers'];
	$map      = asenha_two_factor_get_provider_map__premium_only();

	$filtered = array();

	foreach ( $selected as $key ) {
		if ( isset( $map[ $key ] ) && isset( $providers[ $map[ $key ] ] ) ) {
			$filtered[ $map[ $key ] ] = $providers[ $map[ $key ] ];
		}
	}

	// Always keep at least one provider available to avoid locking users out.
	if ( empty( $filtered ) && isset( $providers['Two_Factor_Email'] ) ) {
		$filtered['Two_Factor_Email'] = $providers['Two_Factor_Email'];
	}

	return $filtered;
}

/**
 * Filter available Two-Factor providers per user in per-role mode.
 *
 * @since 8.2.4
 *
 * @param array       $providers Provider instances.
 * @param int|WP_User $user      User object or ID.
 * @return array
 */
function asenha_two_factor_filter_providers_for_user__premium_only( $providers, $user ) {
	if ( 'different_per_role' !== asenha_two_factor_get_settings_mode__premium_only() ) {
		return $providers;
	}

	$providers = is_array( $providers ) ? $providers : array();
	$config    = asenha_two_factor_get_effective_config_for_user__premium_only( $user );

	if ( empty( $config['required'] ) ) {
		return array();
	}

	$map      = asenha_two_factor_get_provider_map__premium_only();
	$filtered = array();

	foreach ( $config['available_providers'] as $provider_slug ) {
		if ( isset( $map[ $provider_slug ] ) && isset( $providers[ $map[ $provider_slug ] ] ) ) {
			$filtered[ $map[ $provider_slug ] ] = $providers[ $map[ $provider_slug ] ];
		}
	}

	if ( empty( $filtered ) && isset( $providers['Two_Factor_Email'] ) ) {
		$filtered['Two_Factor_Email'] = $providers['Two_Factor_Email'];
	}

	return $filtered;
}

/**
 * Filter email token TTL used by the Email provider.
 *
 * @since 8.2.4
 *
 * @param int $ttl     TTL in seconds.
 * @param int $user_id User ID.
 * @return int
 */
function asenha_two_factor_email_token_ttl__premium_only( $ttl, $user_id ) {
	$config = asenha_two_factor_get_effective_config_for_user__premium_only( $user_id );
	$ttl    = isset( $config['email_token_ttl'] ) ? absint( $config['email_token_ttl'] ) : 60;

	$allowed = asenha_two_factor_get_allowed_email_token_ttls__premium_only();
	if ( ! in_array( $ttl, $allowed, true ) ) {
		$ttl = 60;
	}

	return $ttl;
}

/**
 * Check whether the current user is required to enable 2FA.
 *
 * @since 8.2.4
 *
 * @return bool
 */
function asenha_two_factor_is_required_for_current_user__premium_only() {
	$user_id = get_current_user_id();
	if ( ! $user_id ) {
		return false;
	}

	$user = get_user_by( 'id', $user_id );
	if ( ! $user instanceof WP_User ) {
		return false;
	}

	return asenha_two_factor_is_required_for_user__premium_only( $user );
}

/**
 * Check whether a specific user is required to enable 2FA.
 *
 * @since 8.2.4
 *
 * @param WP_User|int $user User object or user ID.
 * @return bool
 */
function asenha_two_factor_is_required_for_user__premium_only( $user ) {
	$config = asenha_two_factor_get_effective_config_for_user__premium_only( $user );

	return ! empty( $config['required'] );
}

/**
 * Determine whether to show Two-Factor options UI for a user.
 *
 * @since 8.2.4
 *
 * @param bool    $show Whether the UI should be shown.
 * @param WP_User $user The user being edited.
 * @return bool
 */
function asenha_two_factor_show_user_options__premium_only( $show, $user ) {
	return asenha_two_factor_is_required_for_user__premium_only( $user );
}

/**
 * Whether forced Email enrollment is active for the user (ASE setting + enforcement + Email available).
 *
 * @since 8.5.3
 *
 * @param WP_User|int $user User object or ID.
 * @return bool
 */
function asenha_two_factor_is_forced_email_enrollment_active_for_user__premium_only( $user ) {
	if ( is_numeric( $user ) ) {
		$user = get_user_by( 'id', absint( $user ) );
	}

	if ( ! $user instanceof WP_User ) {
		return false;
	}

	if ( ! asenha_two_factor_is_required_for_user__premium_only( $user ) ) {
		return false;
	}

	$config = asenha_two_factor_get_effective_config_for_user__premium_only( $user );

	if ( empty( $config['email_auto_enable'] ) ) {
		return false;
	}

	if ( ! in_array( 'email', $config['available_providers'], true ) ) {
		return false;
	}

	return true;
}

/**
 * Enable Email 2FA and set primary to Email when multiple methods are supported.
 *
 * @since 8.5.3
 *
 * @param int $user_id User ID.
 * @return void
 */
function asenha_two_factor_apply_forced_email_enrollment__premium_only( $user_id ) {
	$user_id = absint( $user_id );
	if ( ! $user_id || ! class_exists( 'Two_Factor_Core', false ) ) {
		return;
	}

	$user = get_user_by( 'id', $user_id );
	if ( ! $user instanceof WP_User ) {
		return;
	}

	if ( ! asenha_two_factor_is_forced_email_enrollment_active_for_user__premium_only( $user ) ) {
		return;
	}

	Two_Factor_Core::enable_provider_for_user( $user_id, 'Two_Factor_Email' );

	$stored_primary = get_user_meta( $user_id, Two_Factor_Core::PROVIDER_USER_META_KEY, true );
	$supported      = Two_Factor_Core::get_supported_providers_for_user( $user_id );

	if ( is_array( $supported ) && count( $supported ) > 1 && ( '' === $stored_primary || false === $stored_primary ) ) {
		update_user_meta( $user_id, Two_Factor_Core::PROVIDER_USER_META_KEY, 'Two_Factor_Email' );
	}
}

/**
 * Run forced Email enrollment when an affected user profile screen loads in wp-admin.
 *
 * @since 8.5.3
 *
 * @return void
 */
function asenha_two_factor_forced_email_on_profile_load__premium_only() {
	if ( ! is_admin() || wp_doing_ajax() ) {
		return;
	}

	global $pagenow;

	$user_id = 0;

	if ( 'profile.php' === $pagenow ) {
		$user_id = get_current_user_id();
	} elseif ( 'user-edit.php' === $pagenow && isset( $_GET['user_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$user_id = absint( wp_unslash( $_GET['user_id'] ) );
	}

	if ( ! $user_id ) {
		return;
	}

	asenha_two_factor_apply_forced_email_enrollment__premium_only( $user_id );
}

/**
 * Lock the Email provider checkbox on the user’s own profile when forced enrollment applies.
 *
 * @since 8.5.3
 *
 * @param bool    $locked       Whether the checkbox is locked.
 * @param string  $provider_key Provider class name.
 * @param WP_User $user         User being edited.
 * @return bool
 */
function asenha_two_factor_lock_email_provider_checkbox__premium_only( $locked, $provider_key, $user ) {
	if ( $locked ) {
		return $locked;
	}

	if ( 'Two_Factor_Email' !== $provider_key ) {
		return $locked;
	}

	if ( get_current_user_id() !== (int) $user->ID ) {
		return $locked;
	}

	if ( ! asenha_two_factor_is_forced_email_enrollment_active_for_user__premium_only( $user ) ) {
		return $locked;
	}

	return true;
}

/**
 * Ensure Email stays in enabled providers on save when forced enrollment applies (server-side safety net).
 *
 * @since 8.5.3
 *
 * @param array $enabled_providers Enabled provider class names.
 * @param int   $user_id          User ID.
 * @param array $providers        Supported providers (class => path or instance map from core).
 * @return array
 */
function asenha_two_factor_merge_email_on_enabled_providers_save__premium_only( $enabled_providers, $user_id, $providers ) {
	$user = get_user_by( 'id', absint( $user_id ) );
	if ( ! $user instanceof WP_User ) {
		return $enabled_providers;
	}

	if ( ! asenha_two_factor_is_forced_email_enrollment_active_for_user__premium_only( $user ) ) {
		return $enabled_providers;
	}

	if ( ! is_array( $providers ) || ! isset( $providers['Two_Factor_Email'] ) ) {
		return $enabled_providers;
	}

	$enabled_providers = is_array( $enabled_providers ) ? $enabled_providers : array();

	if ( ! in_array( 'Two_Factor_Email', $enabled_providers, true ) ) {
		$enabled_providers[] = 'Two_Factor_Email';
	}

	$enabled_providers = array_values( array_intersect( $enabled_providers, array_keys( $providers ) ) );

	if ( ! in_array( 'Two_Factor_Email', $enabled_providers, true ) ) {
		$enabled_providers[] = 'Two_Factor_Email';
	}

	return $enabled_providers;
}

/**
 * Get the grace-period duration in seconds.
 *
 * @since 8.2.4
 *
 * @return int
 */
function asenha_two_factor_get_grace_period_seconds__premium_only( $user = null ) {
	$config = asenha_two_factor_get_effective_config_for_user__premium_only( $user );

	if ( empty( $config['required'] ) ) {
		return 0;
	}

	$days = isset( $config['grace_days'] ) ? absint( $config['grace_days'] ) : 14;

	if ( ! in_array( $days, asenha_two_factor_get_allowed_grace_days__premium_only(), true ) ) {
		$days = 14;
	}

	return $days * DAY_IN_SECONDS;
}

/**
 * Enforce 2FA in wp-admin after grace period by redirecting to Profile.
 *
 * @since 8.2.4
 *
 * @return void
 */
function asenha_two_factor_enforce_in_admin__premium_only() {
	if ( ! is_admin() || wp_doing_ajax() ) {
		return;
	}

	$user_id = get_current_user_id();
	if ( ! $user_id ) {
		return;
	}

	if ( ! asenha_two_factor_is_required_for_current_user__premium_only() ) {
		delete_user_meta( $user_id, 'asenha_2fa_grace_started_at' );
		return;
	}

	// If the user already has 2FA enabled/configured, clear grace tracking and allow.
	if ( class_exists( 'Two_Factor_Core' ) && Two_Factor_Core::is_user_using_two_factor( $user_id ) ) {
		delete_user_meta( $user_id, 'asenha_2fa_grace_started_at' );
		return;
	}

	$grace_seconds = asenha_two_factor_get_grace_period_seconds__premium_only( $user_id );

	$started_at = (int) get_user_meta( $user_id, 'asenha_2fa_grace_started_at', true );
	if ( ! $started_at ) {
		$started_at = time();
		update_user_meta( $user_id, 'asenha_2fa_grace_started_at', $started_at );
	}

	$expired = ( 0 === $grace_seconds ) ? true : ( time() > ( $started_at + $grace_seconds ) );

	// Allow access to profile + logout (and self user-edit) so the user can configure 2FA.
	global $pagenow;
	$allowed = array( 'profile.php', 'wp-login.php' );

	if ( in_array( $pagenow, $allowed, true ) ) {
		return;
	}

	if ( 'user-edit.php' === $pagenow ) {
		$editing_user_id = isset( $_GET['user_id'] ) ? absint( $_GET['user_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( $editing_user_id === $user_id ) {
			return;
		}
	}

	if ( ! $expired ) {
		return;
	}

	$forced_setup_url = add_query_arg(
		'asenha_2fa_required_setup',
		'1',
		self_admin_url( 'profile.php' )
	);

	wp_safe_redirect( $forced_setup_url . '#two-factor-options' );
	exit;
}

/**
 * Show a notice during grace period prompting the user to set up 2FA.
 *
 * @since 8.2.4
 *
 * @return void
 */
function asenha_two_factor_grace_notice__premium_only() {
	if ( ! is_admin() || wp_doing_ajax() ) {
		return;
	}

	$user_id = get_current_user_id();
	if ( ! $user_id ) {
		return;
	}

	if ( ! asenha_two_factor_is_required_for_current_user__premium_only() ) {
		return;
	}

	if ( class_exists( 'Two_Factor_Core' ) && Two_Factor_Core::is_user_using_two_factor( $user_id ) ) {
		return;
	}

	$grace_seconds = asenha_two_factor_get_grace_period_seconds__premium_only( $user_id );
	if ( 0 === $grace_seconds ) {
		// No grace period: enforcement will redirect on next admin_init.
		return;
	}

	$started_at = (int) get_user_meta( $user_id, 'asenha_2fa_grace_started_at', true );
	if ( ! $started_at ) {
		return;
	}

	$expires_at = $started_at + $grace_seconds;
	if ( time() > $expires_at ) {
		return;
	}

	$days_left = (int) ceil( max( 1, ( $expires_at - time() ) / DAY_IN_SECONDS ) );

	$message = sprintf(
		/* translators: %d: number of days remaining */
		__( 'Two-Factor Authentication (2FA) is required for your role. Please set up 2FA within %d day(s) to keep access to wp-admin enabled.', 'admin-site-enhancements' ),
		$days_left
	);

	$link = self_admin_url( 'profile.php#two-factor-options' );

	echo '<div class="notice notice-warning asenha-2fa-grace-notice"><p>' . esc_html( $message ) . ' <a href="' . esc_url( $link ) . '" class="button">' . esc_html__( 'Set Up Now', 'admin-site-enhancements' ) . '</a></p></div>';
}

/**
 * Initialize the embedded Two-Factor fork and ASE glue code.
 *
 * @since 8.2.4
 *
 * @return void
 */
function asenha_two_factor_authentication_init__premium_only() {
	if ( asenha_is_standalone_two_factor_active__premium_only() ) {
		add_action( 'admin_notices', 'asenha_two_factor_conflict_notice__premium_only' );
		add_action( 'network_admin_notices', 'asenha_two_factor_conflict_notice__premium_only' );
		return;
	}

	// Load the embedded Two-Factor plugin code (forked).
	require_once ASENHA_PATH . 'includes/premium/two-factor-authentication/two-factor/two-factor.php';

	// Enforce provider availability (Email/TOTP/Backup Codes only).
	add_filter( 'two_factor_providers', 'asenha_two_factor_filter_providers__premium_only', 999 );
	add_filter( 'two_factor_providers_for_user', 'asenha_two_factor_filter_providers_for_user__premium_only', 10, 2 );

	// Email token TTL.
	add_filter( 'two_factor_email_token_ttl', 'asenha_two_factor_email_token_ttl__premium_only', 10, 2 );

	// REST/XML-RPC application passwords restriction: optional.
	$options = get_option( ASENHA_SLUG_U, array() );
	$restrict_api = isset( $options['two_factor_restrict_api_login_to_app_passwords'] ) ? (bool) $options['two_factor_restrict_api_login_to_app_passwords'] : true;
	if ( ! $restrict_api ) {
		add_filter( 'two_factor_user_api_login_enable', '__return_true', 999, 2 );
	}

	// Enforcement in wp-admin.
	add_action( 'admin_init', 'asenha_two_factor_enforce_in_admin__premium_only', 0 );
	add_action( 'admin_notices', 'asenha_two_factor_grace_notice__premium_only' );

	// Only show 2FA options UI for required roles.
	add_filter( 'asenha_two_factor_show_user_options', 'asenha_two_factor_show_user_options__premium_only', 10, 2 );

	// Forced Email enrollment (auto-enable + locked checkbox on own profile + merge on save).
	add_action( 'admin_init', 'asenha_two_factor_forced_email_on_profile_load__premium_only', 5 );
	add_filter( 'two_factor_lock_enabled_provider_checkbox', 'asenha_two_factor_lock_email_provider_checkbox__premium_only', 10, 3 );
	add_filter( 'two_factor_enabled_providers_for_save', 'asenha_two_factor_merge_email_on_enabled_providers_save__premium_only', 10, 3 );
}

// Initialize after all plugins are loaded so we can reliably detect conflicts.
add_action( 'plugins_loaded', 'asenha_two_factor_authentication_init__premium_only', 20 );

