<?php
/**
 * Amazon SES API v2 provider (SigV4, no AWS SDK).
 *
 * @package EmailSmtp
 * @link https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendEmail.html
 */

namespace EmailSmtp\Providers\AmazonSes;

use EmailSmtp\Includes\AbstractApiProvider;
use EmailSmtp\Includes\ApiHttpClient;
use EmailSmtp\Includes\AwsSigV4;
use EmailSmtp\Includes\DeliveryResult;
use EmailSmtp\Includes\EmailMessage;
use EmailSmtp\Includes\ErrorNormalizer;

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

final class Provider extends AbstractApiProvider {

	/** SES SendEmail destination addresses (To+Cc+Bcc) per request. */
	private const RECIPIENT_CAP = 50;

	public function get_id(): string {
		return 'amazon-ses';
	}

	public function get_name(): string {
		return __( 'Amazon SES', 'wp-powersuite' );
	}

	public function get_description(): string {
		return __( 'Send transactional email through Amazon SES with IAM access keys.', 'wp-powersuite' );
	}

	public function get_recommended_use(): string {
		return __( 'High-volume transactional mail on AWS. Verify your domain, then request production access if you are still in the sandbox.', 'wp-powersuite' );
	}

	public function get_docs_url(): string {
		return 'https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendEmail.html';
	}

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

	public function get_required_fields(): array {
		return [ 'aws_region' ];
	}

	/**
	 * SES regions commonly used for sending.
	 *
	 * @return array<string, string> region_code => label
	 */
	public static function regions(): array {
		return [
			'us-east-1'      => 'US East (N. Virginia)',
			'us-east-2'      => 'US East (Ohio)',
			'us-west-1'      => 'US West (N. California)',
			'us-west-2'      => 'US West (Oregon)',
			'af-south-1'     => 'Africa (Cape Town)',
			'ap-east-1'      => 'Asia Pacific (Hong Kong)',
			'ap-south-1'     => 'Asia Pacific (Mumbai)',
			'ap-south-2'     => 'Asia Pacific (Hyderabad)',
			'ap-northeast-1' => 'Asia Pacific (Tokyo)',
			'ap-northeast-2' => 'Asia Pacific (Seoul)',
			'ap-northeast-3' => 'Asia Pacific (Osaka)',
			'ap-southeast-1' => 'Asia Pacific (Singapore)',
			'ap-southeast-2' => 'Asia Pacific (Sydney)',
			'ap-southeast-3' => 'Asia Pacific (Jakarta)',
			'ap-southeast-4' => 'Asia Pacific (Melbourne)',
			'ca-central-1'   => 'Canada (Central)',
			'ca-west-1'      => 'Canada West (Calgary)',
			'eu-central-1'   => 'Europe (Frankfurt)',
			'eu-central-2'   => 'Europe (Zurich)',
			'eu-west-1'      => 'Europe (Ireland)',
			'eu-west-2'      => 'Europe (London)',
			'eu-west-3'      => 'Europe (Paris)',
			'eu-south-1'     => 'Europe (Milan)',
			'eu-south-2'     => 'Europe (Spain)',
			'eu-north-1'     => 'Europe (Stockholm)',
			'il-central-1'   => 'Israel (Tel Aviv)',
			'me-central-1'   => 'Middle East (UAE)',
			'me-south-1'     => 'Middle East (Bahrain)',
			'sa-east-1'      => 'South America (São Paulo)',
		];
	}


	/**
	 * @param array<string, mixed> $settings
	 * @return true|\WP_Error
	 */
	public function validate_settings( array $settings ) {
		$region = sanitize_key( (string) ( $settings['aws_region'] ?? '' ) );
		if ( $region === '' || ! preg_match( '/^[a-z0-9-]+$/', $region ) ) {
			return new \WP_Error( 'missing_region', __( 'AWS region is required.', 'wp-powersuite' ) );
		}
		$creds = $this->resolve_credentials( $settings );
		if ( $creds['access_key'] === '' || $creds['secret_key'] === '' ) {
			return new \WP_Error(
				'missing_credentials',
				__( 'Enter your AWS access key and secret, or switch Authentication to environment variables.', 'wp-powersuite' )
			);
		}
		return true;
	}

	public function connect( array $settings ): DeliveryResult {
		$valid = $this->validate_settings( $settings );
		if ( is_wp_error( $valid ) ) {
			return ErrorNormalizer::from_mixed( $valid );
		}

		$region = sanitize_key( (string) $settings['aws_region'] );
		$creds  = $this->resolve_credentials( $settings );
		$host   = 'email.' . $region . '.amazonaws.com';
		$uri    = '/v2/email/account';
		$signed = AwsSigV4::sign( 'GET', $host, $uri, '', $region, 'ses', $creds['access_key'], $creds['secret_key'] );

		$response = ApiHttpClient::request( 'GET', $signed['url'], $signed['headers'], null );

		return ApiHttpClient::to_delivery_result(
			$response,
			static function ( array $res ) {
				$json    = is_array( $res['json'] ) ? $res['json'] : [];
				$details = [];
				if ( isset( $json['ProductionAccessEnabled'] ) ) {
					$details[] = ! empty( $json['ProductionAccessEnabled'] )
						? __( 'Production access enabled.', 'wp-powersuite' )
						: __( 'SES sandbox: you can only send to verified identities until production access is approved.', 'wp-powersuite' );
				}
				return [
					'message' => $details !== []
						? implode( ' ', $details )
						: __( 'Amazon SES credentials authenticated successfully.', 'wp-powersuite' ),
				];
			}
		);
	}

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

		$valid = $this->validate_settings( $settings );
		if ( is_wp_error( $valid ) ) {
			return ErrorNormalizer::from_mixed( $valid );
		}

		$region    = sanitize_key( (string) $settings['aws_region'] );
		$creds     = $this->resolve_credentials( $settings );
		$from      = $this->from_parts( $message );
		$charset   = $message->get_charset() ?: 'UTF-8';
		$remaining = self::RECIPIENT_CAP;
		$to        = $this->take_recipients( $message->get_to(), $remaining );
		if ( $to === [] ) {
			return DeliveryResult::failure( __( 'At least one recipient is required.', 'wp-powersuite' ), 'missing_to', 'recipient' );
		}
		$cc  = $this->take_recipients( $message->get_cc(), $remaining );
		$bcc = $this->take_recipients( $message->get_bcc(), $remaining );

		$simple = [
			'Subject' => [
				'Data'    => $message->get_subject() !== '' ? $message->get_subject() : '(no subject)',
				'Charset' => $charset,
			],
			'Body'    => [],
		];

		if ( $message->is_html() ) {
			$html = $message->get_body() !== '' ? $message->get_body() : ' ';
			$simple['Body']['Html'] = [
				'Data'    => $html,
				'Charset' => $charset,
			];
			$simple['Body']['Text'] = [
				'Data'    => $this->plain_text_body( $message ),
				'Charset' => $charset,
			];
		} else {
			$simple['Body']['Text'] = [
				'Data'    => $this->plain_text_body( $message ),
				'Charset' => $charset,
			];
		}

		$attachments = $this->attachments_as_base64( $message );
		if ( $attachments !== [] ) {
			$simple['Attachments'] = array_map(
				static function ( array $a ) {
					return [
						'RawContent'              => $a['content'],
						'FileName'                => $a['name'],
						'ContentType'             => $a['type'] !== '' ? $a['type'] : 'application/octet-stream',
						'ContentDisposition'      => 'ATTACHMENT',
						'ContentTransferEncoding' => 'BASE64',
					];
				},
				$attachments
			);
		}

		$custom = $this->safe_custom_headers( $message );
		if ( $custom !== [] ) {
			$simple['Headers'] = [];
			foreach ( $custom as $name => $value ) {
				$simple['Headers'][] = [
					'Name'  => (string) $name,
					'Value' => (string) $value,
				];
			}
		}

		$payload = [
			'FromEmailAddress' => $this->format_address( $from['email'], $from['name'] ),
			'Destination'      => array_filter(
				[
					'ToAddresses'  => $to,
					'CcAddresses'  => $cc !== [] ? $cc : null,
					'BccAddresses' => $bcc !== [] ? $bcc : null,
				]
			),
			'Content'          => [
				'Simple' => $simple,
			],
		];

		if ( is_email( $message->get_reply_to_email() ) ) {
			$payload['ReplyToAddresses'] = [
				$this->format_address( $message->get_reply_to_email(), $message->get_reply_to_name() ),
			];
		}

		$config_set = sanitize_text_field( (string) ( $settings['aws_configuration_set'] ?? '' ) );
		if ( $config_set !== '' ) {
			$payload['ConfigurationSetName'] = $config_set;
		}

		$host   = 'email.' . $region . '.amazonaws.com';
		$uri    = '/v2/email/outbound-emails';
		$body   = wp_json_encode( $payload );
		$signed = AwsSigV4::sign( 'POST', $host, $uri, (string) $body, $region, 'ses', $creds['access_key'], $creds['secret_key'] );

		$response = ApiHttpClient::request( 'POST', $signed['url'], $signed['headers'], $signed['body'] );

		return ApiHttpClient::to_delivery_result(
			$response,
			static function ( array $res ) {
				$id = is_array( $res['json'] ) ? (string) ( $res['json']['MessageId'] ?? '' ) : '';
				return [
					'message' => __( 'Amazon SES accepted the message.', 'wp-powersuite' ),
					'id'      => $id,
				];
			}
		);
	}

	/**
	 * @param array<string, mixed> $settings
	 * @return array{access_key:string, secret_key:string}
	 */
	private function resolve_credentials( array $settings ): array {
		if ( defined( 'WP_POWERSUITE_AWS_ACCESS_KEY_ID' ) && defined( 'WP_POWERSUITE_AWS_SECRET_ACCESS_KEY' ) ) {
			return [
				'access_key' => (string) constant( 'WP_POWERSUITE_AWS_ACCESS_KEY_ID' ),
				'secret_key' => (string) constant( 'WP_POWERSUITE_AWS_SECRET_ACCESS_KEY' ),
			];
		}

		$env_key    = getenv( 'AWS_ACCESS_KEY_ID' );
		$env_secret = getenv( 'AWS_SECRET_ACCESS_KEY' );
		$prefer_env = sanitize_key( (string) ( $settings['aws_credential_source'] ?? 'keys' ) ) === 'env';
		if ( $prefer_env || ( (string) ( $settings['aws_access_key_id'] ?? '' ) === '' && is_string( $env_key ) && $env_key !== '' ) ) {
			return [
				'access_key' => is_string( $env_key ) ? $env_key : '',
				'secret_key' => is_string( $env_secret ) ? $env_secret : '',
			];
		}

		return [
			'access_key' => $this->secret( $settings, 'aws_access_key_id', 'WP_POWERSUITE_AWS_ACCESS_KEY_ID' ),
			'secret_key' => $this->secret( $settings, 'aws_secret_access_key', 'WP_POWERSUITE_AWS_SECRET_ACCESS_KEY' ),
		];
	}
}
