<?php

namespace Pinova\Services;

use Carbon\Carbon;
use Exception;
use Pinova\Exceptions\SendOTPException;
use Pinova\Helpers\IP;
use Pinova\Helpers\JWT;
use Pinova\Models\OTP;
use Pinova\Objects\Identifier;

class OTPService {

	/**
	 * @throws Exception
	 */
	public static function create( Identifier $identifier, array $channels, string $type, ?int $user_id = null ): array {

		$ip_requests = OTP::query()
		                  ->where( 'ip_address', IP::get() )
		                  ->where( 'expires_at', '>=', Carbon::now()->subHour() )
		                  ->count();

		if ( $ip_requests >= 10 ) {

			FirewallService::block( IP::get(), 60 );

			throw new SendOTPException( __( 'آدرس آی.پی شما مسدود شده است.', 'pinova' ) );
		}

		$identifier_requests = OTP::query()
		                          ->where( 'identifier', $identifier->get_value() )
		                          ->where( 'expires_at', '>=', Carbon::now()->subHour() )
		                          ->count();

		if ( $identifier_requests >= 10 ) {

			FirewallService::block( $identifier->get_value(), 60 );

			throw new SendOTPException( __( 'ایمیل یا تلفن همراه مسدود شده است.', 'pinova' ) );
		}

		$code = random_int( 10_00_00, 99_99_99 );

		/** @var OTP $otp */
		$otp = OTP::query()->create( [
			'user_id'    => $user_id,
			'identifier' => $identifier->get_value(),
			'code'       => $code,
			'type'       => $type,
			'channels'   => array_fill_keys( $channels, false ),
		] );

		try {
			$successful_channels = ChannelService::send( $otp, $code );
		} catch ( Exception $e ) {

			// @todo Shit! log it anyway

			$otp->delete();

			throw $e;
		}

		return [
			JWT::encode( [ 'otp_id' => $otp->id, ] ),
			$successful_channels,
		];
	}

	/**
	 * @throws Exception
	 */
	public static function verify( string $jwt, string $code ): \WP_User {

		try {
			$payload = JWT::decode( $jwt );
		} catch ( Exception $e ) {
			throw new Exception( __( 'کد تایید معتبر نمی‌باشد.', 'pinova' ) );
		}

		/** @var OTP $otp */
		$otp = OTP::query()->findOrFail( $payload['otp_id'] );

		if ( $otp->isExpired() || $otp->isVerified() || $otp->attempts >= 5 ) {
			throw new Exception( __( 'کد تایید معتبر نمی‌باشد.', 'pinova' ) );
		}

		if ( $otp->ip_address != IP::get() ) {
			throw new Exception( __( 'شما به این کد دسترسی ندارید.', 'pinova' ) );
		}

		if ( ! wp_check_password( $code, $otp->code ) ) {

			$otp->incrementAttempts();

			throw new Exception( __( 'کد تایید معتبر نمی‌باشد.', 'pinova' ) );
		}

		$otp->markVerified();

		$user       = UserService::get_or_create( $otp );
		$identifier = new Identifier( $otp->identifier );
		$username   = sanitize_user( $identifier->get_value(), true );

		if ( $identifier->is_mobile() && $user->user_login !== $username ) {
			UserService::update_username( $user->ID, $username );
		}

		return $user;
	}

}
