<?php
/**
 * Postmark REST transport.
 *
 * @package MailDoctor
 */

namespace MailDoctor\Transports;

use WP_Error;

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

/**
 * Handles Postmark API deliveries.
 */
class Postmark {
	/**
	 * API host.
	 */
	const API_BASE = 'https://api.postmarkapp.com';

	/**
	 * Send message via Postmark.
	 *
	 * @param array<string,mixed> $args      Normalized mail args.
	 * @param array<string,mixed> $settings  Plugin settings.
	 * @return array<string,mixed>|WP_Error
	 */
	public static function send( array $args, array $settings ) {
		$token = trim( (string) ( $settings['postmark_api_token'] ?? '' ) );
		if ( '' === $token ) {
			return new WP_Error( 'mail_doctor_postmark_missing_token', __( 'Postmark server token is not configured.', 'mail-doctor' ) );
		}

		$recipients = Utils::recipients_from_args( $args );
		if ( empty( $recipients['to'] ) ) {
			return new WP_Error( 'mail_doctor_postmark_missing_to', __( 'Postmark requires at least one primary recipient.', 'mail-doctor' ) );
		}

		$sender = Utils::sender_from_settings( $settings );
		if ( empty( $sender['email'] ) || ! is_email( $sender['email'] ) ) {
			return new WP_Error( 'mail_doctor_postmark_invalid_sender', __( 'A valid From email address is required to send via Postmark.', 'mail-doctor' ) );
		}

		$bodies = Utils::message_bodies( $args );

		$payload = array(
			'From'     => Utils::format_address( $sender ),
			'To'       => self::format_recipient_string( $recipients['to'] ),
			'Subject'  => (string) ( $args['subject'] ?? '' ),
			'TextBody' => (string) $bodies['text'],
		);

		if ( null !== $bodies['html'] ) {
			$payload['HtmlBody'] = (string) $bodies['html'];
		}

		if ( ! empty( $recipients['cc'] ) ) {
			$payload['Cc'] = self::format_recipient_string( $recipients['cc'] );
		}

		if ( ! empty( $recipients['bcc'] ) ) {
			$payload['Bcc'] = self::format_recipient_string( $recipients['bcc'] );
		}

		if ( ! empty( $recipients['reply_to'] ) ) {
			$payload['ReplyTo'] = Utils::format_address( $recipients['reply_to'] );
		}

		$headers_map = Utils::headers_to_map( $args['headers'] ?? array() );
		if ( ! empty( $headers_map ) ) {
			$payload['Headers'] = array();
			foreach ( $headers_map as $name => $value ) {
				$payload['Headers'][] = array(
					'Name'  => $name,
					'Value' => $value,
				);
			}
		}

		$attachments = Utils::attachment_payloads( $args['attachments'] ?? array() );
		if ( ! empty( $attachments ) ) {
			$payload['Attachments'] = array();
			foreach ( $attachments as $attachment ) {
				$payload['Attachments'][] = array(
					'Name'        => $attachment['name'],
					'Content'     => $attachment['content'],
					'ContentType' => $attachment['type'],
				);
			}
		}

		$response = wp_remote_post(
			self::API_BASE . '/email',
			array(
				'headers' => array(
					'X-Postmark-Server-Token' => $token,
					'Accept'                  => 'application/json',
					'Content-Type'            => 'application/json',
				),
				'timeout' => 30,
				'body'    => wp_json_encode( $payload ),
			)
		);

		if ( is_wp_error( $response ) ) {
			return new WP_Error( 'mail_doctor_postmark_request_error', $response->get_error_message(), array( 'transport' => 'postmark' ) );
		}

		$code = wp_remote_retrieve_response_code( $response );
		$body = wp_remote_retrieve_body( $response );

		if ( 200 !== $code ) {
			$message = __( 'Postmark API request failed.', 'mail-doctor' );
			if ( $body ) {
				$json = json_decode( $body, true );
				if ( isset( $json['Message'] ) ) {
					$message = (string) $json['Message'];
				}
			}

			return new WP_Error(
				'mail_doctor_postmark_failed',
				$message,
				array(
					'status'   => $code,
					'response' => $body,
				)
			);
		}

		$json          = json_decode( $body, true );
		$provider_code = isset( $json['MessageID'] ) ? (string) $json['MessageID'] : '';

		return array(
			'provider_code' => $provider_code,
			'status'        => (int) $code,
			'raw'           => $body,
		);
	}

	/**
	 * Verify connection to Postmark API.
	 *
	 * @param array<string,mixed> $settings Plugin settings.
	 * @return array<string,mixed>
	 */
	public static function check_connection( array $settings ): array {
		$token = trim( (string) ( $settings['postmark_api_token'] ?? '' ) );
		if ( '' === $token ) {
			return array(
				'success' => false,
				'message' => __( 'Postmark server token is missing.', 'mail-doctor' ),
				'data'    => array(
					'host'       => 'api.postmarkapp.com',
					'encryption' => 'https',
					'auth'       => false,
					'debug'      => array(),
				),
			);
		}

		$url   = self::API_BASE . '/server';
		$debug = array( sprintf( 'GET %s', $url ) );

		$response = wp_remote_get(
			$url,
			array(
				'timeout' => 15,
				'headers' => array(
					'X-Postmark-Server-Token' => $token,
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			$debug[] = $response->get_error_message();
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
				'data'    => array(
					'host'       => 'api.postmarkapp.com',
					'encryption' => 'https',
					'auth'       => false,
					'debug'      => $debug,
				),
			);
		}

		$code    = wp_remote_retrieve_response_code( $response );
		$debug[] = sprintf( 'Response: %d %s', $code, wp_remote_retrieve_response_message( $response ) );

		if ( 200 === $code ) {
			return array(
				'success' => true,
				'message' => __( 'Postmark API credentials verified successfully.', 'mail-doctor' ),
				'data'    => array(
					'host'       => 'api.postmarkapp.com',
					'encryption' => 'https',
					'auth'       => true,
					'debug'      => $debug,
				),
			);
		}

		$body = wp_remote_retrieve_body( $response );
		if ( $body ) {
			$debug[] = $body;
		}

		return array(
			'success' => false,
			'message' => __( 'Postmark API request was rejected. Confirm the server token permissions.', 'mail-doctor' ),
			'data'    => array(
				'host'       => 'api.postmarkapp.com',
				'encryption' => 'https',
				'auth'       => false,
				'debug'      => $debug,
			),
		);
	}

	/**
	 * Convert list of addresses to comma separated string.
	 *
	 * @param array<int,array<string,string>> $addresses Recipients.
	 * @return string
	 */
	protected static function format_recipient_string( array $addresses ): string {
		$parts = array();
		foreach ( $addresses as $address ) {
			$parts[] = Utils::format_address( $address );
		}
		return implode( ',', array_filter( $parts ) );
	}
}
