<?php
/**
 * OAuth state + PKCE for Email Delivery (separate from social login).
 *
 * @package EmailSmtp
 * @since 1.3.0
 */

namespace EmailSmtp\Includes\OAuth;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class OAuthState {

	private const TRANSIENT_PREFIX = 'wpps_mail_oauth_st_';
	private const TTL              = 600;

	public const QUERY_PARAM = 'wpps_mail_auth';

	/**
	 * @return array{state:string, code_verifier:string, code_challenge:string}
	 */
	public static function create( string $provider_id, string $redirect_after = '', string $connection_id = '' ): array {
		try {
			$token = bin2hex( random_bytes( 16 ) );
		} catch ( \Throwable $e ) {
			$token = substr( hash( 'sha256', (string) microtime( true ) . wp_generate_password( 32, true, true ) ), 0, 32 );
		}

		try {
			$verifier = rtrim( strtr( base64_encode( random_bytes( 32 ) ), '+/', '-_' ), '=' );
		} catch ( \Throwable $e ) {
			$verifier = rtrim( strtr( base64_encode( hash( 'sha256', wp_generate_password( 64, true, true ), true ) ), '+/', '-_' ), '=' );
		}

		$challenge = rtrim( strtr( base64_encode( hash( 'sha256', $verifier, true ) ), '+/', '-_' ), '=' );

		$payload = [
			'v'  => 1,
			'p'  => sanitize_key( $provider_id ),
			'r'  => (string) $redirect_after,
			'cv' => $verifier,
			'u'  => get_current_user_id(),
			'c'  => sanitize_key( $connection_id ),
		];

		set_transient( self::TRANSIENT_PREFIX . $token, wp_json_encode( $payload ), self::TTL );

		return [
			'state'          => $token,
			'code_verifier'  => $verifier,
			'code_challenge' => $challenge,
		];
	}

	/**
	 * @return array{provider:string, redirect:string, code_verifier:string, user_id:int, connection_id:string}|false
	 */
	public static function verify_and_consume( string $state ) {
		$token = preg_replace( '/[^a-f0-9]/', '', (string) $state ) ?? '';
		if ( strlen( $token ) < 8 ) {
			return false;
		}

		$key    = self::TRANSIENT_PREFIX . $token;
		$stored = get_transient( $key );
		delete_transient( $key );

		if ( ! is_string( $stored ) || $stored === '' ) {
			return false;
		}

		$data = json_decode( $stored, true );
		if ( ! is_array( $data ) || (int) ( $data['v'] ?? 0 ) !== 1 ) {
			return false;
		}

		$provider = sanitize_key( (string) ( $data['p'] ?? '' ) );
		$verifier = (string) ( $data['cv'] ?? '' );
		if ( $provider === '' || $verifier === '' ) {
			return false;
		}

		return [
			'provider'      => $provider,
			'redirect'      => (string) ( $data['r'] ?? '' ),
			'code_verifier' => $verifier,
			'user_id'       => (int) ( $data['u'] ?? 0 ),
			'connection_id' => sanitize_key( (string) ( $data['c'] ?? '' ) ),
		];
	}

	public static function callback_url( string $provider_id ): string {
		return add_query_arg(
			self::QUERY_PARAM,
			sanitize_key( $provider_id ),
			home_url( '/' )
		);
	}
}
