<?php
/**
 * Site-local OAuth callback for Email Delivery (?wpps_mail_auth=).
 *
 * Separate from social sign-in (?wpps_auth=). Never exposes tokens.
 *
 * @package EmailSmtp
 * @since 1.3.0
 */

namespace EmailSmtp\Includes\OAuth;

use EmailSmtp\Includes\ConnectionStore;
use EmailSmtp\Includes\ProviderRegistry;
use EmailSmtp\Module;
use WP_PowerSuite\Core\NetworkScope;
use WP_PowerSuite\Core\TemporarySessionGuard;

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

final class OAuthCallbackHandler {

	public static function register(): void {
		add_action( 'init', [ self::class, 'dispatch' ], 0 );
	}

	public static function dispatch(): void {
		if ( empty( $_GET[ OAuthState::QUERY_PARAM ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return;
		}

		$provider_id = sanitize_key( wp_unslash( (string) $_GET[ OAuthState::QUERY_PARAM ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! in_array( $provider_id, [ 'google', 'microsoft' ], true ) ) {
			self::redirect_admin( $provider_id, 'invalid_provider' );
		}

		if ( ! empty( $_GET['error'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$error = sanitize_text_field( wp_unslash( (string) $_GET['error'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			self::redirect_admin( $provider_id, 'oauth_denied', $error );
		}

		$code  = isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['code'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$state = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['state'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( $code === '' || $state === '' ) {
			self::redirect_admin( $provider_id, 'missing_code' );
		}

		$verified = OAuthState::verify_and_consume( $state );
		if ( false === $verified || ( $verified['provider'] ?? '' ) !== $provider_id ) {
			self::redirect_admin( $provider_id, 'invalid_state' );
		}

		// Require an administrator session (same capability as Email Delivery settings).
		if ( TemporarySessionGuard::isActive() || ! NetworkScope::canManagePlugin() ) {
			self::redirect_admin( $provider_id, 'permission' );
		}

		// State was issued for a specific admin user — reject session switches.
		if ( (int) ( $verified['user_id'] ?? 0 ) !== (int) get_current_user_id() ) {
			self::redirect_admin( $provider_id, 'permission' );
		}

		$provider = ProviderRegistry::get( $provider_id );
		if ( ! $provider instanceof AbstractOAuthMailProvider ) {
			self::redirect_admin( $provider_id, 'invalid_provider' );
		}

		$conn_id  = sanitize_key( (string) ( $verified['connection_id'] ?? '' ) );
		$settings = \EmailSmtp\Includes\EmailRouter::get_settings();
		if ( $conn_id !== '' ) {
			$settings = ConnectionStore::settings_for_connection( $settings, $conn_id );
		}
		$result = $provider->exchange_code( $settings, $code, (string) $verified['code_verifier'] );
		if ( is_wp_error( $result ) ) {
			self::redirect_admin( $provider_id, 'token_exchange', $result->get_error_message() );
		}

		$expires_in = max( 0, (int) ( $result['expires_in'] ?? 3600 ) );
		OAuthTokenStore::write(
			$provider_id,
			[
				'access_token'    => (string) $result['access_token'],
				'refresh_token'   => (string) ( $result['refresh_token'] ?? '' ),
				'expires_at'      => time() + $expires_in,
				'connected_email' => sanitize_email( (string) ( $result['email'] ?? '' ) ),
			],
			$conn_id
		);

		$redirect = (string) ( $verified['redirect'] ?? '' );
		if ( $redirect === '' || ! wp_validate_redirect( $redirect, false ) ) {
			$redirect = NetworkScope::adminUrl(
				'admin.php',
				[
					'page' => Module::PAGE_SLUG,
					'tab' => 'settings',
				]
			);
		}

		wp_safe_redirect( add_query_arg( 'oauth', 'connected', $redirect ) );
		exit;
	}

	private static function redirect_admin( string $provider_id, string $code, string $detail = '' ): void {
		unset( $provider_id );
		$url = NetworkScope::adminUrl(
			'admin.php',
			[
				'page'        => Module::PAGE_SLUG,
				'tab' => 'settings',
				'oauth_error' => $code,
			]
		);
		if ( $detail !== '' ) {
			$url = add_query_arg( 'oauth_detail', rawurlencode( substr( sanitize_text_field( $detail ), 0, 200 ) ), $url );
		}
		wp_safe_redirect( $url );
		exit;
	}
}
