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

namespace MailDoctor\Transports;

use WP_Error;

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

/**
 * Handles SparkPost transmissions API.
 */
class SparkPost {
	/**
	 * Default API endpoint.
	 */
	const API_BASE = 'https://api.sparkpost.com';

	/**
	 * Deliver message through SparkPost.
	 *
	 * @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 ) {
		$api_key  = trim( (string) ( $settings['sparkpost_api_key'] ?? '' ) );
		$endpoint = trim( (string) ( $settings['sparkpost_api_endpoint'] ?? self::API_BASE ) );

		if ( '' === $api_key ) {
			return new WP_Error( 'mail_doctor_sparkpost_missing_key', __( 'SparkPost API key is not configured.', 'mail-doctor' ) );
		}

		$endpoint = rtrim( $endpoint, '/' );

		$recipients = Utils::recipients_from_args( $args );
		if ( empty( $recipients['to'] ) ) {
			return new WP_Error( 'mail_doctor_sparkpost_missing_to', __( 'SparkPost 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_sparkpost_invalid_sender', __( 'A valid From email address is required to send via SparkPost.', 'mail-doctor' ) );
		}

		$bodies         = Utils::message_bodies( $args );
		$to_header      = self::format_header( $recipients['to'] );
		$all_recipients = self::build_recipients( $recipients, $to_header );

		$content = array(
			'from'    => array( 'email' => $sender['email'] ),
			'subject' => (string) ( $args['subject'] ?? '' ),
			'text'    => (string) $bodies['text'],
		);

		if ( ! empty( $sender['name'] ) ) {
			$content['from']['name'] = $sender['name'];
		}

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

		$headers_map = Utils::headers_to_map( $args['headers'] ?? array() );
		if ( ! empty( $headers_map ) ) {
			$content['headers'] = $headers_map;
		}

		if ( ! empty( $recipients['cc'] ) ) {
			$content['headers']['CC'] = self::format_header( $recipients['cc'] );
		}

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

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

		$payload = array(
			'recipients' => $all_recipients,
			'content'    => $content,
		);

		$response = wp_remote_post(
			$endpoint . '/api/v1/transmissions',
			array(
				'timeout' => 30,
				'headers' => array(
					'Authorization' => $api_key,
					'Content-Type'  => 'application/json',
				),
				'body'    => wp_json_encode( $payload ),
			)
		);

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

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

		if ( $code < 200 || $code >= 300 ) {
			$message = __( 'SparkPost API request failed.', 'mail-doctor' );
			if ( $body ) {
				$json = json_decode( $body, true );
				if ( isset( $json['errors'][0]['message'] ) ) {
					$message = (string) $json['errors'][0]['message'];
				}
			}

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

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

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

	/**
	 * Verify SparkPost credentials.
	 *
	 * @param array<string,mixed> $settings Plugin settings.
	 * @return array<string,mixed>
	 */
	public static function check_connection( array $settings ): array {
		$api_key  = trim( (string) ( $settings['sparkpost_api_key'] ?? '' ) );
		$endpoint = trim( (string) ( $settings['sparkpost_api_endpoint'] ?? self::API_BASE ) );

		if ( '' === $api_key ) {
			return array(
				'success' => false,
				'message' => __( 'SparkPost API key is missing.', 'mail-doctor' ),
				'data'    => array(
					'host'       => wp_parse_url( self::API_BASE, PHP_URL_HOST ),
					'encryption' => 'https',
					'auth'       => false,
					'debug'      => array(),
				),
			);
		}

		$endpoint = rtrim( $endpoint, '/' );
		$host     = wp_parse_url( $endpoint, PHP_URL_HOST );
		$url      = $endpoint . '/api/v1/account';
		$debug    = array( sprintf( 'GET %s', $url ) );

		$response = wp_remote_get(
			$url,
			array(
				'timeout' => 15,
				'headers' => array(
					'Authorization' => $api_key,
					'Accept'        => 'application/json',
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			$debug[] = $response->get_error_message();
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
				'data'    => array(
					'host'       => $host,
					'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' => __( 'SparkPost API credentials verified successfully.', 'mail-doctor' ),
				'data'    => array(
					'host'       => $host,
					'encryption' => 'https',
					'auth'       => true,
					'debug'      => $debug,
				),
			);
		}

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

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

	/**
	 * Build recipient list for SparkPost payload.
	 *
	 * @param array<string,array> $recipients Recipient buckets.
	 * @param string              $header_to  Header To string.
	 * @return array<int,array<string,mixed>>
	 */
	protected static function build_recipients( array $recipients, string $header_to ): array {
		$entries = array();

		foreach ( $recipients['to'] as $address ) {
			$entry = array( 'address' => array( 'email' => $address['email'] ) );
			if ( ! empty( $address['name'] ) ) {
				$entry['address']['name'] = $address['name'];
			}
			$entries[] = $entry;
		}

		$cc_and_bcc = array_merge( $recipients['cc'], $recipients['bcc'] );
		foreach ( $cc_and_bcc as $address ) {
			$entry = array(
				'address' => array(
					'email'     => $address['email'],
					'header_to' => $header_to,
				),
			);
			if ( ! empty( $address['name'] ) ) {
				$entry['address']['name'] = $address['name'];
			}
			$entries[] = $entry;
		}

		return $entries;
	}

	/**
	 * Produce header string for recipients.
	 *
	 * @param array<int,array<string,string>> $addresses Addresses.
	 * @return string
	 */
	protected static function format_header( array $addresses ): string {
		return implode( ', ', array_filter( array_map( array( Utils::class, 'format_address' ), $addresses ) ) );
	}
}
