<?php
/**
 * Custom SMTP provider (PHPMailer).
 *
 * @package EmailSmtp
 * @since 1.1.0
 */

namespace EmailSmtp\Providers\Smtp;

use EmailSmtp\Includes\AbstractProvider;
use EmailSmtp\Includes\CredentialManager;
use EmailSmtp\Includes\DeliveryResult;
use EmailSmtp\Includes\EmailMessage;
use EmailSmtp\Includes\ErrorNormalizer;
use EmailSmtp\Module;

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

final class Provider extends AbstractProvider {

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

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

	public function get_description(): string {
		return __( 'Send through any SMTP server, including hosting mail and provider SMTP relays (SendGrid, Mailgun, and others).', 'wp-powersuite' );
	}

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

	public function get_recommended_use(): string {
		return __( 'Use when you have SMTP credentials. Prefer the named API mailer when the provider is listed.', 'wp-powersuite' );
	}

	public function get_docs_url(): string {
		return 'https://www.php.net/manual/en/book.mail.php';
	}

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

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

	/**
	 * @param array<string, mixed> $settings
	 * @return true|\WP_Error
	 */
	public function validate_settings( array $settings ) {
		$host = trim( (string) ( $settings['smtp_host'] ?? '' ) );
		if ( $host === '' ) {
			return new \WP_Error( 'missing_host', __( 'SMTP host is required.', 'wp-powersuite' ) );
		}

		$auth = ! empty( $settings['smtp_auth'] ) && (string) $settings['smtp_auth'] !== '0';
		if ( $auth ) {
			$user = trim( (string) ( $settings['smtp_username'] ?? '' ) );
			$pass = (string) ( $settings['smtp_password'] ?? '' );
			if ( $user === '' ) {
				return new \WP_Error( 'missing_username', __( 'SMTP username is required when authentication is enabled.', 'wp-powersuite' ) );
			}
			if ( $pass === '' && ! CredentialManager::is_from_constant( 'WP_POWERSUITE_SMTP_PASSWORD' ) ) {
				return new \WP_Error( 'missing_password', __( 'SMTP password is required when authentication is enabled.', 'wp-powersuite' ) );
			}
		}

		return true;
	}

	public function get_connection_status( array $settings ): string {
		$valid = $this->validate_settings( $settings );
		if ( ! is_wp_error( $valid ) ) {
			return 'connected';
		}
		$host = trim( (string) ( $settings['smtp_host'] ?? '' ) );
		return $host === '' ? 'not_configured' : 'incomplete';
	}

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

		if ( ! class_exists( '\PHPMailer\PHPMailer\PHPMailer' ) ) {
			require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
			require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
			require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
		}

		$started = (int) round( microtime( true ) * 1000 );
		$mailer  = new \PHPMailer\PHPMailer\PHPMailer( true );

		try {
			Module::configurePhpMailerFromSettings( $mailer, $settings );
			$mailer->Timeout = max( 5, min( 120, (int) ( $settings['smtp_timeout'] ?? 30 ) ) );
			// smtpConnect validates auth without sending a message.
			$connected = $mailer->smtpConnect();
			$mailer->smtpClose();
			$duration = (int) round( microtime( true ) * 1000 ) - $started;

			if ( ! $connected ) {
				return DeliveryResult::failure(
					__( 'Could not connect to the SMTP server.', 'wp-powersuite' ),
					ErrorNormalizer::redact( $mailer->ErrorInfo ?: 'smtpConnect failed' ),
					'network',
					'',
					true,
					__( 'Verify host, port, encryption, and outbound firewall rules.', 'wp-powersuite' ),
					$duration
				);
			}

			return DeliveryResult::success(
				__( 'SMTP connection successful.', 'wp-powersuite' ),
				'',
				$duration
			);
		} catch ( \Throwable $e ) {
			$duration = (int) round( microtime( true ) * 1000 ) - $started;
			$result   = ErrorNormalizer::from_mixed( $e );
			return DeliveryResult::failure(
				$result->get_user_message(),
				$result->get_technical_message(),
				$result->get_category(),
				$result->get_provider_code(),
				$result->is_retry_safe(),
				$result->get_suggested_action(),
				$duration
			);
		}
	}

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

		if ( ! class_exists( '\PHPMailer\PHPMailer\PHPMailer' ) ) {
			require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
			require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
			require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
		}

		$started = (int) round( microtime( true ) * 1000 );
		$mailer  = new \PHPMailer\PHPMailer\PHPMailer( true );

		try {
			Module::configurePhpMailerFromSettings( $mailer, $settings );
			// Guarantee SMTP transport for orchestrated sends — never mail/sendmail/Postfix.
			$mailer->isSMTP();
			$mailer->CharSet = $message->get_charset() !== '' ? $message->get_charset() : 'UTF-8';
			$mailer->Subject = $message->get_subject();
			$mailer->Body    = $message->get_body();
			$mailer->isHTML( $message->is_html() );
			if ( ! $message->is_html() ) {
				$mailer->AltBody = $message->get_body();
			} elseif ( $message->get_alt_body() !== '' ) {
				$mailer->AltBody = $message->get_alt_body();
			}

			$from_email = $message->get_from_email();
			$from_name  = $message->get_from_name();
			if ( ! is_email( $from_email ) ) {
				$from_email = sanitize_email( (string) ( $settings['from_email'] ?? '' ) );
			}
			if ( is_email( $from_email ) ) {
				// auto=false so we control SMTP MAIL FROM via Sender explicitly.
				$mailer->setFrom( $from_email, $from_name, false );
			}

			$envelope = '';
			$set_return_path = ! isset( $settings['set_return_path'] )
				|| ( ! empty( $settings['set_return_path'] ) && (string) $settings['set_return_path'] !== '0' );
			if ( $set_return_path ) {
				$envelope = sanitize_email( $message->get_return_path() );
				if ( ! is_email( $envelope ) ) {
					$envelope = sanitize_email( (string) ( $settings['return_path'] ?? '' ) );
				}
				if ( ! is_email( $envelope ) ) {
					$envelope = is_email( $from_email ) ? $from_email : '';
				}
				if ( is_email( $envelope ) ) {
					$mailer->Sender = $envelope;
				}
			}

			foreach ( $message->get_to() as $to ) {
				$mailer->addAddress( $to );
			}
			foreach ( $message->get_cc() as $cc ) {
				$mailer->addCC( $cc );
			}
			foreach ( $message->get_bcc() as $bcc ) {
				$mailer->addBCC( $bcc );
			}
			if ( is_email( $message->get_reply_to_email() ) ) {
				$mailer->addReplyTo( $message->get_reply_to_email(), $message->get_reply_to_name() );
			}
			foreach ( $message->get_attachments() as $path ) {
				if ( is_readable( $path ) ) {
					$mailer->addAttachment( $path );
				}
			}
			foreach ( $message->get_custom_headers() as $name => $value ) {
				$key = strtolower( (string) $name );
				if ( in_array( $key, [ 'authorization', 'api-key', 'x-api-key', 'cookie', 'set-cookie' ], true ) ) {
					continue;
				}
				$mailer->addCustomHeader( (string) $name, sanitize_text_field( (string) $value ) );
			}

			if ( (string) $mailer->Mailer !== 'smtp' ) {
				return DeliveryResult::failure(
					__( 'SMTP transport was not active; refusing to send via PHP mail.', 'wp-powersuite' ),
					'mailer=' . (string) $mailer->Mailer,
					'configuration',
					'refused_php_mail',
					false,
					__( 'Check the SMTP host and that no other plugin resets PHPMailer.', 'wp-powersuite' ),
					(int) round( microtime( true ) * 1000 ) - $started,
					[
						'transport'       => 'smtp',
						'envelope_sender' => $envelope,
						'connection_id'   => sanitize_key( (string) ( $settings['id'] ?? '' ) ),
					]
				);
			}

			$sent     = $mailer->send();
			$duration = (int) round( microtime( true ) * 1000 ) - $started;
			if ( ! $sent ) {
				return DeliveryResult::failure(
					__( 'SMTP server rejected the message.', 'wp-powersuite' ),
					ErrorNormalizer::redact( $mailer->ErrorInfo ?: 'send failed' ),
					'provider',
					'',
					true,
					__( 'Check SMTP credentials, From address, and recipient.', 'wp-powersuite' ),
					$duration,
					[
						'transport'       => 'smtp',
						'envelope_sender' => $envelope,
						'connection_id'   => sanitize_key( (string) ( $settings['id'] ?? '' ) ),
					]
				);
			}

			return DeliveryResult::success(
				__( 'SMTP accepted the message.', 'wp-powersuite' ),
				'',
				$duration,
				[
					'transport'       => 'smtp',
					'envelope_sender' => $envelope !== '' ? $envelope : ( is_email( $from_email ) ? $from_email : '' ),
					'connection_id'   => sanitize_key( (string) ( $settings['id'] ?? '' ) ),
					'phpmailer_mailer'=> 'smtp',
				]
			);
		} catch ( \Throwable $e ) {
			$duration = (int) round( microtime( true ) * 1000 ) - $started;
			$result   = ErrorNormalizer::from_mixed( $e );
			return DeliveryResult::failure(
				$result->get_user_message(),
				$result->get_technical_message(),
				$result->get_category(),
				$result->get_provider_code(),
				$result->is_retry_safe(),
				$result->get_suggested_action(),
				$duration,
				[
					'transport'     => 'smtp',
					'connection_id' => sanitize_key( (string) ( $settings['id'] ?? '' ) ),
				]
			);
		}
	}

	public function uses_phpmailer_smtp(): bool {
		return true;
	}

	/**
	 * Suggested ports for the selected encryption.
	 *
	 * @return list<int>
	 */
	public static function suggested_ports( string $encryption ): array {
		switch ( $encryption ) {
			case 'ssl':
				return [ 465, 587 ];
			case '':
			case 'none':
				return [ 25, 587 ];
			case 'tls':
			default:
				return [ 587, 465 ];
		}
	}
}
