<?php
/**
 * SendGrid Web API v3 mail provider.
 *
 * @package EmailSmtp
 * @link https://www.twilio.com/docs/sendgrid/api-reference/mail-send/mail-send
 */

namespace EmailSmtp\Providers\Sendgrid;

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 {

	/** Combined To + Cc + Bcc across personalizations (Mail Send limit). */
	private const RECIPIENT_CAP = 1000;

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

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

	public function get_description(): string {
		return __( 'Send via the Twilio SendGrid Web API v3 with an API key.', 'wp-powersuite' );
	}

	public function get_recommended_use(): string {
		return __( 'High-volume transactional email with strong analytics.', 'wp-powersuite' );
	}

	public function get_docs_url(): string {
		return 'https://www.twilio.com/docs/sendgrid/api-reference/mail-send/mail-send';
	}

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

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


	/**
	 * @param array<string, mixed> $settings
	 * @return array<string, string>
	 */
	private function auth_headers( array $settings ): array {
		$key = $this->secret( $settings, 'sendgrid_api_key', 'WP_POWERSUITE_SENDGRID_API_KEY' );
		return [
			'Authorization' => 'Bearer ' . $key,
			'Accept'        => 'application/json',
			'Content-Type'  => 'application/json',
		];
	}

	public function connect( array $settings ): DeliveryResult {
		$key = $this->secret( $settings, 'sendgrid_api_key', 'WP_POWERSUITE_SENDGRID_API_KEY' );
		if ( $key === '' ) {
			return DeliveryResult::failure( __( 'SendGrid API key is required.', 'wp-powersuite' ), '', 'authentication' );
		}

		$host     = $this->api_host( $settings );
		$response = ApiHttpClient::request(
			'GET',
			'https://' . $host . '/v3/scopes',
			$this->auth_headers( $settings )
		);

		if ( ! empty( $response['error'] ) ) {
			return ErrorNormalizer::from_text( (string) $response['error'] );
		}

		$code = (int) ( $response['code'] ?? 0 );
		if ( $code === 403 ) {
			return DeliveryResult::success(
				__( 'SendGrid API key accepted. Ensure the key has Mail Send permission and the From address is a verified sender.', 'wp-powersuite' )
			);
		}

		$result = ApiHttpClient::to_delivery_result(
			$response,
			static function () {
				return [ 'message' => __( 'SendGrid API key authenticated successfully.', 'wp-powersuite' ) ];
			}
		);

		if ( $result->is_success() && is_array( $response['json'] ?? null ) ) {
			$scopes = $response['json']['scopes'] ?? [];
			if ( is_array( $scopes ) && $scopes !== [] && ! in_array( 'mail.send', $scopes, true ) ) {
				return DeliveryResult::success(
					__( 'SendGrid API key authenticated, but mail.send scope was not found. Grant Mail Send permission before sending.', 'wp-powersuite' )
				);
			}
		}

		return $result;
	}

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

		$key = $this->secret( $settings, 'sendgrid_api_key', 'WP_POWERSUITE_SENDGRID_API_KEY' );
		if ( $key === '' ) {
			return DeliveryResult::failure( __( 'SendGrid API key is required.', 'wp-powersuite' ), '', 'authentication' );
		}

		$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' );
		}

		$personalization = [
			'to' => array_map( static fn( string $email ) => [ 'email' => $email ], $to ),
		];
		$cc = $this->take_recipients( $message->get_cc(), $remaining );
		if ( $cc !== [] ) {
			$personalization['cc'] = array_map( static fn( string $e ) => [ 'email' => $e ], $cc );
		}
		$bcc = $this->take_recipients( $message->get_bcc(), $remaining );
		if ( $bcc !== [] ) {
			$personalization['bcc'] = array_map( static fn( string $e ) => [ 'email' => $e ], $bcc );
		}

		$content = [];
		if ( $message->is_html() ) {
			$content[] = [ 'type' => 'text/plain', 'value' => $this->plain_text_body( $message ) ];
			$html      = $message->get_body();
			$content[] = [ 'type' => 'text/html', 'value' => $html !== '' ? $html : ' ' ];
		} else {
			$content[] = [ 'type' => 'text/plain', 'value' => $this->plain_text_body( $message ) ];
		}

		$from    = $this->from_parts( $message );
		$payload = [
			'personalizations' => [ $personalization ],
			'from'             => array_filter(
				[
					'email' => $from['email'],
					'name'  => $from['name'] !== '' ? $from['name'] : null,
				]
			),
			'subject'          => $message->get_subject() !== '' ? $message->get_subject() : '(no subject)',
			'content'          => $content,
		];

		if ( is_email( $message->get_reply_to_email() ) ) {
			$payload['reply_to'] = array_filter(
				[
					'email' => $message->get_reply_to_email(),
					'name'  => $message->get_reply_to_name() !== '' ? $message->get_reply_to_name() : null,
				]
			);
		}

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

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

		$host     = $this->api_host( $settings );
		$response = ApiHttpClient::request(
			'POST',
			'https://' . $host . '/v3/mail/send',
			$this->auth_headers( $settings ),
			$payload
		);

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

	/**
	 * @param array<string, mixed> $settings
	 */
	private function api_host( array $settings ): string {
		$region = sanitize_key( (string) ( $settings['sendgrid_region'] ?? 'global' ) );
		return $region === 'eu' ? 'api.eu.sendgrid.com' : 'api.sendgrid.com';
	}
}
