<?php
/**
 * SMTP.com email API provider (API v4).
 *
 * Auth and payload shape follow SMTP.com API v4. Live sends require a paid SMTP.com plan.
 *
 * @package EmailSmtp
 * @link https://www.smtp.com/resources/api-documentation/
 * @link https://knowledge.smtp.com/s/article/API-v4-Authentication
 */

namespace EmailSmtp\Providers\Smtpcom;

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 {

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

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

	public function get_description(): string {
		return __( 'Send email through the SMTP.com Channel API (v4).', 'wp-powersuite' );
	}

	public function get_docs_url(): string {
		return 'https://www.smtp.com/resources/api-documentation/';
	}

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

	public function get_required_fields(): array {
		return [ 'smtpcom_api_key', 'smtpcom_channel' ];
	}


	public function connect( array $settings ): DeliveryResult {
		$key     = $this->secret( $settings, 'smtpcom_api_key', 'WP_POWERSUITE_SMTPCOM_API_KEY' );
		$channel = sanitize_text_field( (string) ( $settings['smtpcom_channel'] ?? '' ) );
		if ( $key === '' || $channel === '' ) {
			return DeliveryResult::failure( __( 'SMTP.com API key and channel name are required.', 'wp-powersuite' ), '', 'authentication' );
		}

		$response = ApiHttpClient::request(
			'GET',
			'https://api.smtp.com/v4/channels/' . rawurlencode( $channel ),
			$this->auth_headers( $key )
		);

		return $this->result_from_response(
			$response,
			static function () {
				return [ 'message' => __( 'SMTP.com credentials authenticated successfully.', 'wp-powersuite' ) ];
			}
		);
	}

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

		$key     = $this->secret( $settings, 'smtpcom_api_key', 'WP_POWERSUITE_SMTPCOM_API_KEY' );
		$channel = sanitize_text_field( (string) ( $settings['smtpcom_channel'] ?? '' ) );
		if ( $key === '' || $channel === '' ) {
			return DeliveryResult::failure( __( 'SMTP.com API key and channel name are required.', 'wp-powersuite' ), '', 'authentication' );
		}

		$from     = $this->from_parts( $message );
		$charset  = function_exists( 'get_bloginfo' ) ? (string) get_bloginfo( 'charset' ) : 'UTF-8';
		$charset  = $charset !== '' ? $charset : 'UTF-8';
		$parts    = [];

		if ( $message->is_html() ) {
			$parts[] = [
				'type'    => 'text/html',
				'content' => $message->get_body(),
				'charset' => $charset,
			];
			$parts[] = [
				'type'    => 'text/plain',
				'content' => $message->get_alt_body() !== ''
					? $message->get_alt_body()
					: wp_strip_all_tags( $message->get_body() ),
				'charset' => $charset,
			];
		} else {
			$parts[] = [
				'type'    => 'text/plain',
				'content' => $message->get_body(),
				'charset' => $charset,
			];
		}

		$payload = [
			'channel'    => $channel,
			'subject'    => $message->get_subject(),
			'recipients' => [
				'to' => array_map( [ $this, 'address_object' ], $message->get_to() ),
			],
			'originator' => [
				'from' => $this->address_object( $from['email'], $from['name'] ),
			],
			'body'       => [
				'parts' => $parts,
			],
		];

		if ( $message->get_cc() !== [] ) {
			$payload['recipients']['cc'] = array_map( [ $this, 'address_object' ], $message->get_cc() );
		}
		if ( $message->get_bcc() !== [] ) {
			$payload['recipients']['bcc'] = array_map( [ $this, 'address_object' ], $message->get_bcc() );
		}
		if ( is_email( $message->get_reply_to_email() ) ) {
			$payload['originator']['reply_to'] = $this->address_object(
				$message->get_reply_to_email(),
				$message->get_reply_to_name()
			);
		}

		$attachments = $this->attachments_as_base64( $message );
		if ( $attachments !== [] ) {
			$payload['body']['attachments'] = array_map(
				static function ( array $a ) {
					return [
						'filename'    => $a['name'],
						'content'     => $a['content'],
						'type'        => $a['type'] !== '' ? $a['type'] : 'application/octet-stream',
						'encoding'    => 'base64',
						'disposition' => 'attachment',
					];
				},
				$attachments
			);
		}

		$headers = $this->safe_custom_headers( $message );
		if ( $headers !== [] ) {
			$payload['custom_headers'] = $headers;
		}

		$response = ApiHttpClient::request(
			'POST',
			'https://api.smtp.com/v4/messages',
			$this->auth_headers( $key ),
			$payload
		);

		return $this->result_from_response(
			$response,
			static function ( array $res ) {
				$id = '';
				if ( is_array( $res['json'] ) ) {
					$data = $res['json']['data'] ?? null;
					if ( is_array( $data ) ) {
						$id = (string) ( $data['message_id'] ?? $data['id'] ?? '' );
						if ( $id === '' && ! empty( $data['message'] ) && is_string( $data['message'] ) ) {
							// SMTP.com success text looks like: "msg_id: <id>"
							if ( preg_match( '/msg_id:\s*(\S+)/i', $data['message'], $m ) ) {
								$id = $m[1];
							}
						}
					}
				}
				return [
					'message' => __( 'SMTP.com accepted the message.', 'wp-powersuite' ),
					'id'      => $id,
				];
			}
		);
	}

	/**
	 * Preferred auth from SMTP.com docs: Bearer {apikey}.
	 *
	 * @return array<string, string>
	 */
	private function auth_headers( string $api_key ): array {
		return [ 'Authorization' => 'Bearer ' . $api_key ];
	}

	/**
	 * @return array{address:string, name?:string}
	 */
	private function address_object( string $email, string $name = '' ): array {
		$row = [ 'address' => sanitize_email( $email ) ];
		$name = sanitize_text_field( $name );
		if ( $name !== '' ) {
			$row['name'] = $name;
		}
		return $row;
	}

	/**
	 * Map HTTP + JSend responses to DeliveryResult.
	 * SMTP.com returns JSend (`status`: success|fail|error).
	 *
	 * @param array{code:int, body:string, headers:array<string,string>, duration_ms:int, json:array<string,mixed>|null, error?:string} $response
	 * @param callable                                                                                                                     $success_extractor
	 */
	private function result_from_response( array $response, callable $success_extractor ): DeliveryResult {
		$json   = $response['json'] ?? null;
		$status = is_array( $json ) ? (string) ( $json['status'] ?? '' ) : '';

		if ( in_array( $status, [ 'fail', 'error' ], true ) ) {
			$technical = ApiHttpClient::extract_error_text( $response );
			$result    = ErrorNormalizer::from_text( $technical, (string) ( $response['code'] ?? '' ) );
			return DeliveryResult::failure(
				$result->get_user_message(),
				$result->get_technical_message(),
				$result->get_category(),
				(string) ( $response['code'] ?? '' ),
				$result->is_retry_safe(),
				$result->get_suggested_action(),
				(int) ( $response['duration_ms'] ?? 0 )
			);
		}

		return ApiHttpClient::to_delivery_result( $response, $success_extractor );
	}
}
