<?php
/**
 * File Path: includes/api/esp/class-mbs-esp-adapter-base.php
 *
 * ESP Adapter Base Class -- My Book Showroom
 *
 * Abstract base for all ESP HTTP adapters.  Provides concrete get(), post(),
 * and request() helpers built on the WordPress HTTP API so each concrete
 * adapter only needs to declare its BASE_URL constant and implement
 * auth_headers().  Dynamic-base-URL adapters (Mailchimp, ActiveCampaign)
 * override endpoint_url() instead of using the constant.
 *
 * DUP-006: extracted from 10 adapter files (Session 136).
 *
 * Subclass contract:
 *   protected const BASE_URL = 'https://...';  // override in each adapter
 *   protected function auth_headers(): array { ... }
 *
 * Optional override for dynamic base URLs:
 *   protected function endpoint_url( string $endpoint ): string { ... }
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Api/Esp
 * @since      1.1.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

abstract class MBS_ESP_Adapter_Base implements MBS_ESP_Adapter { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	/** Connection / subscribe timeout in seconds. */
	protected const TIMEOUT_CONNECT = 10;

	/** Campaign operation timeout in seconds. */
	protected const TIMEOUT_CAMPAIGN = 15;

	/**
	 * Base URL for API requests.
	 *
	 * Subclasses must redeclare this constant with their own URL.
	 * Adapters with a datacenter-based or dynamic URL instead override
	 * endpoint_url() and leave this constant at its empty default.
	 */
	protected const BASE_URL = '';

	// -------------------------------------------------------------------------
	// Abstract contract
	// -------------------------------------------------------------------------

	/**
	 * Return the HTTP headers required to authenticate with this ESP.
	 *
	 * Examples:
	 *   Bearer token:  array( 'Authorization' => 'Bearer ' . $this->api_key )
	 *   API key header: array( 'X-Api-Key' => $this->api_key )
	 *   Key-in-URL:    array() -- URL auth handled via endpoint_url() override
	 *
	 * @return array<string, string>
	 */
	abstract protected function auth_headers(): array;

	// -------------------------------------------------------------------------
	// HTTP helpers
	// -------------------------------------------------------------------------

	/**
	 * Build the full request URL for a relative endpoint.
	 *
	 * Default: static::BASE_URL . $endpoint.
	 *
	 * Adapters that compute their base URL at runtime (Mailchimp, ActiveCampaign)
	 * or embed auth in the URL (Moosend) override this method.
	 *
	 * @param string $endpoint Relative endpoint path (no leading slash required).
	 * @return string Full URL.
	 */
	protected function endpoint_url( string $endpoint ): string {
		return static::BASE_URL . $endpoint;
	}

	/**
	 * Execute a GET request against the ESP API.
	 *
	 * @param string $endpoint Relative endpoint.
	 * @return array|WP_Error
	 */
	protected function get( string $endpoint ) {
		return wp_remote_get( $this->endpoint_url( $endpoint ), array(
			'headers'   => $this->auth_headers(),
			'timeout'   => static::TIMEOUT_CONNECT,
			'sslverify' => true,
		) );
	}

	/**
	 * Execute a POST request with a JSON body against the ESP API.
	 *
	 * @param string $endpoint Relative endpoint.
	 * @param array  $body     Request body; JSON-encoded automatically.
	 * @param int    $timeout  Request timeout in seconds.
	 * @return array|WP_Error
	 */
	protected function post( string $endpoint, array $body = array(), int $timeout = self::TIMEOUT_CONNECT ) {
		$headers = array_merge( $this->auth_headers(), array( 'Content-Type' => 'application/json; charset=utf-8' ) );
		return wp_remote_post( $this->endpoint_url( $endpoint ), array(
			'headers'   => $headers,
			'body'      => wp_json_encode( $body ),
			'timeout'   => $timeout,
			'sslverify' => true,
		) );
	}

	/**
	 * Execute an arbitrary HTTP request (PUT, PATCH, DELETE, POST) with a JSON body.
	 *
	 * Use this instead of post() when the HTTP method is not POST.
	 *
	 * @param string $method   HTTP method ('PUT', 'PATCH', 'DELETE', 'POST').
	 * @param string $endpoint Relative endpoint.
	 * @param array  $body     Request body; JSON-encoded automatically.
	 * @param int    $timeout  Request timeout in seconds.
	 * @return array|WP_Error
	 */
	protected function request( string $method, string $endpoint, array $body = array(), int $timeout = self::TIMEOUT_CONNECT ) {
		$headers = array_merge( $this->auth_headers(), array( 'Content-Type' => 'application/json; charset=utf-8' ) );
		$args = array(
			'method'    => $method,
			'headers'   => $headers,
			'timeout'   => $timeout,
			'sslverify' => true,
		);
		if ( ! empty( $body ) ) {
			$args['body'] = wp_json_encode( $body );
		}
		return wp_remote_request( $this->endpoint_url( $endpoint ), $args );
	}
}
