<?php

namespace WeeConnectPay\Integrations;

use WeeConnectPay\Environment;
use WeeConnectPay\Exceptions\WeeConnectPayException;
use WeeConnectPay\Exceptions\Codes\ExceptionCode;
use WeeConnectPay\WordPress\Plugin\includes\WeeConnectPayAPI;
use WeeConnectPay\WordPress\Plugin\includes\WeeConnectPayUtilities;

/**
 * Manages RFC 8628 Device Authorization flow for plugin onboarding.
 *
 * Handles requesting device codes and polling for credentials,
 * replacing manual Client ID/Secret entry with a code-based approval flow.
 *
 * @since 3.20.0
 */
class DeviceFlowManager {

	/**
	 * App UUIDs per environment (Clover app identifiers).
	 */
	/**
	 * Get the app UUID for the current environment.
	 * Uses the Environment registry which includes all environments (local, sandbox, staging, production).
	 *
	 * @return string
	 * @since 3.20.0
	 * @updated 4.1.0 Delegates to Environment class
	 */
	public function getAppUuid(): string {
		return Environment::getAppUuid();
	}

	/**
	 * Request a device code from the WeeConnectPay API.
	 *
	 * Calls POST /v1/connect/authorize with platform identity. No authentication required.
	 *
	 * @return object Response containing device_code, user_code, verification_uri, verification_uri_complete, expires_in, interval
	 * @throws WeeConnectPayException
	 * @since 3.20.0
	 */
	public function requestDeviceCode(): object {
		$apiDomain = WeeConnectPayAPI::getApiDomain();
		$endpoint  = $apiDomain . '/v1/connect/authorize';

		global $wpdb, $wp_version;

		$pluginsUrl = plugins_url();
		if ( $pluginsUrl !== null ) {
			$pluginsUrl = preg_replace( '/\s+/', '', $pluginsUrl );
		}

		$body = array(
			'app_uuid'     => $this->getAppUuid(),
			'platform_key' => 'woocommerce',
			'client_name'  => 'WeeConnectPay WordPress Plugin v' . ( defined( 'WEECONNECT_VERSION' ) ? WEECONNECT_VERSION : '0' ),
			'metadata'     => array(
				'integration_version' => defined( 'WEECONNECT_VERSION' ) ? WEECONNECT_VERSION : '0',
				'platform_version' => defined( 'WC_VERSION' ) ? WC_VERSION : '0',
			),
			'platform_data' => array(
				'domain'               => wp_parse_url( site_url(), PHP_URL_HOST ),
				'woocommerce_version'  => defined( 'WC_VERSION' ) ? WC_VERSION : '0',
				'wordpress'            => array(
					'site_url'     => site_url(),
					'home_url'     => home_url(),
					'db_prefix'    => $wpdb->prefix,
					'version'      => $wp_version ?: '0',
					'plugins_url'  => $pluginsUrl,
					'settings_url' => WeeConnectPayUtilities::getSettingsURL(),
				),
			),
		);

		$response = wp_remote_post(
			$endpoint,
			array(
				'timeout' => 60,
				'headers' => array(
					'Content-Type' => 'application/json',
					'Accept'       => 'application/json',
				),
				'body' => wp_json_encode( $body ),
			)
		);

		if ( is_wp_error( $response ) ) {
			$errorMessage = $response->get_error_message();
			LogService::error( 'Device flow: code request failed (WP Error): ' . $errorMessage );
			throw new WeeConnectPayException(
				'Failed to connect to WeeConnectPay API: ' . $errorMessage,
				ExceptionCode::UNHANDLED_EXCEPTION
			);
		}

		$httpCode     = wp_remote_retrieve_response_code( $response );
		$responseBody = json_decode( wp_remote_retrieve_body( $response ) );

		if ( $httpCode !== 200 ) {
			$errorMessage = isset( $responseBody->message ) ? $responseBody->message : 'Unknown error';
			LogService::error( 'Device flow: code request failed (HTTP ' . $httpCode . '): ' . $errorMessage );
			throw new WeeConnectPayException(
				'Failed to start device authorization: ' . $errorMessage,
				ExceptionCode::UNHANDLED_EXCEPTION
			);
		}

		if ( ! isset( $responseBody->device_code, $responseBody->user_code ) ) {
			LogService::error( 'Device flow: code request response missing required fields' );
			throw new WeeConnectPayException(
				'Invalid response from authorization server.',
				ExceptionCode::UNHANDLED_EXCEPTION
			);
		}

		return $responseBody;
	}

	/**
	 * Poll for credentials using a device code.
	 *
	 * Calls POST /v1/connect/token. No authentication required.
	 *
	 * @param string $deviceCode The device code to poll with
	 * @return object Response — either success with client_id/client_secret or error status
	 * @throws WeeConnectPayException On network errors only; API-level errors are returned as response objects
	 * @since 3.20.0
	 */
	public function pollForCredentials( string $deviceCode ): object {
		$apiDomain = WeeConnectPayAPI::getApiDomain();
		$endpoint  = $apiDomain . '/v1/connect/token';

		$response = wp_remote_post(
			$endpoint,
			array(
				'timeout' => 30,
				'headers' => array(
					'Content-Type' => 'application/json',
					'Accept'       => 'application/json',
				),
				'body' => wp_json_encode( array(
					'grant_type'  => 'urn:ietf:params:oauth:grant-type:device_code',
					'device_code' => $deviceCode,
				) ),
			)
		);

		if ( is_wp_error( $response ) ) {
			$errorMessage = $response->get_error_message();
			LogService::error( 'Device flow: poll failed (WP Error): ' . $errorMessage );
			throw new WeeConnectPayException(
				'Failed to connect to WeeConnectPay API: ' . $errorMessage,
				ExceptionCode::UNHANDLED_EXCEPTION
			);
		}

		$httpCode     = wp_remote_retrieve_response_code( $response );
		$responseBody = json_decode( wp_remote_retrieve_body( $response ) );

		if ( ! $responseBody ) {
			$responseBody = (object) array( 'error' => 'invalid_response' );
		}

		// Attach HTTP code for the controller to inspect
		$responseBody->http_code = $httpCode;

		return $responseBody;
	}
}
