<?php
/**
 * Mailer transport helpers.
 *
 * @package MailDoctor
 */

namespace MailDoctor;

use MailDoctor\Transports\Generic_Http;
use MailDoctor\Transports\Mailgun;
use MailDoctor\Transports\Postmark;
use MailDoctor\Transports\SendGrid;
use MailDoctor\Transports\Ses;
use MailDoctor\Transports\SparkPost;
use WP_Error;

trait Mailer_Transport_Trait {

	/**
	 * Provide list of API-based transport identifiers.
	 *
	 * @return array<int, string>
	 */
	protected static function api_transports(): array {
		return array( 'mailgun_api', 'sendgrid_api', 'postmark_api', 'sparkpost_api', 'ses_api', 'generic_http' );
	}

	/**
	 * Check if a transport uses HTTP API delivery.
	 *
	 * @param string $transport Transport identifier.
	 * @return bool
	 */
	protected static function is_api_transport( string $transport ): bool {
		return in_array( $transport, self::api_transports(), true );
	}

	/**
	 * Deliver email via configured HTTP API transport.
	 *
	 * @param string               $transport Transport identifier.
	 * @param array<string, mixed> $atts      Raw wp_mail arguments.
	 * @return array{
	 *     ok: bool,
	 *     deferred?: bool,
	 *     error?: WP_Error,
	 *     log_id?: int,
	 *     provider_code?: string
	 * }
	 */
	protected static function deliver_via_transport( string $transport, array $atts ): array {
		$args = self::normalize_mail_args( $atts );
		self::trace_event(
			'transport.dispatch',
			array(
				'transport' => $transport,
				'to'        => implode( ',', self::parse_recipient_array( $args['to'] ?? array() ) ),
				'subject'   => mb_substr( (string) ( $args['subject'] ?? '' ), 0, 120 ),
			)
		);

		if ( class_exists( __NAMESPACE__ . '\RateLimiter' ) ) {
			$slot = RateLimiter::claim_slot();
			if ( ! $slot['allow'] ) {
				self::queue_deferral( $args, max( 1, (int) $slot['wait'] ), $slot['reason'] ?? 'rate_limit' );
				return array(
					'ok'       => true,
					'deferred' => true,
				);
			}
		}

		self::prepare_profile();
		$context = self::build_logging_context();

		$log_id = self::$active_log_id ? (int) self::$active_log_id : 0;

		if ( $log_id > 0 ) {
			self::debug_log(
				'deliver_via_transport using existing log',
				array(
					'log_id'    => $log_id,
					'transport' => $transport,
				)
			);
		} else {
			$log_id = Logger::log_attempt( $args, $context );
			if ( $log_id ) {
				self::$active_log_id = $log_id;
			}
		}

		if ( ! $log_id ) {
			$error = new WP_Error( 'mail_doctor_log_failure', __( 'Unable to persist transport attempt.', 'mail-doctor' ) );
			self::trace_event(
				'transport.log_failure',
				array(
					'transport' => $transport,
					'error'     => $error->get_error_message(),
				)
			);
			return array(
				'ok'    => false,
				'error' => $error,
			);
		}

		if ( ! empty( self::$attachment_meta ) ) {
			Logger::annotate_attachments( $log_id, self::$attachment_meta, self::$attachment_total );
		}

		$headers         = self::inject_metadata_headers( $args['headers'] ?? array(), $log_id );
		$headers         = self::inject_thread_headers( $headers, $context );
		$args['headers'] = $headers;

		$payload = Logger::get_payload( $log_id );
		if ( ! empty( $payload ) ) {
			$payload['mail_args']['headers'] = $headers;
			Logger::update_payload( $log_id, $payload );
		}

		$result = self::dispatch_transport_send( $transport, $args, Settings::all() );

		if ( is_wp_error( $result ) ) {
			Logger::mark_failed( $log_id, $result );
			$error_meta = array(
				'code'    => $result->get_error_code(),
				'message' => $result->get_error_message(),
				'log_id'  => $log_id,
			);

			$data = $result->get_error_data();
			if ( is_array( $data ) ) {
				if ( isset( $data['status'] ) ) {
					$error_meta['status'] = (int) $data['status'];
				}
				if ( isset( $data['response'] ) ) {
					$body                          = (string) $data['response'];
					$error_meta['response_digest'] = substr( hash( 'sha256', $body ), 0, 12 );
					$error_meta['response_bytes']  = strlen( $body );
				}
			}

			$excerpt = '';
			if ( isset( $data['response'] ) ) {
				$excerpt = wp_trim_words( wp_strip_all_tags( (string) $data['response'] ), 60, '…' );
			}

			Logger::annotate_transport_debug(
				$log_id,
				array(
					'status'           => $error_meta['status'] ?? null,
					'response_digest'  => $error_meta['response_digest'] ?? '',
					'response_bytes'   => $error_meta['response_bytes'] ?? null,
					'response_excerpt' => $excerpt,
					'error_code'       => $result->get_error_code(),
				)
			);

			self::trace_event( 'transport.api_error', $error_meta );

			return array(
				'ok'     => false,
				'error'  => $result,
				'log_id' => $log_id,
			);
		}

		$meta = array();
		if ( ! empty( $result['provider_code'] ) ) {
			$meta['provider_code'] = sanitize_text_field( (string) $result['provider_code'] );
		}

		Logger::mark_sent( $log_id, $meta );

		$response_body = isset( $result['raw'] ) ? (string) $result['raw'] : '';
		self::trace_event(
			'transport.api_success',
			array(
				'log_id'          => $log_id,
				'provider_code'   => $meta['provider_code'] ?? '',
				'response_digest' => $response_body ? substr( hash( 'sha256', $response_body ), 0, 12 ) : '',
				'response_bytes'  => $response_body ? strlen( $response_body ) : 0,
			)
		);

		Logger::annotate_transport_debug(
			$log_id,
			array(
				'status'           => isset( $result['status'] ) ? (int) $result['status'] : null,
				'provider_message' => $meta['provider_code'] ?? '',
				'response_digest'  => $response_body ? substr( hash( 'sha256', $response_body ), 0, 12 ) : '',
				'response_bytes'   => $response_body ? strlen( $response_body ) : 0,
			)
		);

		return array(
			'ok'            => true,
			'log_id'        => $log_id,
			'provider_code' => $meta['provider_code'] ?? '',
		);
	}

	/**
	 * Route payload to provider transport implementation.
	 *
	 * @param string              $transport Transport identifier.
	 * @param array<string,mixed> $args      Normalized mail arguments.
	 * @param array<string,mixed> $settings  Plugin settings.
	 * @return array<string,mixed>|WP_Error
	 */
	protected static function dispatch_transport_send( string $transport, array $args, array $settings ) {
		switch ( $transport ) {
			case 'mailgun_api':
				return Mailgun::send( $args, $settings );
			case 'sendgrid_api':
				return SendGrid::send( $args, $settings );
			case 'postmark_api':
				return Postmark::send( $args, $settings );
			case 'sparkpost_api':
				return SparkPost::send( $args, $settings );
			case 'ses_api':
				return Ses::send( $args, $settings );
			case 'generic_http':
				return Generic_Http::send( $args, $settings );
			default:
				return new WP_Error( 'mail_doctor_unknown_transport', __( 'Unsupported delivery transport.', 'mail-doctor' ) );
		}
	}

	/**
	 * Deliver email using Mailgun REST API.
	 *
	 * @param array<string,mixed> $atts Raw mail arguments.
	 * @return array{
	 *     ok: bool,
	 *     deferred?: bool,
	 *     error?: WP_Error,
	 *     log_id?: int,
	 *     provider_code?: string
	 * }
	 */
	protected static function deliver_via_mailgun_api( array $atts ): array {
		return self::deliver_via_transport( 'mailgun_api', $atts );
	}

	/**
	 * Summarise async state for status screen.
	 *
	 * @return array<string, string>
	 */
	public static function async_status_summary(): array {
		$mode    = Settings::async_mode();
		$choices = Settings::async_mode_choices();
		$label   = $choices[ $mode ] ?? ucfirst( $mode );
		$detail  = '';

		switch ( $mode ) {
			case 'action_scheduler':
				$detail = Settings::action_scheduler_available()
					? __( 'Messages are dispatched by the Action Scheduler queue.', 'mail-doctor' )
					: __( 'Action Scheduler unavailable, falling back to shutdown processing.', 'mail-doctor' );
				break;
			case 'shutdown':
				$detail = __( 'Messages send after the response is generated (PHP shutdown).', 'mail-doctor' );
				break;
			default:
				$detail = __( 'Messages send immediately within the request lifecycle.', 'mail-doctor' );
		}

		return array(
			'mode'   => $mode,
			'label'  => $label,
			'detail' => $detail,
		);
	}
}
