<?php
/**
 * REST API base.
 *
 * @package northmill-online-shared
 */

declare(strict_types=1);

namespace Northmill_Online_Shared\Abstracts\REST;

use Northmill_Online_Shared\Abstract_Shared_Base;
use Northmill_Online_Shared\Helpers\Hook_Helpers;

/**
 * Base class for REST API.
 */
abstract class Abstract_API {

	private const GLOBAL_KEY = 'rest_api_roots';

	/**
	 * The routes for this API.
	 *
	 * @var Abstract_Route[]
	 */
	private array $routes = array();

	/**
	 * Constructor
	 */
	public function __construct() {
		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
		add_filter( 'woocommerce_is_rest_api_request', array( $this, 'filter_routes_loading_full_wc' ) );
		add_action( 'init', array( $this, 'prep_js_data' ), 10, 0 );

		Abstract_Shared_Base::base_instance()->globals->add_unique_action(
			'wp_head',
			array( __CLASS__, 'header_js_data' ),
			10,
			0
		);

		$this->construct_routes();
	}

	/**
	 * Prepares the JS data for the REST API.
	 */
	public function prep_js_data(): void {
		Abstract_Shared_Base::base_instance()->globals->append(
			self::GLOBAL_KEY,
			array(
				'rest_root' => esc_url_raw( rest_url( $this->get_rest_namespace() ) ),
				'nonce'     => Abstract_Route_Nonce::create_nonce(),
			),
			$this->get_rest_namespace()
		);
	}

	/**
	 * Retrieves the REST namespace for the class.
	 *
	 * Example value: 'northmill/express-order'
	 *
	 * @return string The REST namespace for the class.
	 */
	abstract public function get_rest_namespace(): string;

	/**
	 * Retrieves the nonce action for the current REST namespace.
	 *
	 * @return string The nonce action for the current REST namespace.
	 */
	public function get_nonce_action(): string {
		return sanitize_title( $this->get_rest_namespace() ) . '_wp_rest';
	}

	/**
	 * Can be overwritten to return an array of routes that should load the full WC environment.
	 * Example value: '/v1/checkout/info'.
	 *
	 * @return array
	 */
	public function get_routes_loading_wc(): array {
		return array();
	}

	/**
	 * Retrieves the root URL of the REST API.
	 *
	 * @return string The root URL of the REST API.
	 * @noinspection PhpUnused Used by packages loading this composer package.
	 */
	public function get_api_root(): string {
		return rest_url( $this->get_rest_namespace() );
	}

	/**
	 * Registers the REST routes for this API.
	 * This should construct classes extending the Abstract_Route class.
	 *
	 * @return void
	 */
	abstract public function construct_routes(): void;

	/**
	 * Registers the REST routes for this API.
	 * This should register the routes constructed in the construct_routes method.
	 *
	 * @return void
	 */
	final public function register_routes(): void {
		foreach ( $this->routes as $route ) {
			$route->register_rest_route();
		}
	}

	/**
	 * Stores a constructed route.
	 *
	 * @param Abstract_Route $route The route to store.
	 *
	 * @return void
	 */
	public function store_route(
		Abstract_Route $route,
	): void {
		$this->routes[ $route->get_route() ] = $route;
	}

	/**
	 * Retrieves the routes for this API.
	 *
	 * @param string $route The route to retrieve.
	 *
	 * @return Abstract_Route|null
	 */
	public function get_route( string $route ): ?Abstract_Route {
		return $this->routes[ $route ] ?? null;
	}

	/**
	 * Filters whether the WooCommerce routes should load the full WC environment.
	 *
	 * @param boolean $is_rest_api_request Whether the current request is a REST API request or not.
	 *
	 * @return boolean Whether to load the full WC environment for the WooCommerce routes or not.
	 */
	public function filter_routes_loading_full_wc( bool $is_rest_api_request ): bool {
		if ( empty( $_SERVER['REQUEST_URI'] ) ) {
			return $is_rest_api_request;
		}

		// Bail early if this is not our request.
		foreach ( $this->get_routes_loading_wc() as $route ) {
			if ( str_contains( $_SERVER['REQUEST_URI'], $this->get_rest_namespace() . $route ) ) {
				return false;
			}
		}

		return $is_rest_api_request;
	}

	/**
	 * Adds the JS data to WP Head.
	 */
	public static function header_js_data(): void {
		$data = Hook_Helpers::apply_scoped_filters(
			'northmill_online_shared_rest_api_js_data',
			Abstract_Shared_Base::base_instance()->globals->get(
				self::GLOBAL_KEY,
			),
		);

		echo '<script type="text/javascript">';
		echo 'const nmsRestApiData = ' . wp_json_encode( $data ) . ';';
		echo '</script>';
	}
}
