<?php
/**
 * Class used to define Email Service.
 *
 * @package automattic/jetpack-account-protection
 */

namespace Automattic\Jetpack\Account_Protection;

use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Jetpack_Options;

/**
 * Class Email_Service
 */
class Email_Service {
	/**
	 * Connection manager dependency.
	 *
	 * @var Connection_Manager
	 */
	private $connection_manager;

	/**
	 * Constructor for dependency injection.
	 *
	 * @param Connection_Manager|null $connection_manager Connection manager dependency.
	 */
	public function __construct(
		?Connection_Manager $connection_manager = null
	) {
		$this->connection_manager = $connection_manager ?? new Connection_Manager();
	}

	/**
	 * Send the email using the API.
	 *
	 * @param int    $user_id The user ID.
	 * @param string $auth_code The authentication code.
	 *
	 * @return true|\WP_Error True if the email was sent successfully, \WP_Error otherwise.
	 */
	public function api_send_auth_email( int $user_id, string $auth_code ) {
		$blog_id = Jetpack_Options::get_option( 'id' );

		/**
		 * Filters whether the Account Protection verification email should be handled externally.
		 *
		 * When the filter returns a truthy value, the default WPCOM API email send is skipped,
		 * allowing sites to deliver the email locally (e.g. via `wp_mail()`).
		 *
		 * @since 0.3.0
		 *
		 * @param bool   $handled  Whether the email has been handled. Default false.
		 * @param int    $user_id  The user ID.
		 * @param string $auth_code The authentication code.
		 * @param int    $blog_id  The blog ID, or false if not available.
		 */
		$handled = apply_filters( 'jetpack_account_protection_send_auth_email', false, $user_id, $auth_code, $blog_id );

		if ( $handled ) {
			return true;
		}

		if ( ! $blog_id || ! $this->connection_manager->is_connected() ) {
			return new \WP_Error( 'jetpack_connection_error', __( 'Jetpack is not connected. Please connect and try again.', 'jetpack-account-protection' ) );
		}

		$body = array(
			'user_id' => $user_id,
			'code'    => $auth_code,
		);

		$response = $this->send_email_request( (int) $blog_id, $body );
		if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
			return new \WP_Error( 'email_send_error', __( 'Failed to send authentication code. Please try again.', 'jetpack-account-protection' ) );
		}

		$response_code = wp_remote_retrieve_response_code( $response );
		$body          = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( 200 !== $response_code ) {
			return new \WP_Error( $body['code'] ?? 'email_send_error', $body['message'] ?? __( 'Failed to send authentication code. Please try again.', 'jetpack-account-protection' ) );
		}

		if ( empty( $body['email_send_success'] ) ) {
			return new \WP_Error( 'email_send_error', __( 'Failed to send authentication code. Please try again.', 'jetpack-account-protection' ) );
		}

		return true;
	}

	/**
	 * Dependency decoupling for the static call to the client.
	 *
	 * @param int   $blog_id Blog ID.
	 * @param array $body The request body.
	 * @return array|\WP_Error Response data or error.
	 */
	protected function send_email_request( int $blog_id, array $body ) {
		return Client::wpcom_json_api_request_as_blog(
			sprintf( '/sites/%d/jetpack-protect-send-verification-code', $blog_id ),
			'2',
			array(
				'method' => 'POST',
			),
			$body,
			'wpcom'
		);
	}

	/**
	 * Resend email attempts.
	 *
	 * @param int    $user_id The user ID.
	 * @param array  $transient_data The transient data.
	 * @param string $token The token.
	 *
	 * @return true|\WP_Error True if the email was resent successfully, \WP_Error otherwise.
	 */
	public function resend_auth_email( int $user_id, array $transient_data, string $token ) {
		if ( $transient_data['requests'] >= Config::PASSWORD_DETECTION_EMAIL_REQUEST_LIMIT ) {
			return new \WP_Error( 'email_request_limit_exceeded', __( 'Email request limit exceeded. Please try again later.', 'jetpack-account-protection' ) );
		}

		$auth_code                   = $this->generate_auth_code();
		$transient_data['auth_code'] = $auth_code;

		$resend = $this->api_send_auth_email( $user_id, $auth_code );
		if ( is_wp_error( $resend ) ) {
			return $resend;
		}

		++$transient_data['requests'];

		if ( ! set_transient( Config::PREFIX . "_{$token}", $transient_data, Config::PASSWORD_DETECTION_EMAIL_SENT_EXPIRATION ) ) {
			return new \WP_Error( 'transient_set_error', __( 'Failed to set transient data. Please try again.', 'jetpack-account-protection' ) );
		}

		return true;
	}

	/**
	 * Generate an auth code.
	 *
	 * @return string The generated auth code.
	 */
	public function generate_auth_code(): string {
		return (string) wp_rand( 100000, 999999 );
	}

	/**
	 * Mask an email address like d*****@g*****.com.
	 *
	 * @param string $email The email address to mask.
	 *
	 * @return string The masked email address.
	 */
	public function mask_email_address( string $email ): string {
		$parts        = explode( '@', $email );
		$name         = substr( $parts[0], 0, 1 ) . str_repeat( '*', strlen( $parts[0] ) - 1 );
		$domain_parts = explode( '.', $parts[1] );
		$domain       = substr( $domain_parts[0], 0, 1 ) . str_repeat( '*', strlen( $domain_parts[0] ) - 1 );

		// Join all domain parts except the first one with dots
		$tld = implode( '.', array_slice( $domain_parts, 1 ) );

		return "{$name}@{$domain}.{$tld}";
	}
}
