<?php

namespace SugarCalendarBookings\Integrations\OAuthRelay;

use SugarCalendarBookings\Vendor\ProductApi\Auth\HMACAuthStrategy;
use SugarCalendarBookings\Vendor\ProductApi\Auth\InstallationId;
use SugarCalendarBookings\Vendor\ProductApi\Context;
use SugarCalendarBookings\Vendor\ProductApi\Options;
use SugarCalendarBookings\Vendor\ProductApi\ProductApi;
use WP_Error;

/**
 * OAuth Relay Client.
 *
 * Handles communication with the Product API OAuth Relay endpoints.
 *
 * @since 1.4.0
 */
class OAuthRelayClient {

	/**
	 * Get the installation ID from the Product API client.
	 *
	 * @since 1.4.0
	 *
	 * @return string
	 */
	private function get_installation_id(): string {

		$options = ProductApi::get( Options::class );

		return ( new InstallationId( $options ) )->get();
	}

	/**
	 * Get the effective license type for relay routing.
	 *
	 * Returns 'pro' only when the Pro plugin is active AND the license is valid.
	 * Pro installs without a valid license fall back to 'lite' so OAuth flows
	 * keep working on Lite relay endpoints until the license is activated.
	 *
	 * @since 1.4.0
	 *
	 * @return string
	 */
	private function get_license_type(): string {

		$context = ProductApi::get( Context::class );

		return ( $context->is_pro() && $context->is_license_valid() ) ? 'pro' : 'lite';
	}

	/**
	 * Get the authorization URL to start OAuth flow.
	 *
	 * @since 1.4.0
	 *
	 * @param string $provider   Provider name: 'google' or 'zoom'.
	 * @param string $return_url URL to return to after OAuth completes.
	 * @param string $feature    Feature identifier (e.g., 'integrations').
	 *
	 * @return string Authorization URL.
	 */
	public function get_authorization_url(
		string $provider,
		string $return_url,
		string $feature
	): string {

		$context = ProductApi::get( Context::class );

		// Add security params to return URL for callback validation.
		$return_url = add_query_arg(
			[
				'nonce'    => wp_create_nonce( 'scbookings_oauth_connect' ),
				'provider' => $provider,
				'feature'  => $feature,
			],
			$return_url
		);

		$query_params = [
			'provider'        => $provider,
			'feature'         => $feature,
			'installation_id' => $this->get_installation_id(),
			'return_url'      => rawurlencode( $return_url ),
		];

		return add_query_arg(
			$query_params,
			$context->get_api_url() . '/oauth-relay/v1/' . $this->get_license_type() . '/auth/start'
		);
	}

	/**
	 * Exchange authorization code for encrypted tokens.
	 *
	 * @since 1.4.0
	 *
	 * @param string $code Exchange code from OAuth callback.
	 *
	 * @return array|WP_Error Token data or error.
	 */
	public function exchange_code( string $code ) {

		$hmac_auth = ProductApi::get( HMACAuthStrategy::class );

		$response = ProductApi::client()
			->post( '/oauth-relay/v1/' . $this->get_license_type() . '/auth/token' )
			->json( [ 'code' => $code ] )
			->auth_strategy( $hmac_auth )
			->send();

		return $this->handle_relay_response( $response );
	}

	/**
	 * Refresh an expired access token.
	 *
	 * @since 1.4.0
	 *
	 * @param string $refresh_token Encrypted refresh token.
	 * @param int    $app_id        OAuth app ID from original token exchange.
	 *
	 * @return array|WP_Error New token data or error.
	 */
	public function refresh_token( string $refresh_token, int $app_id ) {

		$hmac_auth = ProductApi::get( HMACAuthStrategy::class );

		$response = ProductApi::client()
			->post( '/oauth-relay/v1/' . $this->get_license_type() . '/auth/refresh' )
			->json(
				[
					'refresh_token' => $refresh_token,
					'app_id'        => $app_id,
				]
			)
			->auth_strategy( $hmac_auth )
			->send();

		return $this->handle_relay_response( $response );
	}

	/**
	 * Register a webhook URL with the Product API.
	 *
	 * Tells the relay to forward provider webhook events to the given URL.
	 *
	 * @since 1.4.0
	 *
	 * @param string $provider         Provider slug (e.g. 'zoom').
	 * @param string $provider_user_id Provider account/user ID.
	 * @param string $webhook_url      Publicly accessible URL to receive webhooks.
	 *
	 * @return array|WP_Error Response body or error.
	 */
	public function register_webhook( string $provider, string $provider_user_id, string $webhook_url ) {

		$hmac_auth = ProductApi::get( HMACAuthStrategy::class );

		$response = ProductApi::client()
			->post( '/oauth-relay/v1/' . $this->get_license_type() . '/webhooks/register' )
			->json(
				[
					'provider'         => $provider,
					'provider_user_id' => $provider_user_id,
					'webhook_url'      => $webhook_url,
				]
			)
			->auth_strategy( $hmac_auth )
			->send();

		return $this->handle_relay_response( $response );
	}

	/**
	 * Unregister a webhook URL from the Product API.
	 *
	 * Stops the relay from forwarding provider webhook events for this user.
	 *
	 * @since 1.4.0
	 *
	 * @param string $provider         Provider slug (e.g. 'zoom').
	 * @param string $provider_user_id Provider account/user ID.
	 *
	 * @return array|WP_Error Response body or error.
	 */
	public function unregister_webhook( string $provider, string $provider_user_id ) {

		$hmac_auth = ProductApi::get( HMACAuthStrategy::class );

		$response = ProductApi::client()
			->post( '/oauth-relay/v1/' . $this->get_license_type() . '/webhooks/unregister' )
			->json(
				[
					'provider'         => $provider,
					'provider_user_id' => $provider_user_id,
				]
			)
			->auth_strategy( $hmac_auth )
			->send();

		return $this->handle_relay_response( $response );
	}

	/**
	 * Handle a relay API response.
	 *
	 * @since 1.4.0
	 *
	 * @param mixed $response Response from ProductApi client.
	 *
	 * @return array|WP_Error Response body or error.
	 */
	private function handle_relay_response( $response ) {

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		if ( ! $response->is_successful() ) {
			$error = $response->get_errors();

			// Attach HTTP status code to the error for diagnostics.
			if ( is_wp_error( $error ) ) {
				$existing_data = $error->get_error_data( $error->get_error_code() );
				$merged_data   = is_array( $existing_data ) ? $existing_data : [];

				$merged_data['http_status'] = $response->get_status_code();

				$error->add_data( $merged_data, $error->get_error_code() );
			}

			return $error;
		}

		return $response->get_body();
	}
}
