<?php
/**
 * Masteriyo Two-Factor Authentication Handler.
 *
 * Manages user authentication and handles OTP (One-Time Password) verification.
 *
 * @package Masteriyo\Addons\TwoFactorAuthentication
 *
 * @since   2.7.0
 */

namespace Masteriyo\Addons\TwoFactorAuthentication;

use Masteriyo\Addons\TwoFactorAuthentication\Enums\LoginLocations;
use Masteriyo\Addons\TwoFactorAuthentication\Enums\OTPMethods;
use Masteriyo\Enums\UserStatus;
use WP_Error;
use WP_User;

defined( 'ABSPATH' ) || exit;

/**
 * Handles user authentication and OTP verification.
 *
 * @since 2.7.0
 */
class TwoFactorAuthenticator {

	/**
	 * Initialize the class and set up hooks.
	 *
	 * @since 2.7.0
	 *
	 * @return void
	 */
	public static function init() {
		self::init_hooks();
	}

	/**
	 * Set up hooks related to user authentication.
	 *
	 * @since 2.7.0
	 *
	 * @return void
	 */
	private static function init_hooks() {
		add_filter( 'wp_authenticate_user', array( __CLASS__, 'verify_before_authentication' ), 9999, 2 );
	}

	/**
	 * Pre-authenticate a user and handle OTP if necessary.
	 *
	 * @since 2.7.0
	 *
	 * @param WP_User|WP_Error $user The user or error object.
	 * @param string $password The password entered.
	 *
	 * @return WP_User|WP_Error Modified user or error object.
	 */
	public static function verify_before_authentication( $user, $password ) {

		if ( is_wp_error( $user ) ) {
			return $user;
		}

		if ( ! wp_check_password( $password, $user->user_pass ) ) {
			return $user;
		}

		// Handle 2FA if required.
		return masteriyo_is_2fa_required( $user ) ? self::handle_two_factor_authentication( $user ) : $user;
	}

	/**
	 * Handle the two-factor authentication based on the OTP method and login location.
	 *
	 * @since 2.7.0
	 *
	 * @param WP_User $user The WordPress user object.
	 *
	 * @return WP_User|WP_Error Modified user or error object.
	 */
	private static function handle_two_factor_authentication( $user ) {

		$method   = Setting::get( 'method' );
		$location = Setting::get( 'location' );

		if ( OTPMethods::METHOD_EMAIL === $method ) {
			if ( LoginLocations::LOCATION_BOTH === $location || self::is_location_matched( $location ) ) {
				return self::handle_email_otp( $user );
			}
		}

		return $user;
	}

	/**
	 * Check if the given login location matches the current request location.
	 *
	 * @since 2.7.0
	 *
	 * @param string $location The configured login location.
	 *
	 * @return bool Whether the location matches.
	 */
	private static function is_location_matched( $location ) {
		return ( LoginLocations::LOCATION_MASTERIYO === $location && masteriyo_is_request_from_masteriyo_login() ) ||
		( LoginLocations::LOCATION_WP === $location && masteriyo_is_request_from_wp_login() );
	}

	/**
	 * Handle OTP via email.
	 *
	 * @since 2.7.0
	 *
	 * @param WP_User $user The WordPress user object.
	 *
	 * @return WP_User|WP_Error Modified user or error object.
	 */
	public static function handle_email_otp( $user ) {
		$masteriyo_user = masteriyo_get_user( $user );

		if ( is_null( $masteriyo_user ) || is_wp_error( $masteriyo_user ) ) {
			return $user;
		}

		if ( UserStatus::SPAM === $masteriyo_user->get_status() ) {
			return $user;
		}

		$can_resend_otp = masteriyo_can_update_otp_resend_count( $masteriyo_user );

		if ( is_wp_error( $can_resend_otp ) ) {
			return new WP_Error( $can_resend_otp->get_error_code(), $can_resend_otp->get_error_message() );
		}

		/**
		 * Fires before login OTP redirection.
		 *
		 * This action hook allows other parts of the system to hook into the OTP process before redirection.
		 *
		 * @since 2.7.0
		 *
		 * @param int $id User ID.
		 * @param \Masteriyo\Models\User $user User object.
		 */
		do_action( 'masteriyo_before_login_otp_redirection', $user->ID, $masteriyo_user );

		$redirect_url = self::get_redirect_url( $user );

		self::redirect_or_send_json( $redirect_url );
	}

	/**
	 * Retrieves the OTP redirection URL for a given user.
	 *
	 * @since 2.7.0
	 *
	 * @param WP_User $user The WordPress user object.
	 *
	 * @return string The OTP redirection URL.
	 */
	private static function get_redirect_url( WP_User $user ) {
		return add_query_arg( 'token', masteriyo_generate_otp_hash( $user->ID ), masteriyo_get_account_endpoint_url( 'otp' ) );
	}

	/**
	 * Redirects the user or sends a JSON success message based on the request origin.
	 *
	 * @since 2.7.0
	 *
	 * @param string $redirect_url The OTP redirection URL.
	 *
	 * @return void
	 */
	private static function redirect_or_send_json( $redirect_url ) {
		if ( masteriyo_is_request_from_wp_login() ) {
			wp_safe_redirect( $redirect_url );
			exit();
		}

		wp_send_json_success(
			array(
				'otp_required' => true,
				'otp_page_url' => $redirect_url,
			)
		);
	}
}
