<?php

namespace Prodigy\Includes;

use Prodigy\Includes\Content\Prodigy_Api_Client;
use Prodigy\Includes\Content\Prodigy_Request_Maker;
use Prodigy\Includes\Frontend\Cart_Error_Handler;
use Prodigy\Includes\Frontend\Pages\Prodigy_Page;
use Prodigy\Includes\Helpers\Prodigy_Cookies;
use Prodigy\Includes\Helpers\Prodigy_Logger;
use Prodigy\Includes\Helpers\Prodigy_Parameters;
use Prodigy\Includes\Helpers\Prodigy_Url_Helper;
use Prodigy\includes\models\Prodigy_Order;

/**
 * Class for user Authentication process
 *
 * @version    3.0.9
 * @package    Prodigy
 * @subpackage Prodigy/includes
 */
class Prodigy_User {

	public const AUTHORIZATION_COOKIE_NAME = 'wordpress_prodigy_user_auth_token';

	public const ACCESS_COOKIE_NAME = 'wordpress_prodigy_user_access_token';

	public const REFRESH_COOKIE_NAME = 'wordpress_prodigy_user_refresh_token';

	public const USER_INFO_COOKIE_NAME = 'wordpress_prodigy_user_name';

	public const USER_GROUP_COOKIE_NAME = 'wordpress_prodigy_user_group';

	public const USER_ID_COOKIE_NAME = 'wordpress_prodigy_user';

	public const USER_INFO_COOKIE_EMAIL = 'wordpress_prodigy_user_email';

	public const USER_INFO_COOKIE_LAST_NAME = 'wordpress_prodigy_user_last_name';

	public const USER_INFO_COOKIE_PHONE = 'wordpress_prodigy_user_phone';

	public const REQUIRE_LOGIN_VALUE = 'required';

	public const REQUIRE_LOGIN_DISABLED_VALUE = 'disabled';

	public const REQUIRE_LOGIN_OPTIONAL_VALUE = 'optional';

	public const CUSTOMER_BALANCE_OPTION = 'prodigy-user-customer-balance-option';

	public const SUBSCRIPTION_OPTION = 'prodigy-user-subscription-option';

	public const IMPERSONATION_COOKIE_ID = 'wordpress_prodigy_impersonation_id';

	public const PRODIGY_USER_PAIRED_SESSION_COOKIE_NAME = 'wordpress_prodigy_user_paired_session_token';

	public const PRODIGY_USER_GROUP_NAME = 'Guest';

	public const EXTERNAL_CHECKOUT_ID_COOKIE_NAME = 'wordpress_prodigy_external_checkout_id';

	public const GUEST_COOKIE_LIFETIME_SECONDS = DAY_IN_SECONDS;

	/**
	 * @var string|mixed|null
	 */
	public ?string $user_session;

	/**
	 * @throws \Exception
	 */
	public function __construct() {
		$this->user_session = Prodigy_Parameters::get_parameter( $_GET, 'session_token' );
		$this->set_external_checkout_id_cookie();
		$this->set_guest_user_cookie();
	}

	/**
	 * @return void
	 * @throws \Exception
	 */
	private function set_external_checkout_id_cookie(): void {
		$external_checkout_id = Prodigy_Parameters::get_parameter( $_GET, 'external_checkout_id' );
		if ( empty( $external_checkout_id ) || ! is_string( $external_checkout_id ) ) {
			return;
		}
		$external_checkout_id = sanitize_text_field( wp_unslash( $external_checkout_id ) );
		if ( $external_checkout_id === '' ) {
			return;
		}
		Prodigy_Cookies::set_cookie(
			self::EXTERNAL_CHECKOUT_ID_COOKIE_NAME,
			$external_checkout_id,
			time() + self::GUEST_COOKIE_LIFETIME_SECONDS,
			'/',
		);
		if ( PRODIGY_DEBUG_MODE ) {
			Prodigy_Logger::add_record(
				Prodigy_Logger::TYPE_INFO,
				'External checkout id cookie set',
				'prodigy-user',
				wp_json_encode(
					array(
						'cookie_name'          => self::EXTERNAL_CHECKOUT_ID_COOKIE_NAME,
						'external_checkout_id' => $external_checkout_id,
					)
				)
			);
		}
	}

	/**
	 * @throws \Exception
	 */
	private function set_guest_user_cookie(): void {
		if ( ! Prodigy_Cookies::get_cookie( self::USER_GROUP_COOKIE_NAME ) ) {
			Prodigy_Cookies::set_cookie(
				self::USER_GROUP_COOKIE_NAME,
				self::PRODIGY_USER_GROUP_NAME,
				time() + self::GUEST_COOKIE_LIFETIME_SECONDS,
				'/',
			);
		}
	}

	/**
	 * @return array
	 */
	public static function get_user_info(): array {
		$user_auth_hash = Prodigy_Cookies::get_cookie( self::AUTHORIZATION_COOKIE_NAME );

		return Prodigy_Request_Maker::get_instance()->do_user_info_request( $user_auth_hash );
	}

	/**
	 * @return string|null
	 */
	public static function get_user_session(): ?string {
		return self::is_logged_in() ? self::get_user_id() : Prodigy_Cookies::get_user_cart_cookie();
	}

	/**
	 * @return string|null
	 */
	public static function get_external_checkout_id(): ?string {
		$value = Prodigy_Cookies::get_cookie( self::EXTERNAL_CHECKOUT_ID_COOKIE_NAME );
		if ( PRODIGY_DEBUG_MODE ) {
			Prodigy_Logger::add_record(
				Prodigy_Logger::TYPE_INFO,
				'External checkout id cookie read',
				'prodigy-user',
				wp_json_encode(
					array(
						'cookie_name' => self::EXTERNAL_CHECKOUT_ID_COOKIE_NAME,
						'present'     => $value !== '',
					)
				)
			);
		}
		return $value !== '' ? $value : null;
	}

	/**
	 * @return int
	 */
	public static function get_user_id(): ?int {
		$user_hash = Prodigy_Cookies::get_cookie( self::USER_ID_COOKIE_NAME );
		return self::prodigy_decrypt_user_id( $user_hash ?? 0 );
	}

	/**
	 * @return bool
	 */
	public function is_logout_action(): bool {
		$action = Prodigy_Parameters::get_parameter( $_POST, 'action' );

		return $action === 'prodigy-user-logout';
	}

	/**
	 * @param   bool $user_logged_in
	 * @param   bool $require_login
	 * @param   bool $has_session_token
	 *
	 * @return bool
	 */
	public function should_return_early( bool $user_logged_in, bool $require_login, bool $has_session_token ): bool {
		return ( ! $has_session_token && ! $user_logged_in && ! $require_login ) || ! Helpers\Prodigy_Page::prodigy_is_need_user_authorise();
	}

	/**
	 * @param   bool   $require_login
	 * @param   string $request_uri
	 *
	 * @return void
	 * @throws \Exception
	 */
	public function handle_unauthenticated_user( bool $require_login, string $request_uri ): void {
		if ( $require_login && self::is_frontend_authentication_required( $request_uri ) ) {
			$this->set_user_require_authentication();
		} else {
			$this->process_login_redirect();
		}
	}

	/**
	 * @param   string $url
	 *
	 * @return bool
	 */
	public static function is_frontend_authentication_required( string $url ): bool {
		if ( wp_doing_ajax() ) {
			return false;
		}

		$result = false;
		$array  = array(
			Prodigy::get_prodigy_category_slug(),
			Prodigy::get_prodigy_tag_slug(),
			Prodigy::get_prodigy_brand_slug(),
			Prodigy::get_prodigy_product_slug(),
			Prodigy_Page::PAGE_TYPE_SHOP,
			Prodigy_Page::PAGE_TYPE_CART,
			Prodigy_Page::PAGE_TYPE_THANK,
		);

		$path = wp_parse_url( $url, PHP_URL_PATH );

		if ( $path === '/' ) {
			$result = true;
		}

		foreach ( $array as $value ) {
			if ( is_string( $path ) && str_contains( $path, $value ) ) {
				if ( str_contains( $path, $value ) ) {
					$result = true;
				}
			}
		}

		$result = $result && ( ! isset( $_SERVER['HTTP_SEC_FETCH_DEST'] ) || $_SERVER['HTTP_SEC_FETCH_DEST'] !== 'iframe' );

		if ( ! $result && ! is_admin() ) {
			return true;
		}

		return $result;
	}

	/**
	 * @return void
	 * @throws \Exception
	 */
	public function set_user_require_authentication(): void {
		if ( self::is_logged_in() ) {
			return;
		}

		if ( ! empty( $this->user_session ) ) {
			$this->process_login_redirect();
		}

		$url = self::get_hosted_system_login_url();
		Prodigy_Url_Helper::login_redirect( $url );
	}

	/**
	 * @return bool
	 */
	public static function is_logged_in(): bool {
		return isset( $_COOKIE[ self::AUTHORIZATION_COOKIE_NAME ], $_COOKIE[ self::ACCESS_COOKIE_NAME ] );
	}

	/**
	 * @return void
	 * @throws \Exception
	 */
	public function process_login_redirect(): void {
		$this->authorization();

		$cart = new Prodigy_Cart( Prodigy_Request_Maker::get_instance(), new Prodigy_Order(), new Cart_Error_Handler() );
		$cart->move_guest_order_to_customer_order();

		$request_uri  = Prodigy_Url_Helper::prodigy_parse_request_uri( - 1 );
		$redirect_url = $this->clear_url( $request_uri );
		Prodigy_Url_Helper::login_redirect( $redirect_url );
	}

	/**
	 *
	 * @return void
	 * @throws \Exception
	 */
	public function authorization(): void {
		$user_data = Prodigy_Request_Maker::get_instance()->do_user_request( $this->user_session );

		if ( isset( $user_data['meta']['jwt'] ) ) {
			$this->login( $user_data['meta']['jwt'] );
			$this->set_user_info( $user_data['data'], $user_data['meta']['jwt']['access_expires_at'] );
			Prodigy_Cookies::set_cookie(
				self::PRODIGY_USER_PAIRED_SESSION_COOKIE_NAME,
				$this->user_session,
				Prodigy_Cookies::calculate_expiration_time( $user_data['meta']['jwt']['access_expires_at'] )
			);
			if ( isset(
				$user_data['data']['attributes']['customer-balance-amount'],
				$user_data['data']['attributes']['subscriptions-present']
			) ) {
				$this->set_additional_user_info(
					(float) $user_data['data']['attributes']['customer-balance-amount'],
					(bool) $user_data['data']['attributes']['subscriptions-present']
				);
			}
		}
	}

	/**
	 * @param   array $data
	 *
	 * @return bool
	 * @throws \Exception
	 */
	public function login( array $data ): bool {
		if ( ! isset( $data['refresh'], $data['access'], $data['csrf'] ) ) {
			return false;
		}

		$is_set_refresh_cookie = Prodigy_Cookies::set_cookie(
			self::REFRESH_COOKIE_NAME,
			$data['refresh'],
			Prodigy_Cookies::calculate_expiration_time( $data['refresh_expires_at'] )
		);

		$is_set_access_cookie = Prodigy_Cookies::set_cookie(
			self::ACCESS_COOKIE_NAME,
			$data['access'],
			Prodigy_Cookies::calculate_expiration_time( $data['access_expires_at'] )
		);

		$is_set_jwt_cookie = Prodigy_Cookies::set_cookie(
			self::AUTHORIZATION_COOKIE_NAME,
			$data['csrf'],
			Prodigy_Cookies::calculate_expiration_time( $data['access_expires_at'] )
		);

		return $is_set_refresh_cookie && $is_set_access_cookie && $is_set_jwt_cookie;
	}

	/**
	 * @param   array       $data_attributes
	 * @param   string|null $expiration
	 *
	 * @return bool
	 * @throws \Exception
	 */
	public function set_user_info( array $data_attributes, ?string $expiration = null ): bool {
		$expiration_time = Prodigy_Cookies::calculate_expiration_time( $expiration );

		if ( isset( $data_attributes['attributes']['impersonated'] ) && $data_attributes['attributes']['impersonated'] ) {
			Prodigy_Cookies::set_cookie(
				self::IMPERSONATION_COOKIE_ID,
				$data_attributes['attributes']['impersonation-id'],
				$expiration_time,
				'/'
			);
		}

		if ( isset( $data_attributes['attributes']['customer-group-name'] ) ) {
			Prodigy_Cookies::set_cookie(
				self::USER_GROUP_COOKIE_NAME,
				$data_attributes['attributes']['customer-group-name'],
				$expiration_time,
				'/',
			);
		}

		Prodigy_Cookies::set_cookie(
			self::USER_ID_COOKIE_NAME,
			self::prodigy_encrypt_user_id( $data_attributes['id'] ),
			$expiration_time,
			'/',
		);

		$is_set_name_cookie = Prodigy_Cookies::set_cookie(
			self::USER_INFO_COOKIE_LAST_NAME,
			$data_attributes['attributes']['last-name'] ?? '',
			$expiration_time
		);

		$is_set_last_name_cookie = Prodigy_Cookies::set_cookie(
			self::USER_INFO_COOKIE_NAME,
			$data_attributes['attributes']['first-name'] ?? '',
			$expiration_time
		);

		$is_set_email_cookie = Prodigy_Cookies::set_cookie(
			self::USER_INFO_COOKIE_EMAIL,
			$data_attributes['attributes']['email'] ?? '',
			$expiration_time,
			'/'
		);

		$is_set_phone_cookie = Prodigy_Cookies::set_cookie(
			self::USER_INFO_COOKIE_PHONE,
			$data_attributes['attributes']['phone'] ?? '',
			$expiration_time
		);

		return $is_set_name_cookie && $is_set_last_name_cookie && $is_set_email_cookie && $is_set_phone_cookie;
	}

	/**
	 * @param   int $user_id
	 *
	 * @return string
	 * @throws \Random\RandomException
	 */
	public static function prodigy_encrypt_user_id( int $user_id ): string {
		$key = hash( 'sha256', AUTH_SALT, true );

		$nonce     = random_bytes( 12 );
		$plaintext = (string) $user_id;

		$tag        = '';
		$ciphertext = openssl_encrypt(
			$plaintext,
			'aes-256-gcm',
			$key,
			OPENSSL_RAW_DATA,
			$nonce,
			$tag,
			'',
			16
		);

		$blob = $nonce . $tag . $ciphertext;

		return rtrim( strtr( base64_encode( $blob ), '+/', '-_' ), '=' );
	}

	/**
	 * @param   float|bool $balance
	 * @param   bool       $subscription
	 *
	 * @return void
	 */
	public function set_additional_user_info( float $balance, bool $subscription ): void {
		update_option( self::CUSTOMER_BALANCE_OPTION, isset( $balance ) ? (float) $balance : false );
		update_option( self::SUBSCRIPTION_OPTION, $subscription );
	}

	/**
	 * @param   array $urlComponents
	 *
	 * @return string
	 */
	public function clear_url( array $urlComponents ): string {
		parse_str( $urlComponents['query'] ?? '', $queryParams );
		unset( $queryParams['session_token'], $queryParams['external_checkout_id'] );
		$newQueryString = http_build_query( $queryParams );
		$newUrl         = $urlComponents['host'] ?? '' . $urlComponents['path'];
		if ( ! empty( $newQueryString ) ) {
			$newUrl .= '?' . $newQueryString;
		}

		return $newUrl;
	}

	/**
	 * Generate url for Hosted System
	 *
	 * @return string
	 */
	public static function get_hosted_system_login_url(): string {
		$store_subdomain = Prodigy_Url_Helper::get_hs_store_subdomain();

		return Prodigy_Api_Client::API_PROTOCOL . $store_subdomain . '.' . PRODIGY_CHECKOUT_DOMAIN . Prodigy_Api_Client::HS_CUSTOMER_LOGIN_URL;
	}

	/**
	 * @param   string $token
	 *
	 * @return int|null
	 */
	public static function prodigy_decrypt_user_id( string $token ): ?int {
		$key = hash( 'sha256', AUTH_SALT, true );

		$blob = base64_decode( strtr( $token, '-_', '+/' ), true );
		if ( $blob === false || strlen( $blob ) < ( 12 + 16 + 1 ) ) {
			return null;
		}

		$nonce      = substr( $blob, 0, 12 );
		$tag        = substr( $blob, 12, 16 );
		$ciphertext = substr( $blob, 28 );

		$plaintext = openssl_decrypt(
			$ciphertext,
			'aes-256-gcm',
			$key,
			OPENSSL_RAW_DATA,
			$nonce,
			$tag,
			''
		);

		if ( $plaintext === false || ! ctype_digit( $plaintext ) ) {
			return null;
		}

		return (int) $plaintext;
	}

	/**
	 * Get user info from Hosted System
	 *
	 * @return void
	 * @throws \Exception
	 */
	public function get_user_info_from_hosted_system(): void {
		$user_auth_hash = Prodigy_Cookies::get_cookie( self::AUTHORIZATION_COOKIE_NAME );
		$response       = Prodigy_Request_Maker::get_instance()->do_user_info_request( $user_auth_hash );
		if ( ! isset( $response['code'] ) ) {
			return;
		}

		if ( ( $response['code'] === \WP_Http::OK ) && isset(
			$response['data']['attributes']['customer-balance-amount'],
			$response['data']['attributes']['subscriptions-present']
		) ) {
			$this->set_additional_user_info(
				$response['data']['attributes']['customer-balance-amount'],
				(bool) $response['data']['attributes']['subscriptions-present']
			);
		}

		if ( $response['code'] === \WP_Http::UNAUTHORIZED ) {
			$refresh_key = Prodigy_Cookies::get_cookie( self::REFRESH_COOKIE_NAME );
			$this->remove_impersonation_user( $refresh_key );
			$this->hosted_system_logout_process( $refresh_key );
			$this->logout();
			$this->delete_user_cookie();
			$this->clear_external_checkout_cookie();
		}
	}



	/**
	 *
	 * @return void
	 * @throws \Exception
	 */
	public function delete_user_cookie(): void {
		if ( Prodigy_Cookies::has_cookie( self::USER_ID_COOKIE_NAME ) ) {
			Prodigy_Cookies::remove_cookie( self::USER_ID_COOKIE_NAME );
		}
	}


	/**
	 * @param   string $refresh_key
	 *
	 * @return true|void
	 */
	public function remove_impersonation_user( string $refresh_key ) {
		$impersonation_cookie = Prodigy_Cookies::has_cookie( self::IMPERSONATION_COOKIE_ID );
		if ( ! empty( $refresh_key ) ) {
			if ( $impersonation_cookie ) {
				$impersonation_id = Prodigy_Cookies::get_cookie( self::IMPERSONATION_COOKIE_ID );
				if ( isset( $impersonation_id ) ) {
					$response = Prodigy_Request_Maker::get_instance()
													->do_delete_impersonation_user( $refresh_key, $impersonation_id );
					if ( $response['code'] === \WP_Http::NO_CONTENT ) {
						Prodigy_Cookies::remove_cookie( self::IMPERSONATION_COOKIE_ID );
						Prodigy_Cookies::remove_cookie( Prodigy_Cart::CART_COOKIE_NAME );
					}

					if ( PRODIGY_DEBUG_MODE && isset( $response['errors'][0]['details'] ) ) {
						Prodigy_Logger::add_record( Prodigy_Logger::TYPE_INFO, 'Failed removing impersonation user! Impersonation id ' . $impersonation_id . ' Response code ' . $response['errors'][0]['details'] . ' Refresh key ' . $refresh_key );
					}
				}

				return true;
			}
		}
	}

	/**
	 * @param   string $refresh_key
	 *
	 * @return bool
	 */
	public function hosted_system_logout_process( string $refresh_key ): bool {
		if ( ! empty( $refresh_key ) ) {
			$response = Prodigy_Request_Maker::get_instance()->do_user_logout( $refresh_key );
			if ( $response['code'] === \WP_Http::NO_CONTENT ) {
				if ( PRODIGY_DEBUG_MODE ) {
					Prodigy_Logger::add_record( Prodigy_Logger::TYPE_INFO, 'store successfully logout with token ' . $refresh_key . ' Response code ' . $response['code'] );
				}

				return true;
			}
		}

		if ( PRODIGY_DEBUG_MODE ) {
			$error_code = isset( $response ) ? $response['code'] : 'unknown';
			Prodigy_Logger::add_record( Prodigy_Logger::TYPE_ERROR, 'store unsuccessfully logout error code' . $error_code );
		}

		return false;
	}

	/**
	 * @return void
	 * @throws \Exception
	 */
	public function logout(): void {
		Prodigy_Cookies::remove_cookie( self::AUTHORIZATION_COOKIE_NAME );
		Prodigy_Cookies::remove_cookie( self::ACCESS_COOKIE_NAME );
		Prodigy_Cookies::remove_cookie( self::USER_INFO_COOKIE_EMAIL );
		Prodigy_Cookies::remove_cookie( self::USER_INFO_COOKIE_NAME );
		Prodigy_Cookies::remove_cookie( self::USER_INFO_COOKIE_LAST_NAME );
		Prodigy_Cookies::remove_cookie( self::REFRESH_COOKIE_NAME );
		Prodigy_Cookies::remove_cookie( self::IMPERSONATION_COOKIE_ID );
		Prodigy_Cookies::remove_cookie( self::USER_GROUP_COOKIE_NAME );
		Prodigy_Cookies::remove_cookie( self::PRODIGY_USER_PAIRED_SESSION_COOKIE_NAME );
	}

	/**
	 * Clears punch-out external checkout cookie (call on explicit user logout, not when rotating session_token).
	 *
	 * @return void
	 */
	public function clear_external_checkout_cookie(): void {
		Prodigy_Cookies::remove_cookie( self::EXTERNAL_CHECKOUT_ID_COOKIE_NAME );
	}
}
