<?php
/**
 * File Path: includes/api/esp/class-mbs-esp-mailchimp.php
 *
 * Mailchimp ESP Adapter -- My Book Showroom
 *
 * API: Mailchimp Marketing API v3.
 * Auth: HTTP Basic anystring:{api_key}.
 * Base URL: https://{dc}.api.mailchimp.com/3.0/
 * (dc extracted from API key suffix, e.g. key ending in -us6 -> dc = us6).
 *
 * Sprint F-026: Email Channel Integration.
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Api/Esp
 * @since      1.1.0
 */

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

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

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

	/** @var string Datacenter prefix extracted from API key. */
	private string $dc;

	/** @var string Base URL for all requests. */
	private string $base_url;

	/**
	 * @param string $api_key Decrypted Mailchimp API key.
	 */
	public function __construct( string $api_key ) {
		$this->api_key  = $api_key;
		// Extract datacenter: key format is '{token}-{dc}' e.g. 'abc123-us6'.
		$parts      = explode( '-', $api_key );
		$raw_dc     = count( $parts ) > 1 ? end( $parts ) : 'us1';
		// SA-L10: Validate DC suffix format before injecting into URL hostname.
		$this->dc   = ( 1 === preg_match( '/^[a-z]{2}[0-9]+$/', $raw_dc ) ) ? $raw_dc : 'us1';
		$this->base_url = 'https://' . $this->dc . '.api.mailchimp.com/3.0/';
	}

	protected function endpoint_url( string $endpoint ): string {
		return $this->base_url . $endpoint;
	}

	/** {@inheritDoc} */
	public function connect(): bool {
		$response = $this->get( 'ping' );
		if ( is_wp_error( $response ) ) {
			return false;
		}
		$code = wp_remote_retrieve_response_code( $response );
		return 200 === (int) $code;
	}

	/** {@inheritDoc} */
	public function get_lists(): array {
		$count    = (int) apply_filters( 'mbs_mailchimp_list_count', 200 ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix
		$count    = min( max( 1, $count ), 1000 );
		$response = $this->get( 'lists?count=' . $count );
		if ( is_wp_error( $response ) ) {
			return array();
		}
		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( empty( $body['lists'] ) || ! is_array( $body['lists'] ) ) {
			return array();
		}
		$result = array();
		foreach ( $body['lists'] as $list ) {
			$result[] = array( 'id' => (string) $list['id'], 'name' => (string) $list['name'] );
		}
		return $result;
	}

	/** {@inheritDoc} */
	public function subscribe( string $email, string $name, string $list_id, bool $double_optin ): bool {
		$subscriber_hash = md5( strtolower( $email ) );
		$endpoint = 'lists/' . rawurlencode( $list_id ) . '/members/' . $subscriber_hash;
		$payload  = array(
			'email_address' => $email,
			'status_if_new'  => $double_optin ? 'pending' : 'subscribed',
			'status'         => 'subscribed',
		);
		if ( '' !== $name ) {
			// Split name into first/last for Mailchimp's merge fields.
			$parts  = explode( ' ', $name, 2 );
			$payload['merge_fields'] = array(
				'FNAME' => $parts[0],
				'LNAME' => isset( $parts[1] ) ? $parts[1] : '',
			);
		}
		$response = $this->request( 'PUT', $endpoint, $payload );
		if ( is_wp_error( $response ) ) {
			return false;
		}
		$code = (int) wp_remote_retrieve_response_code( $response );
		// 200 = updated existing, 201 = created new.
		return $code >= 200 && $code < 300;
	}

	/** {@inheritDoc} */
	public function tag_contact( string $email, array $tags, string $list_id ): bool {
		if ( empty( $tags ) ) {
			return true;
		}
		$subscriber_hash = md5( strtolower( $email ) );
		$endpoint = 'lists/' . rawurlencode( $list_id ) . '/members/' . $subscriber_hash . '/tags';
		$tag_objects = array();
		foreach ( $tags as $tag ) {
			$tag_objects[] = array( 'name' => (string) $tag, 'status' => 'active' );
		}
		$response = $this->request( 'POST', $endpoint, array( 'tags' => $tag_objects ) );
		if ( is_wp_error( $response ) ) {
			return false;
		}
		$code = (int) wp_remote_retrieve_response_code( $response );
		// Mailchimp returns 204 No Content on success for tag updates.
		return $code >= 200 && $code < 300;
	}

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

	/** {@inheritDoc} */
	public function create_campaign( string $subject, string $body_html, string $list_id ): ?string {
		// Step 1: Create the campaign.
		$sender_name  = get_bloginfo( 'name' );
		$sender_email = get_option( 'admin_email' );
		$settings_from_name  = mbs_get_setting( 'email', 'from_name', '' );
		$settings_reply_to   = mbs_get_setting( 'email', 'reply_to', '' );
		$create_payload = array(
			'type'       => 'regular',
			'recipients'  => array( 'list_id' => $list_id ),
			'settings'    => array(
				'subject_line' => $subject,
				'title'        => $subject,
				'from_name'    => ! empty( $settings_from_name ) ? $settings_from_name : $sender_name,
				'reply_to'     => ! empty( $settings_reply_to ) ? $settings_reply_to : $sender_email,
			),
		);
		$create_resp = $this->request( 'POST', 'campaigns', $create_payload, self::TIMEOUT_CAMPAIGN );
		if ( is_wp_error( $create_resp ) ) {
			return null;
		}
		$create_code = (int) wp_remote_retrieve_response_code( $create_resp );
		if ( $create_code < 200 || $create_code >= 300 ) {
			return null;
		}
		$create_body  = json_decode( wp_remote_retrieve_body( $create_resp ), true );
		$campaign_id  = $create_body['id'] ?? '';
		if ( '' === $campaign_id ) {
			return null;
		}
		// Step 2: Set campaign content.
		$content_endpoint = 'campaigns/' . rawurlencode( $campaign_id ) . '/content';
		$content_resp = $this->request( 'PUT', $content_endpoint, array( 'html' => $body_html ), self::TIMEOUT_CAMPAIGN );
		if ( is_wp_error( $content_resp ) ) {
			// Campaign created but content not set -- return ID so caller can retry.
			return $campaign_id;
		}
		return $campaign_id;
	}

	/** {@inheritDoc} */
	public function send_campaign( string $campaign_id ): bool {
		$endpoint = 'campaigns/' . rawurlencode( $campaign_id ) . '/actions/send';
		$response = $this->request( 'POST', $endpoint, array(), self::TIMEOUT_CAMPAIGN );
		if ( is_wp_error( $response ) ) {
			return false;
		}
		$code = (int) wp_remote_retrieve_response_code( $response );
		// Mailchimp returns 204 No Content on successful send.
		return 204 === $code;
	}

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

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



	/**
	 * Build Basic Auth headers for Mailchimp.
	 *
	 * @return array<string, string>
	 */
	protected function auth_headers(): array {
		return array( 'Authorization' => 'Basic ' . base64_encode( 'anystring:' . $this->api_key ) );
	}
}
