<?php
/**
 * Abstract payment class.
 */

declare(strict_types=1);
namespace FeData\FastEvents\Payment;

defined( 'ABSPATH' ) || exit;

use FeData\FastEvents\Logging\LoggerFactory;
use FeData\FastEvents\Storage\Options;
use FeData\FastEvents\Storage\PluginSettings;
use FeData\FastEvents\Storage\UserMeta;
use FeData\FastEvents\Utilities\PluginUtil;
use JsonException;
use WP_Error;
use WP_User;

/**
 * Abstract payment class.
 */
abstract class AbstractPayment implements PaymentInterface {

	/**
	 * User agent string used for the API requests.
	 *
	 * @since 3.0
	 * @var array
	 */
	private array $user_agent = array();

	/**
	 * HTTP headers for the API requests.
	 *
	 * @since 3.0
	 * @var array
	 */
	protected array $headers = array();

	/**
	 * The payload for the API requests.
	 *
	 * @since 3.0
	 * @var array
	 */
	protected array $payload = array();


	/**
	 * Retrieve the payment provider name.
	 *
	 * @since 3.0.0
	 *
	 * @return string The name of the payment provider.
	 */
	public function get_provider_name(): string {
		return static::$provider_name;
	}

	/**
	 * AbstractMailer constructor.
	 *
	 * @since 1.0
	 *
	 * @param Options        $options   The options.
	 * @param PluginSettings $settings  The settings.
	 * @param UserMeta       $user_meta The user meta.
	 * @param LoggerFactory  $logger    The logger.
	 */
	public function __construct(
		protected readonly Options $options,
		protected readonly PluginSettings $settings,
		protected readonly UserMeta $user_meta,
		protected readonly LoggerFactory $logger
	) {
		$this->user_agent['User-Agent'] = PluginUtil::get_user_agent();
	}

	/**
	 * Execute the payment provider API call.
	 * Logging occurs here, invoking a callback to the provider for error analysis.
	 *
	 * @since 3.0.0
	 *
	 * @param string $endpoint The API endpoint.
	 * @param string $method   The API method (POST, GET, DELETE).
	 * @return array|WP_Error Returns the response from the API.
	 */
	protected function api_call( string $endpoint, string $method ): array|WP_Error {
		$func     = static::$encoder;
		$response = wp_remote_request(
			$endpoint,
			array(
				'method'  => $method,
				'headers' => array_merge( $this->default_headers(), $this->user_agent, $this->headers ),
				...( ! empty( $this->payload ) ? array( 'body' => $func( $this->payload ) ) : array() ),
			)
		);

		// Check for errors.
		$body = wp_remote_retrieve_body( $response );
		if ( is_wp_error( $response ) ) {
			$this->logger->alert(
				$response->get_error_message(),
				array(
					'type'     => 'Payment API',
					'endpoint' => $endpoint,
					'method'   => $method,
					'provider' => static::$provider_name,
				)
			);
			return new WP_Error( 'invalid_request', 'Payment provider (' . static::$provider_name . ') -> ' . $response->get_error_message() );
		}

		// Check the response code.
		$http_code = (int) wp_remote_retrieve_response_code( $response );
		try {
			$result = json_decode( $body, true, 512, JSON_THROW_ON_ERROR );
		} catch ( JsonException ) {
			$result = array();
		}
		if ( 200 > $http_code || 299 < $http_code ) {
			$error = $this->analyse_error( $result );
			$this->logger->alert(
				'Payment provider -> ' . $error,
				array(
					'type'      => 'Payment API',
					'endpoint'  => $endpoint,
					'method'    => $method,
					'http_code' => $http_code,
					'provider'  => static::$provider_name,
					'body'      => $result,
				)
			);
			return new WP_Error( 'invalid_request', static::$provider_name . '-> ' . $error, array( 'status' => $http_code ) );
		}

		$result['http_code'] = $http_code;
		return $result;
	}

	/**
	 * Return a unique state string (nonce) to prevent CSRF attacks.
	 *
	 * @since 3.0.0
	 *
	 * @param WP_User $user The user object.
	 * @return string Returns the state string.
	 */
	protected function get_state( WP_User $user ): string {
		return wp_create_nonce( 'FE-SaaS' . $this->settings->saas_client_secret() . $user->ID ) . ':' . $user->ID;
	}

	/**
	 * Verify the state string (nonce) to prevent CSRF attacks.
	 *
	 * @since 3.0.0
	 *
	 * @param string $state   The state string.
	 * @param int    $user_id The user ID.
	 * @return bool Returns true if the state is valid, false otherwise.
	 */
	protected function verify_state( string $state, int $user_id ): bool {
		return is_int( wp_verify_nonce( $state, 'FE-SaaS' . $this->settings->saas_client_secret() . $user_id ) );
	}


	/**
	 * The provider-specific default headers.
	 *
	 * @since 3.0.0
	 *
	 * @return array Returns the default headers.
	 */
	abstract protected function default_headers(): array;

	/**
	 * The provider-specific error analysis.
	 *
	 * @since 3.0.0
	 *
	 * @param array $error  The error body payload.
	 * @return string Returns the proper error message.
	 */
	abstract protected function analyse_error( array $error ): string;
}
