<?php

namespace Clonable\Services;

use Clonable\Helpers\Functions;
use Clonable\Helpers\TextTranslator;
use Clonable\Services\ClonableWooCommerceService;
use function is_string;
use function trim;
use const PREG_SPLIT_NO_EMPTY;

if (!defined('ABSPATH')) {
	exit; // Exit if accessed directly.
}

class EmailTranslationsService
{
	private const OPT_ENABLED = 'clonable_email_translations_enabled';
	private const OPT_EXCLUDED = 'clonable_email_translations_excluded_addresses';
	private const OPT_HEADERS_BY_DOMAIN = 'clonable_email_headers_by_domain';
	private $currentOrderId = null;

	public function __construct()
	{
		if (!$this->emailTranslationsEnabled()) {
			return;
		}

		add_filter('wp_mail', [$this, 'translateMail'], 20, 1);
		add_filter('wp_mail_from', [$this, 'setMailFrom'], PHP_INT_MAX);
		add_filter('wp_mail_from_name', [$this, 'setMailSenderName'], PHP_INT_MAX);
		add_filter('option_woocommerce_email_header_image', [$this, 'setMailHeaderImage'], PHP_INT_MAX);
		add_action('phpmailer_init', [$this, 'override_phpmailer_sender'], PHP_INT_MAX);

		// Capture order ID for use in email translations (in case clone ID is not available in the request, e.g. for async emails)
		add_action('woocommerce_after_order_object_save', [$this, 'captureOrderFromSave'], 10, 1);
	}

	public function captureOrderFromSave($order)
	{
		if ($order instanceof \WC_Order) {
			$order_id = $order->get_id();
			$this->currentOrderId = $order_id;
		}
	}

	public function translateMail(array $args): array
	{
		$clone_id = $this->getCloneIdFromRequest();
		if (!$clone_id) {  // no clone ID in $_POST data, so we check if we have a global order ID set by the WooCommerce email hook, and if so, add it to the args for potential use in translation
			$order_id = $this->currentOrderId;
			// get the clone id from the order meta, if it exists
			if (!empty($order_id)) {
				$order_clone_id = wc_get_order($order_id)->get_meta('clonable_clone_id', true);
				if (!empty($order_clone_id)) {
					$clone_id = $order_clone_id;
				}
			}
		}

		if (empty($clone_id)) {
			return $args;
		}

		if ($this->isRecipientExcluded($args)) {
			return $args;
		}

		$hasSubject = isset($args['subject']) && is_string($args['subject']) && $args['subject'] !== '';
		if ($hasSubject) {
			$args['subject'] = TextTranslator::translateText($args['subject'], $clone_id);
		}

		$hasMessage = isset($args['message']) && is_string($args['message']) && $args['message'] !== '';
		if ($hasMessage) {
			$message         = Functions::minify_html($args['message']);
			$args['message'] = TextTranslator::translateText((string) $message, $clone_id, false);
		}

		// set reply-to header
		$headerInfo = $this->getHeaderInfoForClone($clone_id);

		if ($headerInfo === null) {
			return $args;
		}
		$replyToAddress = isset($headerInfo['reply_to_address']) && is_string($headerInfo['reply_to_address'])
			? trim($headerInfo['reply_to_address'])
			: '';

		if ($replyToAddress !== '') {
			$headers = $args['headers'] ?? [];
			if (is_string($headers)) {
				$headers .= "\r\n" . 'Reply-To: ' . $replyToAddress;
			} elseif (is_array($headers)) {
				$headers[] = 'Reply-To: ' . $replyToAddress;
			} else {
				$headers = ['Reply-To: ' . $replyToAddress];
			}
			$args['headers'] = $headers;
		}

		return $args;
	}

	public function setMailFrom(string $from): string
	{
		$clone_id = $this->getCloneIdFromRequest();
		if (empty($clone_id)) {
			return $from;
		}

		$headerInfo = $this->getHeaderInfoForClone($clone_id);
		if ($headerInfo === null) {
			return $from;
		}

		$fromAddress = isset($headerInfo['from_address']) && is_string($headerInfo['from_address'])
			? trim($headerInfo['from_address'])
			: '';

		return $fromAddress !== '' ? $fromAddress : $from;
	}

	public function setMailSenderName(string $name): string
	{
		$clone_id = $this->getCloneIdFromRequest();
		if (empty($clone_id)) {
			return $name;
		}

		$headerInfo = $this->getHeaderInfoForClone($clone_id);
		if ($headerInfo === null) {
			return $name;
		}

		$senderName = isset($headerInfo['sender_name']) && is_string($headerInfo['sender_name'])
			? trim($headerInfo['sender_name'])
			: '';

		return $senderName !== '' ? $senderName : $name;
	}

	public function setMailHeaderImage(string $image): string
	{
		$clone_id = $this->getCloneIdFromRequest();
		if (empty($clone_id)) {
			return $image;
		}

		$headerInfo = $this->getHeaderInfoForClone($clone_id);
		if ($headerInfo === null) {
			return $image;
		}

		$emailLogo = isset($headerInfo['email_logo']) && is_string($headerInfo['email_logo'])
			? trim($headerInfo['email_logo'])
			: '';

		return $emailLogo !== '' ? $emailLogo : $image;
	}

	/**
	 * Directly override the sender details on the PHPMailer instance.
	 * This helps ensure compatibility with SMTP plugins such as WP Mail SMTP.
	 *
	 * @param \PHPMailer PHPMailer instance
	 */
	public function override_phpmailer_sender($php_mailer)
	{
		$from_name  = $this->setMailSenderName($php_mailer->FromName);
		$from_email = $this->setMailFrom($php_mailer->From);

		// Override From Name
		if (!empty($from_name)) {
			$php_mailer->FromName = $from_name;
		}

		// Override From Email
		if (!empty($from_email)) {
			$php_mailer->From   = $from_email;
			$php_mailer->Sender = $from_email; // Also set Sender for better compatibility
		}

		// Set Reply-To if specified
		$clone_id    = $this->getCloneIdFromRequest();
		$header_info = $this->getHeaderInfoForClone($clone_id);
		if ($header_info !== null) {
			$reply_to_address = isset($header_info['reply_to_address']) && is_string($header_info['reply_to_address'])
				? trim($header_info['reply_to_address'])
				: '';
			if ($reply_to_address !== '') {
				$php_mailer->addReplyTo($reply_to_address);

				try {
					$php_mailer->addReplyTo($reply_to_address, $from_name);
				} catch (\Exception $e) {
					// Ignore invalid email address
				}
			}
		}

		// Ensure UTF-8 charset
		$php_mailer->CharSet = 'UTF-8';
	}

	private function getCloneIdFromRequest(): ?string
	{
		return isset($_POST['clonable_clone_id']) ? (string) $_POST['clonable_clone_id'] : null;
	}

	private function emailTranslationsEnabled(): bool
	{
		return get_option(self::OPT_ENABLED, 'on') === 'on';
	}

	private function getHeadersByDomain(): array
	{
		$raw     = get_option(self::OPT_HEADERS_BY_DOMAIN, '[]');
		$decoded = json_decode((string) $raw, true);

		return is_array($decoded) ? $decoded : [];
	}

	private function getHeaderInfoForClone(?string $clone_id): ?array
	{
		if (empty($clone_id)) {
			return null;
		}

		foreach ($this->getHeadersByDomain() as $header_info) {
			if (!is_array($header_info)) {
				continue;
			}

			$header_clone_id = isset($header_info['clone_id']) ? trim((string) $header_info['clone_id']) : '';
			if ($header_clone_id === $clone_id) {
				return $header_info;
			}
		}

		return null;
	}

	private function isRecipientExcluded(array $args): bool
	{
		$option = get_option(self::OPT_EXCLUDED, '');
		if (!is_string($option) || $option === '') {
			return false;
		}

		$excluded = $this->parseLowercasedList($option);
		if ($excluded === []) {
			return false;
		}

		$to         = $args['to'] ?? null;
		$recipients = [];

		if (is_string($to) && $to !== '') {
			$recipients = preg_split('/[\s,;]+/', $to, -1, PREG_SPLIT_NO_EMPTY) ?: [];
		} elseif (is_array($to)) {
			$recipients = $to;
		}

		foreach ($recipients as $recipient) {
			if (!is_string($recipient) || $recipient === '') {
				continue;
			}

			$recipientLower = Functions::lower($recipient);
			foreach ($excluded as $needle) {
				if ($needle !== '' && strpos($recipientLower, $needle) !== false) {
					return true;
				}
			}
		}

		return false;
	}

	private function parseLowercasedList(string $value): array
	{
		$parts = preg_split('/[\s,;]+/', $value, -1, PREG_SPLIT_NO_EMPTY) ?: [];
		$out   = [];

		foreach ($parts as $part) {
			$part = trim((string) $part);
			if ($part !== '') {
				$out[] = Functions::lower($part);
			}
		}

		return $out;
	}
}
