<?php
/**
 * Postmark email API provider.
 *
 * @package EmailSmtp
 * @link https://postmarkapp.com/developer/api/email-api
 */

namespace EmailSmtp\Providers\Postmark;

use EmailSmtp\Includes\AbstractApiProvider;
use EmailSmtp\Includes\ApiHttpClient;
use EmailSmtp\Includes\DeliveryResult;
use EmailSmtp\Includes\EmailMessage;
use EmailSmtp\Includes\ErrorNormalizer;

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

final class Provider extends AbstractApiProvider {

	/** Max addresses per To / Cc / Bcc (comma-separated). */
	private const RECIPIENT_CAP = 50;

	public function get_id(): string {
		return 'postmark';
	}

	public function get_name(): string {
		return __( 'Postmark', 'wp-powersuite' );
	}

	public function get_description(): string {
		return __( 'Send transactional email through the Postmark Email API.', 'wp-powersuite' );
	}

	public function get_recommended_use(): string {
		return __( 'Transactional email with message streams.', 'wp-powersuite' );
	}

	public function get_docs_url(): string {
		return 'https://postmarkapp.com/developer/api/email-api';
	}

	public function get_icon(): string {
		return 'dashicons-email-alt2';
	}

	public function get_required_fields(): array {
		return [ 'postmark_server_token' ];
	}


	/**
	 * @param array<string, mixed> $settings
	 * @return array<string, string>
	 */
	private function auth_headers( array $settings ): array {
		$token = $this->secret( $settings, 'postmark_server_token', 'WP_POWERSUITE_POSTMARK_SERVER_TOKEN' );
		return [
			'X-Postmark-Server-Token' => $token,
			'Accept'                  => 'application/json',
			'Content-Type'            => 'application/json',
		];
	}

	/**
	 * @param list<string> $emails
	 */
	private function address_list( array $emails ): string {
		return implode( ',', array_slice( $emails, 0, self::RECIPIENT_CAP ) );
	}

	public function connect( array $settings ): DeliveryResult {
		$token = $this->secret( $settings, 'postmark_server_token', 'WP_POWERSUITE_POSTMARK_SERVER_TOKEN' );
		if ( $token === '' ) {
			return DeliveryResult::failure( __( 'Postmark server token is required.', 'wp-powersuite' ), '', 'authentication' );
		}

		$response = ApiHttpClient::request(
			'GET',
			'https://api.postmarkapp.com/server',
			$this->auth_headers( $settings )
		);

		return ApiHttpClient::to_delivery_result(
			$response,
			static function () {
				return [ 'message' => __( 'Postmark server token authenticated successfully.', 'wp-powersuite' ) ];
			}
		);
	}

	public function send( EmailMessage $message, array $settings ): DeliveryResult {
		$missing = $this->require_from( $message );
		if ( $missing ) {
			return $missing;
		}

		$token = $this->secret( $settings, 'postmark_server_token', 'WP_POWERSUITE_POSTMARK_SERVER_TOKEN' );
		if ( $token === '' ) {
			return DeliveryResult::failure( __( 'Postmark server token is required.', 'wp-powersuite' ), '', 'authentication' );
		}

		$from    = $this->from_parts( $message );
		$payload = [
			'From'          => $this->format_address( $from['email'], $from['name'] ),
			'To'            => $this->address_list( $message->get_to() ),
			'Subject'       => $message->get_subject(),
			'MessageStream' => sanitize_text_field( (string) ( $settings['postmark_message_stream'] ?? 'outbound' ) ) ?: 'outbound',
		];

		if ( $message->is_html() ) {
			$payload['HtmlBody'] = $message->get_body();
			$payload['TextBody'] = $message->get_alt_body() !== ''
				? $message->get_alt_body()
				: wp_strip_all_tags( $message->get_body() );
		} else {
			$payload['TextBody'] = $message->get_body();
		}

		if ( $message->get_cc() !== [] ) {
			$payload['Cc'] = $this->address_list( $message->get_cc() );
		}
		if ( $message->get_bcc() !== [] ) {
			$payload['Bcc'] = $this->address_list( $message->get_bcc() );
		}
		if ( is_email( $message->get_reply_to_email() ) ) {
			$payload['ReplyTo'] = $this->format_address(
				$message->get_reply_to_email(),
				$message->get_reply_to_name()
			);
		}

		$attachments = $this->attachments_as_base64( $message );
		if ( $attachments !== [] ) {
			$payload['Attachments'] = array_map(
				static function ( array $a ) {
					return [
						'Name'        => $a['name'],
						'Content'     => $a['content'],
						'ContentType' => $a['type'] !== '' ? $a['type'] : 'application/octet-stream',
					];
				},
				$attachments
			);
		}

		$headers = $this->safe_custom_headers( $message );
		if ( $headers !== [] ) {
			$payload['Headers'] = [];
			foreach ( $headers as $name => $value ) {
				$payload['Headers'][] = [
					'Name'  => (string) $name,
					'Value' => (string) $value,
				];
			}
		}

		$response = ApiHttpClient::request(
			'POST',
			'https://api.postmarkapp.com/email',
			$this->auth_headers( $settings ),
			$payload
		);

		if ( ! empty( $response['error'] ) ) {
			return ErrorNormalizer::from_text( (string) $response['error'] );
		}

		// Postmark returns HTTP 200 with ErrorCode 0 on success; non-zero ErrorCode is a failure.
		$json       = is_array( $response['json'] ?? null ) ? $response['json'] : null;
		$error_code = is_array( $json ) ? (int) ( $json['ErrorCode'] ?? 0 ) : 0;
		$code       = (int) ( $response['code'] ?? 0 );

		if ( $code >= 200 && $code <= 299 && $error_code !== 0 ) {
			$msg = is_array( $json ) && ! empty( $json['Message'] ) && is_string( $json['Message'] )
				? $json['Message']
				: __( 'Postmark rejected the message.', 'wp-powersuite' );
			$result = ErrorNormalizer::from_text( $msg . ' (ErrorCode ' . $error_code . ')', (string) $code );
			return DeliveryResult::failure(
				$result->get_user_message(),
				$result->get_technical_message(),
				$result->get_category(),
				(string) $error_code,
				$result->is_retry_safe(),
				__( 'Verify the From address is a confirmed Sender Signature and the message stream is correct.', 'wp-powersuite' ),
				(int) ( $response['duration_ms'] ?? 0 )
			);
		}

		return ApiHttpClient::to_delivery_result(
			$response,
			static function ( array $res ) {
				$id = is_array( $res['json'] ) ? (string) ( $res['json']['MessageID'] ?? '' ) : '';
				return [
					'message' => __( 'Postmark accepted the message.', 'wp-powersuite' ),
					'id'      => $id,
				];
			}
		);
	}
}
