<?php
/**
 * Shift4 APM abstract request class.
 *
 * @package flo-checkout
 */

namespace Flo_Checkout\Gateways\Shift4_APM\Requests;

use Exception;
use Flo_Checkout\Gateways\Apple_Pay\Settings;
use Flo_Checkout\Gateways\Shift4_APM\Enums\Request_Method;
use Flo_Checkout\Gateways\Shift4_APM\Requests\Models\Request_Response_Model;
use Flo_Checkout\Gateways\Shift4_APM\Responses\Abstract_Response;
use Flo_Checkout\Gateways\Shift4_APM\Responses\Response_Factory;
use Flo_Checkout\Gateways\Shift4_APM\Traits\Request_And_Response_Logger_Trait;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;

defined( 'ABSPATH' ) || exit;

/**
 * Abstract request class.
 */
abstract class Abstract_Request {
	use Request_And_Response_Logger_Trait;

	/**
	 * Request response.
	 *
	 * @var Request_Response_Model
	 */
	protected Request_Response_Model $request_response_model;

	/**
	 * Abstract_Request constructor.
	 *
	 * @param Response_Factory $response_factory          Response factory.
	 */
	public function __construct(
		private readonly Response_Factory $response_factory,
	) {}


	/**
	 * Return response handler type.
	 *
	 * @return string
	 */
	abstract protected function get_response_handler_type(): string;

	/**
	 * Return full URL for API request.
	 *
	 * @return string
	 */
	abstract protected function get_url(): string;

	/**
	 * Return method for request call.
	 *
	 * @return Request_Method
	 */
	protected function get_request_method(): Request_Method {
		return Request_Method::POST;
	}

	/**
	 * Get request headers.
	 *
	 * @return string[]
	 */
	protected function get_headers(): array {
		return array();
	}

	/**
	 * Get the request body.
	 *
	 * @return string|null
	 */
	protected function get_body(): ?string {
		return null;
	}

	/**
	 * Send request to Shift4 APM API.
	 *
	 * @throws Exception If request failed.
	 */
	protected function send_request(): void {
		$request_url    = $this->get_url();
		$request_method = $this->get_request_method()->name;
		$headers        = $this->get_headers();
		$json_body      = $this->get_body();
		$timeout        = $this->get_timeout();

		$this->log(
			'Sending Shift4 APM request',
			array(
				'method'  => $request_method,
				'url'     => $request_url,
				'headers' => $headers,
				'body'    => $json_body,
				'timeout' => $timeout,
			)
		);

		$response = wp_remote_request(
			$request_url,
			array(
				'method'  => $request_method,
				'headers' => $headers,
				'body'    => $json_body,
				'timeout' => $timeout,
			)
		);

		if ( is_wp_error( $response ) ) {
			$log_as_debug = apply_filters(
				'flo_checkout_shift4_apm_log_request_error_as_debug',
				false
			);

			$this->log_and_throw_error(
				__( 'Request to payment processor failed.', 'flo-checkout-plugin' ),
				array(
					'method'  => $request_method,
					'url'     => $request_url,
					'error'   => $response->get_error_message(),
					'body'    => $json_body,
					'timeout' => $timeout,
				),
				null,
				$log_as_debug ? Log_Level_Enum::DEBUG : Log_Level_Enum::ERROR
			);
		}

		Settings::log(
			'Shift4 APM response',
			array(
				'method'   => $request_method,
				'url'      => $request_url,
				'headers'  => $headers,
				'body'     => $json_body,
				'timeout'  => $timeout,
				'response' => $response,
			),
		);

		$this->handle_response( $request_url, $response );
	}

	/**
	 * Get request timeout.
	 *
	 * @return int
	 */
	protected function get_timeout(): int {
		return 10;
	}

	/**
	 * Handle request response.
	 *
	 * @param string $request_url Request URL.
	 * @param array  $response    Request response.
	 *
	 * @return void
	 */
	protected function handle_response( string $request_url, array $response ): void {
		$this->request_response_model = new Request_Response_Model(
			$this->get_response_handler_type(),
			wp_remote_retrieve_body( $response ),
			wp_remote_retrieve_response_code( $response ),
			wp_remote_retrieve_headers( $response ),
			$request_url,
			$this->get_request_method()->name,
		);
	}

	/**
	 * Get request response.
	 *
	 * @return Request_Response_Model
	 */
	protected function get_response_data(): Request_Response_Model {
		return $this->request_response_model;
	}

	/**
	 * Return proper response handler.
	 *
	 * @return Abstract_Response
	 * @throws Exception If creation fails.
	 */
	protected function get_response_handler(): Abstract_Response {
		return $this->response_factory->create(
			$this->get_response_data(),
		);
	}

	/**
	 * Process request.
	 *
	 * @throws Exception If request failed.
	 * @return Abstract_Response
	 */
	public function process(): Abstract_Response {
		$this->send_request();

		return $this->get_response_handler()->process();
	}
}
