<?php
/**
 * Cart Recovery — Placeholder Renderer
 *
 * Pure helper that expands `{placeholder}` tokens inside an admin-
 * authored template string. Used by the composer when an admin has
 * chosen to override the AI output with their own copy.
 *
 * Supported placeholders:
 *   {customer_name}    Customer first name, or empty string.
 *   {cart_items}       Comma-separated "Product × qty" list (plain text).
 *   {cart_items_html}  Email-safe product card table built from cart items.
 *   {cart_total}       Formatted total with currency code.
 *   {cart_subtotal}    Formatted subtotal with currency code.
 *   {coupon_code}      Coupon code for mail2 (empty for mail1).
 *   {coupon_discount}  Human-readable discount value (late mailer replacement).
 *   {coupon_ttl_hours} Coupon validity window in hours.
 *   {cart_recovery_url} Tokenized cart restore URL (late mailer replacement).
 *   {store_brand}      Store / brand name from settings.
 *
 * The renderer is intentionally dumb: no conditional blocks, no loops,
 * no dynamic PHP evaluation — just `str_replace` over a whitelist. Safe
 * to feed admin input directly.
 *
 * @package Niwa_AI
 * @since   0.12.0
 */

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

class Niwa_AI_Cart_Recovery_Placeholder_Renderer {

	/**
	 * Build the replacement map once per call. Shared between subject /
	 * html / text expansions so we don't recompute formatting three
	 * times.
	 *
	 * @param Niwa_AI_Cart_Recovery_Composition_Context $ctx         Context.
	 * @param string                                    $coupon_code Actual coupon code (empty for mail1).
	 * @return array<string, string>
	 */
	public static function build_map(
		Niwa_AI_Cart_Recovery_Composition_Context $ctx,
		string $coupon_code = ''
	): array {
		$snapshot = $ctx->snapshot;

		$items_plain = array();
		foreach ( $snapshot->items as $item ) {
			$items_plain[] = sprintf( '%s × %d', $item->name, $item->qty );
		}

		return array(
			'{customer_name}'    => $ctx->customer_name,
			'{cart_items}'       => implode( ', ', $items_plain ),
			'{cart_items_html}'  => self::render_items_cards_html( $snapshot->items, $snapshot->currency, $ctx->locale ),
			'{cart_total}'       => self::format_money( $snapshot->total, $snapshot->currency ),
			'{cart_subtotal}'    => self::format_money( $snapshot->subtotal, $snapshot->currency ),
			'{coupon_code}'      => $coupon_code,
			'{coupon_discount}'  => '{coupon_discount}',
			'{coupon_ttl_hours}' => (string) $ctx->coupon_ttl_hours,
			'{cart_recovery_url}' => '{cart_recovery_url}',
			'{store_brand}'      => $ctx->store_name,
		);
	}

	/**
	 * Expand placeholders in a string. Unknown tokens are left as-is
	 * so typos in admin templates show up in the preview rather than
	 * silently eating content.
	 */
	public static function render( string $template, array $map ): string {
		if ( '' === $template || empty( $map ) ) {
			return $template;
		}
		return strtr( $template, $map );
	}

	/**
	 * Convenience wrapper: expand the full Composition_Result subject +
	 * html + text triplet from a stage template.
	 *
	 * @param array{subject: string, html: string, text: string}  $template
	 * @param Niwa_AI_Cart_Recovery_Composition_Context            $ctx
	 * @param string                                               $coupon_code
	 * @return array{subject: string, html: string, text: string}
	 */
	public static function expand_stage_template(
		array $template,
		Niwa_AI_Cart_Recovery_Composition_Context $ctx,
		string $coupon_code = ''
	): array {
		$map = self::build_map( $ctx, $coupon_code );
		return array(
			'subject' => self::render( (string) ( $template['subject'] ?? '' ), $map ),
			'html'    => self::render( (string) ( $template['html'] ?? '' ), $map ),
			'text'    => self::render( (string) ( $template['text'] ?? '' ), $map ),
		);
	}

	/**
	 * @return array<int, string>  List of supported placeholder tokens
	 *                             (with braces) — useful for the admin
	 *                             UI legend.
	 */
	public static function available_placeholders(): array {
		return array(
			'{customer_name}',
			'{cart_items}',
			'{cart_items_html}',
			'{cart_total}',
			'{cart_subtotal}',
			'{coupon_code}',
			'{coupon_discount}',
			'{coupon_ttl_hours}',
			'{cart_recovery_url}',
			'{store_brand}',
		);
	}

	/**
	 * Render a compact email-safe product recap. Frontend product cards
	 * depend on JS/CSS that email clients will strip, so this is a
	 * separate table-based renderer fed only by snapshot data.
	 *
	 * @param array<int, Niwa_AI_Cart_Recovery_Line_Item> $items
	 */
	private static function render_items_cards_html( array $items, string $currency, string $locale ): string {
		if ( empty( $items ) ) {
			return '';
		}

		$qty_label = self::quantity_label( $locale );
		$html = '<table role="presentation" data-niwa-cart-items="1" width="100%" cellspacing="0" cellpadding="0" border="0" style="border-collapse:separate;border-spacing:0 10px;margin:18px 0;">';
		foreach ( $items as $item ) {
			if ( ! $item instanceof Niwa_AI_Cart_Recovery_Line_Item ) {
				continue;
			}

			$name       = self::escape_html( $item->name );
			$product_id = $item->variation_id > 0 ? $item->variation_id : $item->product_id;
			$url        = self::safe_url( $item->permalink );
			$image      = self::safe_url( $item->image_url );
			$line_total = self::format_money( $item->line_total, $currency );
			$qty        = max( 1, (int) $item->qty );

			$title = '' !== $url
				? '<a href="' . self::escape_attr( $url ) . '" style="color:#1f2937;text-decoration:none;font-weight:700;">' . $name . '</a>'
				: '<span style="color:#1f2937;font-weight:700;">' . $name . '</span>';

			$image_html = '';
			if ( '' !== $image ) {
				$img = '<img src="' . self::escape_attr( $image ) . '" width="72" height="72" alt="' . self::escape_attr( $item->name ) . '" style="display:block;width:72px;height:72px;object-fit:cover;border-radius:8px;border:0;background:#f3f4f6;">';
				$image_html = '' !== $url ? '<a href="' . self::escape_attr( $url ) . '">' . $img . '</a>' : $img;
			} else {
				$image_html = '<div style="width:72px;height:72px;border-radius:8px;background:#f3f4f6;color:#9ca3af;font-size:11px;line-height:72px;text-align:center;">#' . (int) $product_id . '</div>';
			}

			$html .= '<tr><td style="border:1px solid #e5e7eb;border-radius:10px;padding:12px;background:#ffffff;">'
				. '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0"><tr>'
				. '<td width="84" valign="top" style="width:84px;padding-right:12px;">' . $image_html . '</td>'
				. '<td valign="top" style="font-size:14px;line-height:1.45;color:#4b5563;">'
				. '<div style="margin:0 0 6px;">' . $title . '</div>'
				. '<div style="font-size:13px;color:#6b7280;">' . self::escape_html( $qty_label ) . ': ' . $qty . '</div>'
				. '</td>'
				. '<td valign="top" align="right" style="font-size:14px;font-weight:700;color:#111827;white-space:nowrap;padding-left:12px;">' . self::escape_html( $line_total ) . '</td>'
				. '</tr></table>'
				. '</td></tr>';
		}
		$html .= '</table>';

		return $html;
	}

	private static function quantity_label( string $locale ): string {
		$locale = strtolower( trim( str_replace( '-', '_', $locale ) ) );
		$parts  = preg_split( '/_+/', $locale );
		$lang   = isset( $parts[0] ) ? preg_replace( '/[^a-z]/', '', (string) $parts[0] ) : '';
		switch ( $lang ) {
			case 'sr':
			case 'bs':
				return 'Količina';
			case 'hr':
				return 'Količina';
			case 'de':
				return 'Menge';
			case 'es':
				return 'Cantidad';
			case 'fr':
				return 'Quantité';
			default:
				return 'Qty';
		}
	}

	private static function format_money( float $amount, string $currency ): string {
		$formatted = number_format( $amount, 2, '.', '' );
		return '' !== $currency ? $formatted . ' ' . $currency : $formatted;
	}

	private static function escape_html( string $s ): string {
		if ( function_exists( 'esc_html' ) ) {
			return esc_html( $s );
		}
		return htmlspecialchars( $s, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
	}

	private static function escape_attr( string $s ): string {
		if ( function_exists( 'esc_attr' ) ) {
			return esc_attr( $s );
		}
		return htmlspecialchars( $s, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
	}

	private static function safe_url( string $url ): string {
		$url = trim( $url );
		if ( '' === $url || ! preg_match( '#^https?://#i', $url ) ) {
			return '';
		}
		if ( function_exists( 'esc_url' ) ) {
			return esc_url( $url );
		}
		return str_replace( array( '"', "'", '<', '>' ), '', $url );
	}
}
