<?php
/**
 * Campaign Monitor API Client
 *
 * @package Sequensy\Integrations\CampaignMonitor
 * @since 1.0.0
 */

declare(strict_types=1);

namespace Sequensy\Integrations\CampaignMonitor;

use Sequensy\Integrations\AbstractApiClient;
use WP_Error;

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Campaign Monitor API client.
 *
 * Handles authenticated requests to the Campaign Monitor API
 * for subscriber and list management.
 *
 * @since 1.0.0
 */
class CampaignMonitorApi extends AbstractApiClient {

	/**
	 * Campaign Monitor API key.
	 *
	 * @var string
	 */
	private string $api_key;

	/**
	 * Campaign Monitor Client ID.
	 *
	 * @var string
	 */
	private string $client_id;

	/**
	 * Constructor.
	 *
	 * @param string $api_key   Campaign Monitor API key.
	 * @param string $client_id Campaign Monitor Client ID.
	 */
	public function __construct( string $api_key, string $client_id ) {
		$this->api_key   = $api_key;
		$this->client_id = $client_id;
		$this->init_circuit_breaker();
	}

	/**
	 * Get the Campaign Monitor API base URL.
	 *
	 * @return string Base URL for API requests.
	 */
	protected function get_base_url(): string {
		return 'https://api.createsend.com/api/v3.3/';
	}

	/**
	 * Get authentication headers for API requests.
	 *
	 * @return array Authorization headers with Basic auth.
	 */
	protected function get_auth_headers(): array {
		return array(
			'Authorization' => $this->create_basic_auth( $this->api_key, 'x' ),
		);
	}

	/**
	 * Get the error code prefix for WP_Error responses.
	 *
	 * @return string Error code identifier.
	 */
	protected function get_error_code(): string {
		return 'campaignmonitor_api_error';
	}

	/**
	 * Parse error message from API response.
	 *
	 * @param array|null $decoded     Decoded JSON response body.
	 * @param int        $status_code HTTP status code.
	 * @return string Human-readable error message.
	 */
	protected function parse_error_message( ?array $decoded, int $status_code ): string {
		if ( isset( $decoded['Message'] ) ) {
			return $decoded['Message'];
		}

		return sprintf(
			/* translators: %d: HTTP status code */
			__( 'Campaign Monitor API error (HTTP %d)', 'sequensy' ),
			$status_code
		);
	}

	/**
	 * Get the circuit breaker key for this API.
	 *
	 * @return string Unique key for circuit breaker state.
	 */
	protected function get_circuit_key(): string {
		return 'campaignmonitor_api';
	}

	/**
	 * Validate API credentials by listing clients.
	 *
	 * @return array|WP_Error Validation result or error.
	 */
	public function validate_credentials(): array|WP_Error {
		return $this->request( 'GET', 'clients.json' );
	}

	/**
	 * Add a subscriber to a list.
	 *
	 * @param string $list_id Campaign Monitor list ID.
	 * @param array  $data    Subscriber data (EmailAddress, Name, etc.).
	 * @return array|WP_Error Response or error.
	 */
	public function add_subscriber( string $list_id, array $data ): array|WP_Error {
		return $this->request( 'POST', sprintf( 'subscribers/%s.json', $list_id ), $data );
	}

	/**
	 * Update an existing subscriber.
	 *
	 * @param string $list_id Campaign Monitor list ID.
	 * @param string $email   Current subscriber email.
	 * @param array  $data    Updated subscriber data.
	 * @return array|WP_Error Response or error.
	 */
	public function update_subscriber( string $list_id, string $email, array $data ): array|WP_Error {
		$endpoint = sprintf( 'subscribers/%s.json?email=%s', $list_id, rawurlencode( $email ) );
		return $this->request( 'PUT', $endpoint, $data );
	}

	/**
	 * Get subscriber lists for the configured client.
	 *
	 * @return array|WP_Error Array of lists [{id, name}, ...] or error.
	 */
	public function get_lists(): array|WP_Error {
		$result = $this->request( 'GET', sprintf( 'clients/%s/lists.json', $this->client_id ) );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return array_map(
			fn( array $list ) => array(
				'id'   => $list['ListID'],
				'name' => $list['Name'],
			),
			$result['data']
		);
	}

	/**
	 * Unsubscribe a subscriber from a list.
	 *
	 * @param string $list_id Campaign Monitor list ID.
	 * @param string $email   Subscriber email to unsubscribe.
	 * @return array|WP_Error Response or error.
	 */
	public function unsubscribe( string $list_id, string $email ): array|WP_Error {
		return $this->request(
			'POST',
			sprintf( 'subscribers/%s/unsubscribe.json', $list_id ),
			array( 'EmailAddress' => $email )
		);
	}
}
