<?php
/**
 * Variables — resolves {{placeholder}} tokens in email/SMS templates.
 *
 * Supported tokens:
 *
 * Customer:
 *   {{customer.first_name}}, {{customer.last_name}}, {{customer.email}},
 *   {{customer.full_name}}, {{customer.username}}
 *
 * Order:
 *   {{order.number}}, {{order.total}}, {{order.subtotal}}, {{order.status}},
 *   {{order.date}}, {{order.items}}, {{order.items_plain}}, {{order.edit_url}},
 *   {{order.payment_method}}, {{order.payment_url}}, {{order.admin_url}},
 *   {{order.tracking}}, {{order.tracking_url}}, {{order.tracking_link}},
 *   {{order.cross_sells}}, {{order.related_products}}
 *
 * Shop:
 *   {{shop.name}}, {{shop.url}}, {{shop.admin_email}},
 *   {{shop.unsubscribe_url}}
 *
 * Cart (abandoned / checkout started):
 *   {{cart.total}}, {{cart.items}}, {{cart.items_plain}}, {{cart.recover_url}}
 *
 * Misc:
 *   {{coupon.code}}
 *
 * @package CartCue
 */

namespace CartCue;

defined( 'ABSPATH' ) || exit;

class Variables {

	/**
	 * Replace all known tokens in the given string.
	 *
	 * @param string $template
	 * @param array  $data  Context array (may contain 'order_id', 'customer_email', etc.)
	 * @return string
	 */
	public function process( string $template, array $data ): string {
		if ( empty( $template ) ) {
			return $template;
		}

		$map = $this->build_map( $data );

		$template = preg_replace_callback(
			'/\{\{([^|}]+)\|([^}]+)\}\}/',
			static function ( array $match ) use ( $map ): string {
				$token    = '{{' . trim( $match[1] ) . '}}';
				$resolved = isset( $map[ $token ] ) ? (string) $map[ $token ] : '';
				return '' !== $resolved ? $resolved : trim( (string) $match[2] );
			},
			$template
		);

		return str_replace( array_keys( $map ), array_values( $map ), $template );
	}

	// -------------------------------------------------------------------------
	// Build the full replacement map
	// -------------------------------------------------------------------------

	private function build_map( array $data ): array {
		$map = [];

		// Shop variables (always available).
		$map['{{shop.name}}']        = get_bloginfo( 'name' );
		$map['{{shop.url}}']         = get_home_url();
		$map['{{shop.admin_email}}'] = get_option( 'admin_email' );
		$map['{{shop.logo_url}}']    = self::get_store_logo_url();

		// Order variables.
		$order_id = (int) ( $data['order_id'] ?? 0 );
		if ( $order_id ) {
			$order = wc_get_order( $order_id );
			if ( $order ) {
				$map = array_merge( $map, $this->order_vars( $order ) );
				$map = array_merge( $map, $this->customer_vars_from_order( $order ) );
			}
		}

		// Customer variables from raw data (used when no order is available,
		// e.g. cart abandoned, win-back).
		if ( ! isset( $map['{{customer.email}}'] ) ) {
			$map = array_merge( $map, $this->customer_vars_from_data( $data ) );
		}

		$cart_id = (int) ( $data['cart_id'] ?? 0 );
		if ( $cart_id ) {
			$map = array_merge( $map, $this->cart_vars( $cart_id, $data ) );
		}

		// Coupon variables (populated by GenerateCouponAction when it runs first).
		$map['{{coupon.code}}']   = $data['coupon_code']   ?? '';
		$map['{{coupon.expiry}}'] = $data['coupon_expiry'] ?? '';
		$map['{{coupon.amount}}'] = isset( $data['coupon_amount'] ) ? (string) $data['coupon_amount'] : '';
		$map['{{coupon.type}}']   = $data['coupon_type']   ?? '';

		// Review variables.
		$map['{{review.rating}}']   = isset( $data['review_rating'] ) ? str_repeat( '★', (int) $data['review_rating'] ) : '';
		$map['{{review.content}}']  = esc_html( $data['review_content']  ?? '' );
		$map['{{review.product}}']  = esc_html( $data['product_name']    ?? '' );
		$map['{{review.reviewer}}'] = esc_html( $data['reviewer_name']   ?? '' );

		// Product / stock alert (no order).
		if ( ! empty( $data['product_name'] ) || ! empty( $data['product_id'] ) ) {
			$map['{{product.id}}']        = isset( $data['product_id'] ) ? (string) (int) $data['product_id'] : '';
			$map['{{product.name}}']      = esc_html( (string) ( $data['product_name'] ?? '' ) );
			$map['{{product.stock}}']     = isset( $data['stock_quantity'] ) ? (string) (int) $data['stock_quantity'] : '';
			$map['{{product.threshold}}'] = isset( $data['stock_threshold'] ) ? (string) (int) $data['stock_threshold'] : '';
		}

		// Subscription variables.
		$map['{{subscription.next_payment}}'] = esc_html( $data['next_payment_date'] ?? '' );
		$map['{{subscription.status}}']       = esc_html( $data['status']            ?? '' );

		$unsub_email = '';
		if ( isset( $map['{{customer.email}}'] ) && is_email( (string) $map['{{customer.email}}'] ) ) {
			$unsub_email = (string) $map['{{customer.email}}'];
		} elseif ( ! empty( $data['customer_email'] ) && is_email( (string) $data['customer_email'] ) ) {
			$unsub_email = sanitize_email( (string) $data['customer_email'] );
		}

		$map['{{shop.unsubscribe_url}}'] = $unsub_email
			? apply_filters( 'cartcue_unsubscribe_url', Unsubscribe::signed_url( $unsub_email ), $data )
			: apply_filters( 'cartcue_unsubscribe_url', home_url( '/' ), $data );

		return apply_filters( 'cartcue_variable_map', $map, $data );
	}

	// -------------------------------------------------------------------------
	// Order variables
	// -------------------------------------------------------------------------

	private function order_vars( \WC_Order $order ): array {
		return [
			'{{order.number}}'         => $order->get_order_number(),
			'{{order.total}}'          => $order->get_formatted_order_total(),
			'{{order.subtotal}}'       => wc_price( $order->get_subtotal() ),
			'{{order.status}}'         => wc_get_order_status_name( $order->get_status() ),
			'{{order.date}}'           => wc_format_datetime( $order->get_date_created() ),
			'{{order.items}}'          => $this->order_items_html( $order ),
			'{{order.items_plain}}'    => $this->order_items_plain( $order ),
			'{{order.edit_url}}'       => $order->get_view_order_url(),
			'{{order.payment_method}}' => $order->get_payment_method_title(),
			'{{order.payment_url}}'    => $order->get_checkout_payment_url(),
			'{{order.admin_url}}'     => get_edit_post_link( $order->get_id(), '' ) ?: admin_url( 'post.php?post=' . $order->get_id() . '&action=edit' ),
			'{{order.billing_name}}'   => trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ),
			'{{order.billing_address}}' => $this->format_address( $order, 'billing' ),
			'{{order.shipping_name}}'  => trim( $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name() ),
			'{{order.shipping_address}}' => $this->format_address( $order, 'shipping' ),
			'{{order.billing_phone}}'  => esc_html( $order->get_billing_phone() ),
			'{{order.tracking}}'       => $this->order_tracking_number( $order ),
			'{{order.tracking_url}}'   => $this->order_tracking_url( $order ),
			'{{order.tracking_link}}'  => $this->order_tracking_link_html( $order ),
			'{{order.cross_sells}}'    => $this->order_product_recommendations_html( $order, 'cross_sell' ),
			'{{order.related_products}}' => $this->order_product_recommendations_html( $order, 'related' ),
		];
	}

	private function format_address( \WC_Order $order, string $type ): string {
		$fields = [ 'address_1', 'address_2', 'city', 'state', 'postcode', 'country' ];
		$parts  = [];
		foreach ( $fields as $field ) {
			$method = "get_{$type}_{$field}";
			$value  = method_exists( $order, $method ) ? $order->$method() : '';
			if ( $value ) {
				$parts[] = esc_html( $value );
			}
		}
		return implode( ', ', $parts );
	}

	public static function get_store_logo_url(): string {
		$logo_id = (int) get_theme_mod( 'custom_logo', 0 );
		if ( $logo_id ) {
			$src = wp_get_attachment_image_url( $logo_id, 'medium' );
			if ( $src ) {
				return esc_url( $src );
			}
		}

		$cartcue_logo = get_option( 'cartcue_email_logo_url', '' );
		if ( ! empty( $cartcue_logo ) ) {
			return esc_url( $cartcue_logo );
		}

		$icon = get_site_icon_url( 200 );
		return $icon ? esc_url( $icon ) : '';
	}

	/**
	 * Resolve shipment tracking from common WooCommerce meta keys and plugins.
	 *
	 * @return array{number: string, url: string}
	 */
	private function order_tracking_data( \WC_Order $order ): array {
		$number = '';
		$url    = '';

		$items = $order->get_meta( '_wc_shipment_tracking_items', true );
		if ( is_array( $items ) && ! empty( $items ) ) {
			$first  = reset( $items );
			$number = (string) ( $first['tracking_number'] ?? '' );
			$url    = (string) ( $first['tracking_link'] ?? $first['custom_tracking_link'] ?? '' );
		}

		if ( ! $number ) {
			foreach ( [ '_tracking_number', 'tracking_number', '_wc_shipment_tracking_number' ] as $meta_key ) {
				$number = (string) $order->get_meta( $meta_key, true );
				if ( $number ) {
					break;
				}
			}
		}

		if ( ! $url ) {
			foreach ( [ '_tracking_url', 'tracking_url', '_wc_shipment_tracking_url' ] as $meta_key ) {
				$url = (string) $order->get_meta( $meta_key, true );
				if ( $url ) {
					break;
				}
			}
		}

		$number = sanitize_text_field( $number );
		$url    = esc_url_raw( $url );

		return apply_filters( 'cartcue_order_tracking_data', [ 'number' => $number, 'url' => $url ], $order );
	}

	private function order_tracking_number( \WC_Order $order ): string {
		return $this->order_tracking_data( $order )['number'];
	}

	private function order_tracking_url( \WC_Order $order ): string {
		return $this->order_tracking_data( $order )['url'];
	}

	private function order_tracking_link_html( \WC_Order $order ): string {
		$tracking = $this->order_tracking_data( $order );
		if ( ! $tracking['number'] && ! $tracking['url'] ) {
			return '';
		}
		if ( $tracking['url'] ) {
			$label = $tracking['number'] ?: __( 'Track your shipment', 'cartcue-sales-automation' );
			return sprintf(
				'<a href="%s" style="color:inherit">%s</a>',
				esc_url( $tracking['url'] ),
				esc_html( $label )
			);
		}
		return esc_html( $tracking['number'] );
	}

	/**
	 * @param 'cross_sell'|'related' $type
	 */
	private function order_product_recommendations_html( \WC_Order $order, string $type ): string {
		$product_ids = 'cross_sell' === $type
			? $this->order_cross_sell_ids( $order )
			: $this->order_related_product_ids( $order );

		$product_ids = array_slice( array_values( array_unique( array_filter( $product_ids ) ) ), 0, 4 );
		if ( empty( $product_ids ) ) {
			return '';
		}

		$html = '<ul style="padding:0;margin:0;list-style:none;">';
		foreach ( $product_ids as $product_id ) {
			$product = wc_get_product( $product_id );
			if ( ! $product || ! $product->is_purchasable() ) {
				continue;
			}
			$html .= sprintf(
				'<li style="margin-bottom:12px"><a href="%s" style="color:inherit;font-weight:500">%s</a> — %s</li>',
				esc_url( $product->get_permalink() ),
				esc_html( $product->get_name() ),
				wp_kses_post( $product->get_price_html() )
			);
		}
		$html .= '</ul>';

		return $html;
	}

	/** @return int[] */
	private function order_cross_sell_ids( \WC_Order $order ): array {
		$ids = [];
		foreach ( $order->get_items() as $item ) {
			$product = $item->get_product();
			if ( $product ) {
				$ids = array_merge( $ids, $product->get_cross_sell_ids() );
			}
		}
		return apply_filters( 'cartcue_order_cross_sell_ids', $ids, $order );
	}

	/** @return int[] */
	private function order_related_product_ids( \WC_Order $order ): array {
		$from_order = [];
		foreach ( $order->get_items() as $item ) {
			$product = $item->get_product();
			if ( $product ) {
				$from_order[] = $product->get_id();
			}
		}
		if ( empty( $from_order ) ) {
			return [];
		}
		$related = wc_get_related_products( reset( $from_order ), 4, $from_order );
		return apply_filters( 'cartcue_order_related_product_ids', is_array( $related ) ? $related : [], $order );
	}

	private function order_items_html( \WC_Order $order ): string {
		$html = '<ul style="padding:0;margin:0;list-style:none;">';
		foreach ( $order->get_items() as $item ) {
			/** @var \WC_Order_Item_Product $item */
			$html .= sprintf(
				'<li>%s &times; %s — %s</li>',
				esc_html( $item->get_quantity() ),
				esc_html( $item->get_name() ),
				wc_price( $item->get_total() )
			);
		}
		$html .= '</ul>';
		return $html;
	}

	private function order_items_plain( \WC_Order $order ): string {
		$lines = [];
		foreach ( $order->get_items() as $item ) {
			$lines[] = sprintf( '%dx %s', $item->get_quantity(), esc_html( $item->get_name() ) );
		}
		return implode( "\n", $lines );
	}

	// -------------------------------------------------------------------------
	// Customer variables
	// -------------------------------------------------------------------------

	private function customer_vars_from_order( \WC_Order $order ): array {
		$user_id  = (int) $order->get_customer_id();
		$username = '';
		if ( $user_id ) {
			$user = get_user_by( 'id', $user_id );
			$username = $user ? $user->user_login : '';
		}

		return [
			'{{customer.first_name}}' => $order->get_billing_first_name(),
			'{{customer.last_name}}'  => $order->get_billing_last_name(),
			'{{customer.full_name}}'  => trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ),
			'{{customer.email}}'      => $order->get_billing_email(),
			'{{customer.username}}'   => $username,
		];
	}

	private function customer_vars_from_data( array $data ): array {
		$user_id    = (int) ( $data['customer_id'] ?? 0 );
		$user       = $user_id ? get_user_by( 'id', $user_id ) : null;
		$first_name = $user ? $user->first_name : ( $data['first_name'] ?? '' );
		$last_name  = $user ? $user->last_name  : ( $data['last_name']  ?? '' );
		$email      = $user ? $user->user_email : ( $data['customer_email'] ?? '' );

		if ( ( ! $first_name && ! $last_name ) && is_email( (string) $email ) ) {
			$order = $this->latest_order_for_email( (string) $email );
			if ( $order ) {
				$first_name = $first_name ?: $order->get_billing_first_name();
				$last_name  = $last_name  ?: $order->get_billing_last_name();
			}
		}

		return [
			'{{customer.first_name}}' => $first_name,
			'{{customer.last_name}}'  => $last_name,
			'{{customer.full_name}}'  => trim( $first_name . ' ' . $last_name ),
			'{{customer.email}}'      => $email,
			'{{customer.username}}'   => $user ? $user->user_login : '',
		];
	}

	// -------------------------------------------------------------------------
	// Cart variables (abandoned / checkout started)
	// -------------------------------------------------------------------------

	private function cart_vars( int $cart_id, array $data ): array {
		$row = CartRecovery::get_cart_row( $cart_id );
		if ( ! $row ) {
			$total = (string) ( $data['cart_total'] ?? '' );
			return [
				'{{cart.total}}'       => $total,
				'{{cart.items}}'       => '',
				'{{cart.items_plain}}' => '',
				'{{cart.recover_url}}' => CartRecovery::signed_url( $cart_id ),
			];
		}

		$items = json_decode( $row['cart_contents'] ?? '[]', true );
		if ( ! is_array( $items ) ) {
			$items = [];
		}

		return [
			'{{cart.total}}'       => $row['total'] ?: ( $data['cart_total'] ?? '' ),
			'{{cart.items}}'       => $this->cart_items_html( $items ),
			'{{cart.items_plain}}' => $this->cart_items_plain( $items ),
			'{{cart.recover_url}}' => CartRecovery::signed_url( $cart_id ),
		];
	}

	private function cart_items_html( array $items ): string {
		if ( empty( $items ) ) {
			return '';
		}

		$html = '<ul style="padding:0;margin:0;list-style:none;">';
		foreach ( $items as $item ) {
			$product_id   = (int) ( $item['product_id'] ?? 0 );
			$variation_id = (int) ( $item['variation_id'] ?? 0 );
			$qty          = max( 1, (int) ( $item['quantity'] ?? 1 ) );
			$product      = wc_get_product( $variation_id ?: $product_id );
			if ( ! $product ) {
				continue;
			}
			$html .= sprintf(
				'<li style="margin-bottom:12px">%s &times; <a href="%s" style="color:inherit">%s</a> — %s</li>',
				esc_html( (string) $qty ),
				esc_url( $product->get_permalink() ),
				esc_html( $product->get_name() ),
				wp_kses_post( $product->get_price_html() )
			);
		}
		$html .= '</ul>';
		return $html;
	}

	private function cart_items_plain( array $items ): string {
		$lines = [];
		foreach ( $items as $item ) {
			$product_id   = (int) ( $item['product_id'] ?? 0 );
			$variation_id = (int) ( $item['variation_id'] ?? 0 );
			$qty          = max( 1, (int) ( $item['quantity'] ?? 1 ) );
			$product      = wc_get_product( $variation_id ?: $product_id );
			if ( $product ) {
				$lines[] = sprintf( '%dx %s', $qty, $product->get_name() );
			}
		}
		return implode( "\n", $lines );
	}

	private function latest_order_for_email( string $email ): ?\WC_Order {
		if ( ! function_exists( 'wc_get_orders' ) || ! is_email( $email ) ) {
			return null;
		}

		$orders = wc_get_orders(
			[
				'limit'         => 1,
				'billing_email' => $email,
				'orderby'       => 'date',
				'order'         => 'DESC',
				'return'        => 'objects',
				'status'        => array_keys( wc_get_order_statuses() ),
			]
		);

		$order = $orders[0] ?? null;
		return $order instanceof \WC_Order ? $order : null;
	}
}
