<?php
/**
 * Cart Recovery — Template Wrapper
 *
 * Wraps the composer's generated body (HTML + plain text) in a clean,
 * email-safe brand template with:
 *   - Store brand header
 *   - Unsubscribe footer
 *   - Open-tracking pixel (HTML only)
 *   - Click-wrap for every `<a href>` in the HTML body so clicks
 *     route through the tracking REST endpoint.
 *
 * Table-based HTML because Gmail/Outlook/Apple Mail still treat
 * modern flex/grid poorly. No JS, no external CSS.
 *
 * URL building is funneled through public helpers so the mailer and
 * the tracking controller agree on routes without magic strings
 * scattered across classes.
 *
 * @package Niwa_AI
 * @since   0.12.0
 */

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

class Niwa_AI_Cart_Recovery_Template_Wrapper {

	/** @var array<string, mixed> */
	private readonly array $settings;

	/**
	 * @param array<string, mixed>|null $settings Optional pre-fetched settings (for testability).
	 */
	public function __construct( ?array $settings = null ) {
		$this->settings = $settings ?? Niwa_AI_Cart_Recovery_Settings::get_settings();
	}

	/**
	 * Wrap composer HTML in the email-safe brand template.
	 *
	 * @param string $body_html Composer-produced HTML (already interpolated for {{COUPON_CODE}}).
	 * @param string $token     Row's unsubscribe_token — drives all per-recipient URLs.
	 * @param string $locale    Recipient locale (used for `<html lang>`).
	 */
	public function wrap_html( string $body_html, string $token, string $locale = 'sr_RS' ): string {
		$brand            = $this->brand_name();
		$unsub_url        = $this->build_unsubscribe_url( $token );
		$pixel_url        = $this->build_open_pixel_url( $token );
		$body_wrapped     = $this->wrap_tracked_links( $this->decorate_recovery_buttons( $body_html, $token ), $token );
		$unsub_label      = $this->unsubscribe_label( $locale );
		$lang_attr        = esc_attr( str_replace( '_', '-', $locale ) );

		$html = $this->_compose_template_html( $brand, $body_wrapped, $unsub_url, $unsub_label, $pixel_url, $lang_attr );

		/**
		 * Filter the final rendered email HTML. Last-mile hook for
		 * embedding custom analytics tags, additional tracking scripts
		 * (use with care — many email clients strip scripts), or
		 * per-tenant brand overrides.
		 */
		$filtered = apply_filters(
			'niwa_ai_cart_recovery_template_html',
			$html,
			array(
				'brand'     => $brand,
				'token'     => $token,
				'locale'    => $locale,
				'unsub_url' => $unsub_url,
				'pixel_url' => $pixel_url,
			)
		);

		return is_string( $filtered ) ? $filtered : $html;
	}

	private function _compose_template_html( string $brand, string $body_wrapped, string $unsub_url, string $unsub_label, string $pixel_url, string $lang_attr ): string {
		return '<!DOCTYPE html><html lang="' . $lang_attr . '"><head>'
			. '<meta charset="UTF-8">'
			. '<meta name="viewport" content="width=device-width,initial-scale=1">'
			. '<title>' . esc_html( $brand ) . '</title>'
			. '</head>'
			. '<body style="margin:0;padding:0;background:#f6f6f6;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#333;">'
			. '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background:#f6f6f6;padding:24px 0;">'
			. '<tr><td align="center">'
			. '<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width:600px;width:100%;background:#ffffff;border-radius:8px;overflow:hidden;">'
			. '<tr><td style="padding:24px 32px 0;font-size:13px;color:#888;letter-spacing:0.5px;text-transform:uppercase;">'
			. esc_html( $brand )
			. '</td></tr>'
			. '<tr><td style="padding:16px 32px 32px;font-size:15px;line-height:1.55;color:#333;">'
			. $body_wrapped
			. '</td></tr>'
			. '<tr><td style="padding:16px 32px 24px;border-top:1px solid #eee;font-size:12px;color:#aaa;line-height:1.5;">'
			. '<a href="' . esc_url( $unsub_url ) . '" style="color:#aaa;text-decoration:underline;">' . esc_html( $unsub_label ) . '</a>'
			. '</td></tr>'
			. '</table>'
			. '<img src="' . esc_url( $pixel_url ) . '" width="1" height="1" alt="" style="display:none;border:0;max-width:1px;max-height:1px;">'
			. '</td></tr>'
			. '</table>'
			. '</body></html>';
	}

	/**
	 * Text-mode mirror. No pixel (text mail clients don't render it),
	 * no click wrap (preserving readability beats tracking here), just
	 * body + unsubscribe URL appended.
	 */
	public function wrap_text( string $body_text, string $token, string $locale = '' ): string {
		$unsub_url   = $this->build_unsubscribe_url( $token );
		$unsub_label = $this->unsubscribe_label( $locale ) . ':';
		return rtrim( $body_text ) . "\n\n-- \n" . $unsub_label . ' ' . $unsub_url;
	}

	private function unsubscribe_label( string $locale ): string {
		$language = strtolower( substr( str_replace( '-', '_', $locale ), 0, 2 ) );
		return match ( $language ) {
			'sr', 'hr' => __( 'Odjavi se od podsetnika', 'niwa-ai' ),
			'de'       => __( 'Erinnerungen abbestellen', 'niwa-ai' ),
			'es'       => __( 'Cancelar recordatorios', 'niwa-ai' ),
			'fr'       => __( 'Se désabonner des rappels', 'niwa-ai' ),
			default    => __( 'Unsubscribe from reminders', 'niwa-ai' ),
		};
	}

	public function build_unsubscribe_url( string $token ): string {
		return $this->rest_url( Niwa_AI_Constants::REST_ROUTE_CR_UNSUBSCRIBE, $token );
	}

	public function build_open_pixel_url( string $token ): string {
		return $this->rest_url( Niwa_AI_Constants::REST_ROUTE_CR_OPEN, $token );
	}

	public function build_cart_recovery_url( string $token ): string {
		return $this->rest_url( Niwa_AI_Constants::REST_ROUTE_CR_RECOVER, $token );
	}

	public function build_click_wrap_url( string $token, string $target_url ): string {
		$base = $this->rest_url( Niwa_AI_Constants::REST_ROUTE_CR_CLICK, $token );
		$encoded = rtrim( strtr( base64_encode( $target_url ), '+/', '-_' ), '=' );
		$sep  = ( str_contains( $base, '?' ) ) ? '&' : '?';
		return $base . $sep . 'u=' . $encoded;
	}

	/**
	 * Replace every `<a href="http(s)://...">` in `$html` with a
	 * click-wrapped version pointing at the REST click endpoint. The
	 * target URL is base64url-encoded to survive query parsing; the
	 * controller decodes + SSRF-validates on the other side.
	 *
	 * Same-origin / same-host URLs only are wrapped — external links
	 * in a cart-recovery email are unlikely and wrapping them risks
	 * breaking spam filter heuristics. Mailto/tel/javascript/etc are
	 * left untouched.
	 */
	public function wrap_tracked_links( string $html, string $token ): string {
		$home_host = parse_url( $this->home_url(), PHP_URL_HOST );
		if ( ! is_string( $home_host ) || '' === $home_host ) {
			return $html;
		}

		return (string) preg_replace_callback(
			'/<a\s+([^>]*?)href\s*=\s*(["\'])(https?:\/\/[^"\']+)\2([^>]*)>/i',
			function ( array $m ) use ( $home_host, $token ): string {
				$pre_attrs  = $m[1];
				$quote      = $m[2];
				$target     = $m[3];
				$post_attrs = $m[4];

				$host = parse_url( $target, PHP_URL_HOST );
				if ( ! is_string( $host ) || strtolower( $host ) !== strtolower( $home_host ) ) {
					return $m[0];
				}

				$wrapped = $this->build_click_wrap_url( $token, $target );
				return '<a ' . $pre_attrs . 'href=' . $quote . esc_url( $wrapped ) . $quote . $post_attrs . '>';
			},
			$html
		);
	}

	private function decorate_recovery_buttons( string $html, string $token ): string {
		$recovery_url = preg_quote( $this->build_cart_recovery_url( $token ), '/' );
		return (string) preg_replace_callback(
			'/<a\s+([^>]*?)href\s*=\s*(["\'])(' . $recovery_url . ')\2([^>]*)>/i',
			static function ( array $m ): string {
				$pre_attrs  = $m[1];
				$quote      = $m[2];
				$target     = $m[3];
				$post_attrs = $m[4];
				$attrs      = $pre_attrs . $post_attrs;

				if ( preg_match( '/\sstyle\s*=/i', $attrs ) ) {
					return $m[0];
				}

				$style = 'display:inline-block;background:#111827;color:#ffffff;text-decoration:none;padding:12px 18px;border-radius:6px;font-weight:700;';
				return '<a ' . $pre_attrs . 'href=' . $quote . $target . $quote . ' style="' . $style . '"' . $post_attrs . '>';
			},
			$html
		);
	}

	protected function brand_name(): string {
		$brand = (string) ( $this->settings['store_brand_name'] ?? '' );
		if ( '' !== $brand ) {
			return $brand;
		}
		if ( function_exists( 'get_bloginfo' ) ) {
			$bloginfo = (string) get_bloginfo( 'name' );
			if ( '' !== $bloginfo ) {
				return $bloginfo;
			}
		}
		return __( 'Shop', 'niwa-ai' );
	}

	protected function rest_url( string $route, string $token ): string {
		$path = (string) preg_replace( '/\\(\\?P<token>[^)]+\\)/', rawurlencode( $token ), $route );
		if ( function_exists( 'rest_url' ) ) {
			return (string) rest_url( Niwa_AI_Constants::REST_NS_CART_RECOVERY . $path );
		}
		return rtrim( $this->home_url(), '/' ) . '/wp-json/' . Niwa_AI_Constants::REST_NS_CART_RECOVERY . $path;
	}

	protected function home_url(): string {
		if ( function_exists( 'home_url' ) ) {
			return (string) home_url();
		}
		return 'https://example.com';
	}
}
