<?php
/**
 * WooCommerce filter integration for template rendering.
 *
 * @package MailDoctor
 */

namespace MailDoctor;

use WC_Email;
use WC_Order;

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

/**
 * Hooks into WooCommerce email rendering lifecycle.
 */
trait WPMD_Email_Templates_Filters {

	/**
	 * Capture Woo email context for rendering.
	 *
	 * @param WC_Email $email Email object.
	 * @return void
	 */
	public static function capture_email_context( $email ): void {
		if ( ! class_exists( 'WC_Email' ) || ! $email instanceof WC_Email ) {
			return;
		}

		self::$current_email = $email;

		$object = $email->object ?? null;
		if ( class_exists( 'WC_Order' ) && $object instanceof WC_Order ) {
			self::$current_order = $object;
		} else {
			self::$current_order = null;
		}
	}

	/**
	 * Release stored email context.
	 *
	 * @return void
	 */
	public static function release_email_context(): void {
		self::$current_email      = null;
		self::$current_order      = null;
		self::$prepared_templates = array();
	}

	/**
	 * Replace Woo email subject with template subject when available.
	 *
	 * @param string   $subject Default subject.
	 * @param WC_Email $email   Email instance.
	 * @return string
	 */
	public static function filter_email_subject( string $subject, $email ): string {
		if ( ! self::is_enabled() || ! class_exists( 'WC_Email' ) ) {
			return $subject;
		}

		$email = $email instanceof WC_Email ? $email : ( self::$current_email ?? null );
		if ( ! $email instanceof WC_Email ) {
			return $subject;
		}

		$locale   = self::determine_email_locale( $email );
		$resolved = self::resolver()->resolve( $email, $locale );
		if ( null === $resolved ) {
			return $subject;
		}

		$order      = self::resolve_order( $email, self::$current_order ?? null );
		$email_type = $email->id;
		$prepared   = self::prepare_template_for_email(
			$email_type,
			$order,
			array(
				'email'    => $email,
				'template' => $resolved->template()->to_array(),
			)
		);

		if ( empty( $prepared['subject'] ) ) {
			return $subject;
		}

		return $prepared['subject'];
	}

	/**
	 * Replace Woo email heading with template heading when available.
	 *
	 * @param string   $heading Default heading.
	 * @param WC_Email $email   Email instance.
	 * @return string
	 */
	public static function filter_email_heading( string $heading, $email ): string {
		if ( ! self::is_enabled() || ! class_exists( 'WC_Email' ) ) {
			return $heading;
		}

		$email = $email instanceof WC_Email ? $email : ( self::$current_email ?? null );

		if ( ! $email instanceof WC_Email ) {
			return $heading;
		}

		$locale   = self::determine_email_locale( $email );
		$resolved = self::resolver()->resolve( $email, $locale );
		if ( null === $resolved ) {
			return $heading;
		}

		$order      = self::resolve_order( $email, self::$current_order ?? null );
		$email_type = $email->id;
		$prepared   = self::prepare_template_for_email(
			$email_type,
			$order,
			array(
				'email'    => $email,
				'template' => $resolved->template()->to_array(),
			)
		);

		if ( empty( $prepared['heading'] ) ) {
			return $heading;
		}

		return $prepared['heading'];
	}

	/**
	 * Swap Woo email content with rendered template when applicable.
	 *
	 * @param string        $content    Default content.
	 * @param WC_Email|null $email      Woo email object.
	 * @param bool          $plain_text Whether Woo is rendering plain text.
	 * @return string
	 */
	public static function filter_mail_content( string $content, $email = null, $plain_text = false ): string {
		if ( ! self::is_enabled() || ! class_exists( 'WC_Email' ) ) {
			return $content;
		}

		if ( true === $plain_text ) {
			return $content;
		}

		$email = $email instanceof WC_Email ? $email : ( self::$current_email ?? null );
		if ( ! $email instanceof WC_Email ) {
			return $content;
		}

		$locale   = self::determine_email_locale( $email );
		$resolved = self::resolver()->resolve( $email, $locale );
		if ( null === $resolved ) {
			return $content;
		}

		$order      = self::resolve_order( $email, self::$current_order ?? null );
		$email_type = $email->id;

		$prepared = self::prepare_template_for_email(
			$email_type,
			( class_exists( 'WC_Order' ) && $order instanceof WC_Order ) ? $order : null,
			array(
				'email'         => $email,
				'sent_to_admin' => method_exists( $email, 'is_sent_to_admin' ) ? $email->is_sent_to_admin() : false,
				'template'      => $resolved->template()->to_array(),
			)
		);

		if ( empty( $prepared['html'] ) ) {
			return $content;
		}

		return apply_filters(
			'mail_doctor_mail_content',
			$prepared['html'],
			$email_type,
			$order,
			$email,
			$prepared
		);
	}

	/**
	 * Determine template file path for Woo template resolution.
	 *
	 * @param string $template      Located template.
	 * @param string $template_name Template name.
	 * @param string $template_path Template path.
	 * @return string
	 */
	public static function locate_template( string $template, string $template_name, string $template_path ): string { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
		if ( ! self::is_enabled() ) {
			return $template;
		}

		$email_type = self::email_type_from_template_name( $template_name );

		if ( ! $email_type ) {
			return $template;
		}

		$email    = self::$current_email instanceof WC_Email ? self::$current_email : null;
		$locale   = $email instanceof WC_Email ? self::determine_email_locale( $email ) : ( function_exists( 'determine_locale' ) ? determine_locale() : get_locale() );
		$resolved = $email instanceof WC_Email ? self::resolver()->resolve( $email, $locale ) : null;
		if ( null === $resolved ) {
			return $template;
		}

		$resolved_template = $resolved->template();
		if ( 'custom' !== $resolved_template->source() || ! $resolved_template->is_active() ) {
			return $template;
		}

		$file = MAIL_DOCTOR_PATH . 'templates/emails/' . $email_type . '.php';
		if ( file_exists( $file ) ) {
			return $file;
		}

		return $template;
	}

	/**
	 * Resolve current order context for an email.
	 *
	 * @param WC_Email      $email    Email instance.
	 * @param WC_Order|null $fallback Optional order.
	 * @return WC_Order|null
	 */
	protected static function resolve_order( WC_Email $email, $fallback = null ) {
		if ( class_exists( 'WC_Order' ) && $fallback instanceof WC_Order ) {
			return $fallback;
		}

		if ( method_exists( $email, 'get_order' ) ) {
			$maybe_order = $email->get_order();
			if ( $maybe_order instanceof WC_Order ) {
				return $maybe_order;
			}
		}

		return null;
	}

	/**
	 * Prepare template payload for current email.
	 *
	 * @param string               $email_type Email identifier.
	 * @param WC_Order|null        $order      Order instance.
	 * @param array<string, mixed> $context Additional context.
	 * @return array<string, mixed>
	 */
	protected static function prepare_template_for_email( string $email_type, $order = null, array $context = array() ): array {
		$order_id = ( class_exists( 'WC_Order' ) && $order instanceof WC_Order ) ? $order->get_id() : 0;
		$key      = $email_type . ':' . $order_id;

		if ( isset( self::$prepared_templates[ $key ] ) ) {
			return self::$prepared_templates[ $key ];
		}

		$context['email_type'] = $email_type;

		if ( ! isset( $context['email'] ) && class_exists( 'WC_Email' ) && self::$current_email instanceof WC_Email ) {
			$context['email'] = self::$current_email;
		}

		$extra_tokens = self::gather_additional_tokens( $email_type, $context['email'] ?? null );
		if ( ! empty( $extra_tokens ) ) {
			if ( empty( $context['extra_tokens'] ) || ! is_array( $context['extra_tokens'] ) ) {
				$context['extra_tokens'] = $extra_tokens;
			} else {
				$context['extra_tokens'] = array_merge( $context['extra_tokens'], $extra_tokens );
			}
		}

		$prepared = mail_doctor_prepare_email_template(
			$email_type,
			$order_id,
			array_merge(
				$context,
				array(
					'order' => $order,
				)
			)
		);

		self::$prepared_templates[ $key ] = $prepared;

		return $prepared;
	}

	/**
	 * Collect additional template tokens from email object.
	 *
	 * @param string   $email_type Email identifier.
	 * @param WC_Email $email      Email instance.
	 * @return array<string, string>
	 */
	protected static function gather_additional_tokens( string $email_type, $email ): array {
		if ( ! class_exists( 'WC_Email' ) || ! $email instanceof WC_Email ) {
			return array();
		}

		$tokens = array();

		if ( 'customer_reset_password' === $email_type ) {
			$reset_url = '';

			if ( isset( $email->reset_url ) && is_string( $email->reset_url ) ) {
				$reset_url = $email->reset_url;
			} elseif ( isset( $email->reset_password_url ) && is_string( $email->reset_password_url ) ) {
				$reset_url = $email->reset_password_url;
			} elseif ( method_exists( $email, 'get_reset_url' ) ) {
				try {
					$reset_url = (string) $email->get_reset_url();
				} catch ( \Throwable $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
					$reset_url = '';
				}
			}

			if ( empty( $reset_url ) && function_exists( 'wc_get_account_endpoint_url' ) ) {
				$reset_url = wc_get_account_endpoint_url( 'lost-password' );
			}

			if ( empty( $reset_url ) && function_exists( 'wp_lostpassword_url' ) ) {
				$reset_url = wp_lostpassword_url();
			}

			if ( $reset_url ) {
				$tokens['reset_url'] = esc_url_raw( $reset_url );
			}
		}

		if ( 'customer_note' === $email_type ) {
			if ( isset( $email->customer_note ) && is_string( $email->customer_note ) ) {
				$tokens['customer_note'] = $email->customer_note;
			} elseif ( isset( $email->note ) && is_string( $email->note ) ) {
				$tokens['customer_note'] = $email->note;
			}
		}

		return $tokens;
	}

	/**
	 * Infer email type from Woo template name.
	 *
	 * @param string $template_name Template identifier.
	 * @return string
	 */
	public static function email_type_from_template_name( string $template_name ): string {
		$template_name = ltrim( str_replace( '\\', '/', $template_name ), '/' );

		if ( false === strpos( $template_name, 'emails/' ) ) {
			return '';
		}

		if ( 0 === strpos( $template_name, 'emails/plain/' ) ) {
			return '';
		}

		$lookup = array(
			'emails/admin-new-order.php'           => 'new_order',
			'emails/customer-processing-order.php' => 'customer_processing_order',
			'emails/customer-completed-order.php'  => 'customer_completed_order',
			'emails/customer-invoice.php'          => 'customer_invoice',
			'emails/customer-note.php'             => 'customer_note',
			'emails/failed-order.php'              => 'failed_order',
			'emails/customer-refunded-order.php'   => 'customer_refunded_order',
			'emails/customer-reset-password.php'   => 'customer_reset_password',
		);

		if ( isset( $lookup[ $template_name ] ) ) {
			return $lookup[ $template_name ];
		}

		$basename = basename( $template_name, '.php' );
		if ( 0 === strpos( $basename, 'admin-' ) ) {
			$basename = substr( $basename, 6 );
		}

		if ( '' === $basename ) {
			return '';
		}

		return str_replace( '-', '_', $basename );
	}

	/**
	 * Map an email identifier to the corresponding WooCommerce template file.
	 *
	 * @param string $email_type Email identifier.
	 * @return string Template name.
	 */
	public static function template_name_from_email_type( string $email_type ): string {
		$email_type = sanitize_key( $email_type );

		$lookup = array(
			'new_order'                 => 'emails/admin-new-order.php',
			'cancelled_order'           => 'emails/cancelled-order.php',
			'failed_order'              => 'emails/failed-order.php',
			'customer_processing_order' => 'emails/customer-processing-order.php',
			'customer_completed_order'  => 'emails/customer-completed-order.php',
			'customer_refunded_order'   => 'emails/customer-refunded-order.php',
			'customer_invoice'          => 'emails/customer-invoice.php',
			'customer_note'             => 'emails/customer-note.php',
			'customer_reset_password'   => 'emails/customer-reset-password.php',
			'new_account'               => 'emails/customer-new-account.php',
		);

		return $lookup[ $email_type ] ?? '';
	}

	/**
	 * Determine locale for the current email.
	 *
	 * @param WC_Email $email Email instance.
	 * @return string
	 */
	protected static function determine_email_locale( WC_Email $email ): string {
		if ( method_exists( $email, 'get_locale' ) ) {
			$locale = (string) $email->get_locale();
			if ( '' !== $locale ) {
				return $locale;
			}
		}

		return function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
	}
}
