<?php
/**
 * File Path: includes/api/esp/class-mbs-esp-authoremail.php
 *
 * Author.Email ESP Adapter -- My Book Showroom
 *
 * API: MailWizz-powered REST API at https://author.email/email/api/
 * Auth: X-MW-PUBLIC-KEY header (single public key, no secret required).
 * Lists: Standard MailWizz list objects. list_uid is the platform list ID.
 * Subscribe: POST /lists/{uid}/subscribers/create-update (upsert -- no
 *            duplicate error on re-subscribe).
 * Campaigns: Full CRUD via /campaigns. MailWizz has no "send immediately"
 *            endpoint -- campaigns are scheduled via send_at. This adapter
 *            schedules +2 minutes from now on send_campaign().
 * Tags: MailWizz has no native tag endpoint. supports_tags() returns false;
 *       tag_contact() is a no-op returning true (same pattern as Sendy).
 *
 * Sprint F-028: Author.Email Adapter.
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Api/Esp
 * @since      1.1.2
 */

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

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

	protected const BASE_URL        = 'https://author.email/email/api/';

	/** @var string */
	private string $api_key;

	public function __construct( string $api_key ) {
		$this->api_key = $api_key;
	}

	// -------------------------------------------------------------------------
	// Interface implementation
	// -------------------------------------------------------------------------

	/** {@inheritDoc} */
	public function connect(): bool {
		$response = $this->get( 'lists?page=1&per_page=1' );
		if ( is_wp_error( $response ) ) {
			return false;
		}
		return 200 === (int) wp_remote_retrieve_response_code( $response );
	}

	/** {@inheritDoc} */
	public function get_lists(): array {
		$response = $this->get( 'lists' );
		if ( is_wp_error( $response ) ) {
			return array();
		}
		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( empty( $body['data']['records'] ) || ! is_array( $body['data']['records'] ) ) {
			return array();
		}
		$result = array();
		foreach ( $body['data']['records'] as $list ) {
			$uid  = (string) ( $list['list_uid'] ?? '' );
			$name = (string) ( $list['general']['name'] ?? '' );
			if ( '' !== $uid && '' !== $name ) {
				$result[] = array(
					'id'   => $uid,
					'name' => $name,
				);
			}
		}
		return $result;
	}

	/** {@inheritDoc} */
	public function subscribe( string $email, string $name, string $list_id, bool $double_optin ): bool {
		if ( '' === $list_id ) {
			return false;
		}
		$payload = array(
			'EMAIL' => $email,
		);
		if ( '' !== $name ) {
			$payload['FNAME'] = $name;
		}
		// create-update is an upsert: creates the subscriber if new, updates if
		// already exists. Double opt-in confirmation is governed by the list's
		// MailWizz dashboard setting, not the API call.
		$response = $this->post( 'lists/' . rawurlencode( $list_id ) . '/subscribers/create-update', $payload );
		if ( is_wp_error( $response ) ) {
			return false;
		}
		$code = (int) wp_remote_retrieve_response_code( $response );
		return $code >= 200 && $code < 300;
	}

	/**
	 * {@inheritDoc}
	 * Author.Email (MailWizz) has no native tag endpoint.
	 * Returns true as a silent no-op so callers do not treat this as an error.
	 */
	public function tag_contact( string $email, array $tags, string $list_id ): bool {
		return true;
	}

	/** {@inheritDoc} */
	public function supports_tags(): bool {
		return false;
	}

	/**
	 * {@inheritDoc}
	 * Creates a regular campaign scheduled +2 minutes from now.
	 * MailWizz has no "send immediately" API endpoint; +2 min is the
	 * standard programmatic workaround and fires within the next cron tick.
	 */
	public function create_campaign( string $subject, string $body_html, string $list_id ): ?string {
		if ( '' === $list_id || '' === $subject || '' === $body_html ) {
			return null;
		}
		$from_name  = mbs_get_setting( 'email', 'from_name', '' );
		if ( empty( $from_name ) ) {
			$from_name = get_bloginfo( 'name' );
		}
		$reply_to   = mbs_get_setting( 'email', 'reply_to', '' );
		if ( empty( $reply_to ) ) {
			$reply_to = get_option( 'admin_email', '' );
		}
		$payload = array(
			'name'             => $subject,
			'type'             => 'regular',
			'from_name'        => $from_name,
			'from_email'       => $reply_to,
			'reply_to'         => $reply_to,
			'subject'          => $subject,
			'send_at'          => gmdate( 'Y-m-d H:i:s', time() + 120 ),
			'list_uid'         => $list_id,
			'template'         => array(
				'html_content'      => $body_html,
				'plain_text_email'  => 'no',
			),
		);
		$response = $this->post( 'campaigns', $payload );
		if ( is_wp_error( $response ) ) {
			return null;
		}
		$code = (int) wp_remote_retrieve_response_code( $response );
		if ( $code < 200 || $code >= 300 ) {
			return null;
		}
		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		$uid  = (string) ( $body['data']['record']['campaign_uid'] ?? '' );
		return '' !== $uid ? $uid : null;
	}

	/**
	 * {@inheritDoc}
	 * Updates the campaign's send_at to now+2 minutes to trigger immediate
	 * delivery on the next MailWizz cron tick.
	 */
	public function send_campaign( string $campaign_id ): bool {
		if ( '' === $campaign_id ) {
			return false;
		}
		$payload  = array(
			'send_at' => gmdate( 'Y-m-d H:i:s', time() + 120 ),
		);
		$response = $this->request( 'PUT', 'campaigns/' . rawurlencode( $campaign_id ), $payload );
		if ( is_wp_error( $response ) ) {
			return false;
		}
		$code = (int) wp_remote_retrieve_response_code( $response );
		return $code >= 200 && $code < 300;
	}

	/** {@inheritDoc} */
	public function supports_campaigns(): bool {
		return true;
	}

	// -------------------------------------------------------------------------
	// Private helpers
	// -------------------------------------------------------------------------

	/**
	 * Return the authentication header array.
	 *
	 * @return array<string,string>
	 */
	protected function auth_headers(): array {
		return array(
			'X-MW-PUBLIC-KEY' => $this->api_key,
		);
	}
}
