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

namespace MailDoctor\Transports;

use WP_Error;

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

/**
 * Handles Mailgun REST delivery.
 */
class Mailgun {


	/**
	 * Dispatch email via Mailgun REST API.
	 *
	 * @param array<string,mixed> $args     Normalized mail arguments.
	 * @param array<string,mixed> $settings Plugin settings.
	 * @return array<string,mixed>|WP_Error
	 */
	public static function send( array $args, array $settings ) {
		$api_key  = $settings['mailgun_api_key'] ?? '';
		$domain   = $settings['mailgun_api_domain'] ?? '';
		$endpoint = trim( $settings['mailgun_api_endpoint'] ?? 'api.mailgun.net' );

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

		if ( '' === $domain ) {
			return new WP_Error( 'mailgun_missing_domain', __( 'Mailgun sending domain is not configured.', 'mail-doctor' ) );
		}

		$to = self::format_recipients( $args['to'] ?? array() );

		if ( empty( $to ) ) {
			return new WP_Error( 'mailgun_missing_recipients', __( 'Unable to determine message recipients.', 'mail-doctor' ) );
		}

		$from_email = $settings['from_email'] ?? get_option( 'admin_email' );
		$from_name  = $settings['from_name'] ?? get_bloginfo( 'name' );
		$from       = $from_name ? sprintf( '%s <%s>', $from_name, $from_email ) : $from_email;

		$body_type = self::detect_body_type( $args['headers'] ?? array() );
		$text_body = $args['message'] ?? '';
		$html_body = null;

		if ( 'html' === $body_type ) {
			$html_body = $text_body;
			$text_body = wp_strip_all_tags( $text_body );
		}

		$api_url = sprintf( 'https://%s/v3/%s/messages', $endpoint, $domain );

		$request_args = array(
			'body'    => self::build_body( $from, $to, $args['subject'] ?? '', $text_body, $html_body, $args['headers'] ?? array() ),
			'headers' => array(
				'Authorization' => 'Basic ' . base64_encode( 'api:' . $api_key ),
			),
			'timeout' => 30,
		);

		$attachments = $args['attachments'] ?? array();
		if ( ! empty( $attachments ) ) {
			$request_args['body']['attachment'] = self::prepare_attachments( $attachments );
		}

		$response = wp_remote_post( $api_url, $request_args );

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

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

		if ( $code < 200 || $code >= 300 ) {
			$message = __( 'Mailgun API request failed.', 'mail-doctor' );
			$data    = array();

			if ( $body ) {
				$json = json_decode( $body, true );
				if ( isset( $json['message'] ) ) {
					$message = (string) $json['message'];
				}
				$data['response'] = $body;
			}

			return new WP_Error( 'mailgun_api_error', $message, $data );
		}

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

		if ( $body ) {
			$json = json_decode( $body, true );
			if ( isset( $json['id'] ) ) {
				$result['provider_code'] = (string) $json['id'];
			}
		}

		return $result;
	}

	/**
	 * Build request payload.
	 *
	 * @param string            $from       Sender address.
	 * @param array<string>     $recipients Recipients.
	 * @param string            $subject    Subject.
	 * @param string            $text_body  Text body.
	 * @param string|null       $html_body  HTML body.
	 * @param array<int,string> $headers    Headers.
	 * @return array<string,mixed>
	 */
	protected static function build_body( string $from, array $recipients, string $subject, string $text_body, ?string $html_body, array $headers ): array {
		$body = array(
			'from'    => $from,
			'to'      => implode( ',', $recipients ),
			'subject' => $subject,
			'text'    => $text_body,
		);

		if ( null !== $html_body ) {
			$body['html'] = $html_body;
		}

		foreach ( $headers as $header ) {
			if ( stripos( $header, 'Reply-To:' ) === 0 ) {
				$body['h:Reply-To'] = trim( substr( $header, 9 ) );
			} elseif ( stripos( $header, 'X-Order-Mail-Doctor-ID:' ) === 0 ) {
				$body['h:X-Order-Mail-Doctor-ID'] = trim( substr( $header, 24 ) );
			} elseif ( stripos( $header, 'Message-ID:' ) === 0 ) {
				$body['h:Message-Id'] = trim( substr( $header, 11 ) );
			} elseif ( stripos( $header, 'In-Reply-To:' ) === 0 ) {
				$body['h:In-Reply-To'] = trim( substr( $header, 12 ) );
			} elseif ( stripos( $header, 'References:' ) === 0 ) {
				$body['h:References'] = trim( substr( $header, 11 ) );
			}
		}

		return $body;
	}

	/**
	 * Convert attachment paths to CURLFile payloads.
	 *
	 * @param array<int|string,mixed> $attachments Attachments.
	 * @return array<int,\CURLFile|string>
	 */
	protected static function prepare_attachments( array $attachments ): array {
		$prepared = array();

		foreach ( $attachments as $attachment ) {
			$path = is_array( $attachment ) && isset( $attachment['file'] ) ? $attachment['file'] : $attachment;
			$path = wp_normalize_path( (string) $path );

			if ( ! file_exists( $path ) ) {
				continue;
			}

			$filename = basename( $path );
			$mime     = function_exists( 'mime_content_type' ) ? mime_content_type( $path ) : null;

			if ( function_exists( 'curl_file_create' ) ) {
				// phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_file_create
				$prepared[] = curl_file_create( $path, $mime, $filename );
			} else {
				$prepared[] = '@' . $path;
			}
		}

		return $prepared;
	}

	/**
	 * Determine whether the message is HTML or plain text.
	 *
	 * @param array<int,string> $headers Headers.
	 * @return string
	 */
	protected static function detect_body_type( array $headers ): string {
		foreach ( $headers as $header ) {
			if ( stripos( $header, 'Content-Type:' ) === 0 && stripos( $header, 'text/html' ) !== false ) {
				return 'html';
			}
		}

		return 'text';
	}

	/**
	 * Normalize recipients into a list of email addresses.
	 *
	 * @param mixed $recipients Recipient data.
	 * @return array<string>
	 */
	protected static function format_recipients( $recipients ): array {
		if ( empty( $recipients ) ) {
			return array();
		}

		if ( is_string( $recipients ) ) {
			$recipients = array_map( 'trim', explode( ',', $recipients ) );
		}

		if ( ! is_array( $recipients ) ) {
			return array();
		}

		$normalized = array();

		foreach ( $recipients as $recipient ) {
			if ( is_array( $recipient ) && isset( $recipient['email'] ) ) {
				$recipient = $recipient['email'];
			}

			$email = sanitize_email( (string) $recipient );
			if ( $email ) {
				$normalized[] = $email;
			}
		}

		return array_values( array_unique( $normalized ) );
	}
}
