<?php
/**
 * Request response model.
 *
 * @package flo-checkout
 */

namespace Flo_Checkout\Gateways\Shift4_APM\Requests\Models;

use Exception;
use Flo_Checkout\Gateways\Shift4_APM\Traits\Request_And_Response_Logger_Trait;
use WpOrg\Requests\Utility\CaseInsensitiveDictionary;

defined( 'ABSPATH' ) || exit;

/**
 * Request response model.
 */
readonly class Request_Response_Model {
	use Request_And_Response_Logger_Trait;

	/**
	 * Body (decoded JSON).
	 *
	 * @var array
	 */
	private array $body;

	/**
	 * Headers.
	 *
	 * @var CaseInsensitiveDictionary
	 */
	private CaseInsensitiveDictionary $headers;

	/**
	 * Status code.
	 *
	 * @var int
	 */
	private int $status_code;

	/**
	 * Construct class.
	 *
	 * @param string                          $type        Response handler type.
	 * @param string                          $body_string Body.
	 * @param int|string                      $status_code Status code.
	 * @param CaseInsensitiveDictionary|array $headers     Headers.
	 * @param string                          $url         URL. For debugging.
	 * @param string                          $method      Method. For debugging.
	 */
	public function __construct(
		private string $type,
		private string $body_string,
		int|string $status_code,
		CaseInsensitiveDictionary|array $headers,
		private string $url,
		private string $method,
	) {
		if ( ! empty( $headers ) ) {
			$this->headers = $headers;
		}
		if ( ! empty( $status_code ) ) {
			$this->status_code = $status_code;
		}
	}

	/**
	 * Get the response body.
	 *
	 * @return array
	 * @throws Exception If response body can't be decoded.
	 */
	public function get_body(): array {
		if ( ! isset( $this->body ) ) {
			$body = json_decode( $this->body_string, true );

			if ( empty( $body ) || ! is_array( $body ) ) {
				$this->log_and_throw_error(
					__( 'Failed to process response.', 'flo-checkout-plugin' ),
					array(
						'body_string' => $this->body_string,
						'body'        => $body,
						'url'         => $this->url,
						'method'      => $this->method,
					),
					'Failed to decode response body.'
				);
			}

			$this->body = $body;
		}
		return $this->body;
	}

	/**
	 * Get the response type, used to determine how to handle the response.
	 *
	 * @return string
	 */
	public function get_type(): string {
		return $this->type;
	}

	/**
	 * Get the response status code.
	 *
	 * @return int
	 */
	public function get_status_code(): int {
		return $this->status_code;
	}

	/**
	 * Whether the response is considered successful.
	 *
	 * @throws Exception If not success.
	 */
	public function validate(): void {
		if (
			! (
				$this->get_status_code() >= 200 &&
				$this->get_status_code() < 300
			)
		) {
			$this->log_and_throw_error(
				__( 'Failed to process response.', 'flo-checkout-plugin' ),
				array(
					'status_code' => $this->get_status_code(),
					'url'         => $this->url,
					'method'      => $this->method,
					'body'        => $this->body_string,
				),
				'Failed to validate response status code.'
			);
		}
	}
}
