<?php
/**
 * Site email REST controller.
 *
 * @package RewindBrevoFormBridge
 */

namespace RewindBrevoFormBridge\Rest\Controllers;

use RewindBrevoFormBridge\Admin\Settings;
use RewindBrevoFormBridge\Api\Cache;
use RewindBrevoFormBridge\Api\Transactional;
use RewindBrevoFormBridge\Emails\Mailer;
use RewindBrevoFormBridge\Emails\MailStatus;
use RewindBrevoFormBridge\Emails\SmtpRelay;
use RewindBrevoFormBridge\Rest\BaseController;
use RewindBrevoFormBridge\Support\Errors;

defined( 'ABSPATH' ) || exit;

/**
 * Everything the Settings → Email screen needs.
 *
 * GET /email answers from stored state and the sender cache only — it never
 * reaches Brevo. The two routes that do are both explicit button presses:
 * /email/test and /email/senders/refresh.
 *
 * @since 0.5.0
 */
class EmailController extends BaseController {

	/**
	 * Other mail-routing plugins we should warn about, slug => label.
	 *
	 * Two plugins both rewriting wp_mail() is the classic cause of "some
	 * mail sends, some vanishes": whichever hooks last wins, unpredictably.
	 *
	 * @var array<string, string>
	 */
	const KNOWN_MAILERS = array(
		'wp-mail-smtp/wp_mail_smtp.php'             => 'WP Mail SMTP',
		'easy-wp-smtp/easy-wp-smtp.php'             => 'Easy WP SMTP',
		'post-smtp/postman-smtp.php'                => 'Post SMTP',
		'fluent-smtp/fluent-smtp.php'               => 'FluentSMTP',
		'wp-smtp/wp-smtp.php'                       => 'WP SMTP',
		'gmail-smtp/main.php'                       => 'Gmail SMTP',
		'smtp-mailer/main.php'                      => 'SMTP Mailer',
		'sendgrid-email-delivery-simplified/wpsendgrid.php' => 'SendGrid',
		'mailin/sendinblue.php'                     => 'Brevo (official plugin)',
	);

	/**
	 * Settings storage service.
	 *
	 * @var Settings
	 */
	private Settings $settings;

	/**
	 * API-transport mailer.
	 *
	 * @var Mailer
	 */
	private Mailer $mailer;

	/**
	 * SMTP-relay transport.
	 *
	 * @var SmtpRelay
	 */
	private SmtpRelay $smtp;

	/**
	 * Transactional resource (senders).
	 *
	 * @var Transactional
	 */
	private Transactional $transactional;

	/**
	 * Transient cache.
	 *
	 * @var Cache
	 */
	private Cache $cache;

	/**
	 * Constructor.
	 *
	 * @since 0.5.0
	 * @param Settings      $settings      Settings storage service.
	 * @param Mailer        $mailer        API-transport mailer.
	 * @param SmtpRelay     $smtp          SMTP-relay transport.
	 * @param Transactional $transactional Transactional resource.
	 * @param Cache         $cache         Transient cache.
	 */
	public function __construct( Settings $settings, Mailer $mailer, SmtpRelay $smtp, Transactional $transactional, Cache $cache ) {
		parent::__construct( 'email' );

		$this->settings      = $settings;
		$this->mailer        = $mailer;
		$this->smtp          = $smtp;
		$this->transactional = $transactional;
		$this->cache         = $cache;
	}

	/**
	 * Register routes.
	 *
	 * @since 0.5.0
	 * @return void
	 */
	public function register_routes(): void {
		register_rest_route(
			self::NAMESPACE_V1,
			'/' . $this->rest_base,
			array(
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_status' ),
				'permission_callback' => array( $this, 'can_manage' ),
			)
		);

		register_rest_route(
			self::NAMESPACE_V1,
			'/' . $this->rest_base . '/test',
			array(
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'send_test' ),
				'permission_callback' => array( $this, 'can_manage' ),
				'args'                => array(
					'to' => array(
						'type'              => 'string',
						'required'          => true,
						'sanitize_callback' => 'sanitize_email',
						'validate_callback' => array( $this, 'validate_email' ),
					),
				),
			)
		);

		register_rest_route(
			self::NAMESPACE_V1,
			'/' . $this->rest_base . '/senders/refresh',
			array(
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'refresh_senders' ),
				'permission_callback' => array( $this, 'can_manage' ),
			)
		);
	}

	/**
	 * Validate callback for a required email address.
	 *
	 * @since 0.5.0
	 * @param mixed $value Raw value.
	 * @return bool
	 */
	public function validate_email( $value ): bool {
		return (bool) is_email( trim( (string) $value ) );
	}

	/**
	 * Delivery status. Reads stored state and the sender cache only.
	 *
	 * @since 0.5.0
	 * @return \WP_REST_Response
	 */
	public function get_status(): \WP_REST_Response {
		$transport = (string) $this->settings->get( 'email_transport', 'api' );
		$senders   = $this->transactional->cached_senders();
		$from      = (string) $this->settings->get( 'email_from_email', '' );

		return $this->respond(
			array(
				'enabled'          => (bool) $this->settings->get( 'email_enabled', false ),
				'transport'        => $transport,
				'active'           => $this->mailer->is_active() || $this->smtp->is_active(),
				'has_api_key'      => '' !== $this->settings->api_key(),
				'has_smtp_key'     => '' !== $this->settings->smtp_key(),
				'smtp_key_source'  => $this->settings->smtp_key_source(),
				'smtp_key_hint'    => $this->settings->masked_smtp_key(),
				'from_email'       => $from,
				'sender_verified'  => $this->sender_verified( $from, $senders ),
				'senders'          => null === $senders ? array() : $senders,
				'senders_cached'   => null !== $senders,
				'last_sent'        => MailStatus::last_sent(),
				'last_error'       => MailStatus::last_error(),
				'conflicts'        => $this->conflicts(),
				'smtp_ports'       => SmtpRelay::PORTS,
				'smtp_host'        => SmtpRelay::HOST,
			)
		);
	}

	/**
	 * Send a test email through whichever transport is configured.
	 *
	 * @since 0.5.0
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function send_test( \WP_REST_Request $request ) {
		$to = (string) $request->get_param( 'to' );

		if ( 'smtp' === $this->settings->get( 'email_transport', 'api' ) ) {
			return $this->send_test_over_smtp( $to );
		}

		if ( '' === $this->settings->api_key() ) {
			return $this->error(
				'rbfb_no_key',
				__( 'Add your Brevo API key on the Connections screen before sending a test.', 'rewind-form-bridge-for-brevo' )
			);
		}

		$response = $this->mailer->send_test( $to );

		if ( ! $response->is_success() ) {
			return $this->error( 'rbfb_mail_failed', Errors::for_email( $response ) );
		}

		$body = $response->body();

		return $this->respond(
			array(
				'sent'       => true,
				'transport'  => 'api',
				'message_id' => isset( $body['messageId'] ) ? (string) $body['messageId'] : '',
				'sent_at'    => MailStatus::last_sent(),
			)
		);
	}

	/**
	 * Test the relay by putting a real wp_mail() through it.
	 *
	 * Nothing else would prove the route works: the relay only engages
	 * inside PHPMailer, so the settings have to be saved and switched on
	 * first. Failures arrive through wp_mail_failed, which SmtpRelay
	 * records.
	 *
	 * @since 0.5.0
	 * @param string $to Recipient address.
	 * @return \WP_REST_Response|\WP_Error
	 */
	private function send_test_over_smtp( string $to ) {
		if ( ! $this->smtp->is_active() ) {
			return $this->error(
				'rbfb_smtp_inactive',
				__( 'Save your SMTP login and key and switch Brevo email delivery on before sending a test — the relay only applies to real wp_mail() calls.', 'rewind-form-bridge-for-brevo' )
			);
		}

		$sent = wp_mail(
			$to,
			__( 'Rewind Form Bridge for Brevo — test email', 'rewind-form-bridge-for-brevo' ),
			__( 'This is a test email sent through the Brevo SMTP relay. If you are reading it, WordPress email from this site delivers through Brevo — no SMTP plugin required.', 'rewind-form-bridge-for-brevo' )
		);

		if ( ! $sent ) {
			$error = MailStatus::last_error();

			return $this->error(
				'rbfb_mail_failed',
				null === $error
					? __( 'The relay refused the message and reported no reason. Check the SMTP login and key, and whether this host allows outbound connections on the chosen port.', 'rewind-form-bridge-for-brevo' )
					: $error['message']
			);
		}

		return $this->respond(
			array(
				'sent'      => true,
				'transport' => 'smtp',
				'sent_at'   => MailStatus::last_sent(),
			)
		);
	}

	/**
	 * Explicit "Refresh senders": flush that one cache key and refetch.
	 *
	 * @since 0.5.0
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function refresh_senders() {
		if ( '' === $this->settings->api_key() ) {
			return $this->error(
				'rbfb_no_key',
				__( 'Add your Brevo API key on the Connections screen before loading senders.', 'rewind-form-bridge-for-brevo' )
			);
		}

		$this->cache->delete( 'senders' );

		$senders = $this->transactional->senders( true );

		if ( is_wp_error( $senders ) ) {
			return $this->from_wp_error( $senders );
		}

		return $this->respond(
			array(
				'senders'         => $senders,
				'sender_verified' => $this->sender_verified( (string) $this->settings->get( 'email_from_email', '' ), $senders ),
			)
		);
	}

	/**
	 * Whether the saved From address matches an active Brevo sender.
	 *
	 * @since 0.5.0
	 * @param string     $from    Saved From address.
	 * @param array|null $senders Cached senders, null when the cache is cold.
	 * @return bool|null Null when we have no senders to judge against.
	 */
	private function sender_verified( string $from, ?array $senders ): ?bool {
		if ( null === $senders || 0 === count( $senders ) || '' === $from ) {
			return null;
		}

		foreach ( $senders as $sender ) {
			if ( isset( $sender['email'] ) && strtolower( (string) $sender['email'] ) === strtolower( $from ) ) {
				return ! empty( $sender['active'] );
			}
		}

		return false;
	}

	/**
	 * Other active plugins that also rewrite wp_mail().
	 *
	 * @since 0.5.0
	 * @return string[] Human labels, empty when nothing conflicts.
	 */
	private function conflicts(): array {
		if ( ! function_exists( 'is_plugin_active' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		$found = array();

		foreach ( self::KNOWN_MAILERS as $file => $label ) {
			if ( is_plugin_active( $file ) ) {
				$found[] = $label;
			}
		}

		return $found;
	}
}
