<?php
/**
 * Sends form submission emails with merge tag replacement.
 *
 * @package ThemeREX\AutoDealer
 */

declare(strict_types=1);

namespace ThemeREX\AutoDealer\Forms;

use function ThemeREX\AutoDealer\getVehicleSpecs;
use function ThemeREX\AutoDealer\formatPrice;

class FormMailer {

	/**
	 * Send the form submission email to the dealer/admin, plus an
	 * optional auto-reply to the customer.
	 *
	 * @param array  $formConfig Form config from FormConfig.
	 * @param array  $fieldValues Submitted field values (id => value).
	 * @param int    $postId     Listing post ID.
	 * @param string $formId     Form ID (inquiry, test_drive, etc).
	 * @return bool True when the *admin* email was sent (auto-reply success
	 *              is best-effort; failure to send the auto-reply doesn't
	 *              break the submission).
	 */
	public static function send( array $formConfig, array $fieldValues, int $postId, string $formId = '' ): bool {
		$to      = $formConfig['email_to'] ?? '';
		$subject = $formConfig['email_subject'] ?? '';
		$body    = $formConfig['email_body'] ?? '';

		if ( '' === $to ) {
			$to = get_option( 'admin_email' );
		}

		// Per-form override → dealer email → admin_email — captures the
		// "send to dealer" cascade without requiring config edits per form.
		// Reads dealer_id from the specs row first (canonical per session 9
		// fix — post meta can drift to 0 when seed scripts trash the dealer
		// then the listing isn't re-linked).
		if ( '' === ( $formConfig['email_to'] ?? '' ) && $postId > 0 ) {
			$dealerId = self::resolveDealerId( $postId );
			if ( $dealerId > 0 ) {
				$dealerEmail = (string) get_post_meta( $dealerId, '_trx_ad_email', true );
				if ( '' !== $dealerEmail && is_email( $dealerEmail ) ) {
					$to = $dealerEmail;
				}
			}
		}

		$tags    = self::buildMergeTags( $fieldValues, $postId, $formId, $formConfig );
		$subject = self::replaceTags( $subject, $tags );
		$body    = self::replaceTags( $body, $tags );

		$headers = array(
			'Content-Type: text/plain; charset=UTF-8',
		);

		// Add reply-to if email field exists.
		$replyEmail = $fieldValues['email'] ?? '';
		$replyName  = $fieldValues['name'] ?? '';
		if ( '' !== $replyEmail && is_email( $replyEmail ) ) {
			$replyHeader = '' !== $replyName
				? sprintf( 'Reply-To: %s <%s>', $replyName, $replyEmail )
				: sprintf( 'Reply-To: %s', $replyEmail );
			$headers[]   = $replyHeader;
		}

		$sent = wp_mail( $to, $subject, $body, $headers );

		// Best-effort auto-reply. Skipped silently if disabled, customer
		// email missing, or invalid. Auto-reply failure does not fail the
		// submission — the customer's primary feedback is the success
		// message on the form, not the auto-reply.
		if ( ! empty( $formConfig['autoreply'] ) && '' !== $replyEmail && is_email( $replyEmail ) ) {
			$arSubject = (string) ( $formConfig['autoreply_subject'] ?? '' );
			$arBody    = (string) ( $formConfig['autoreply_body'] ?? '' );
			if ( '' !== $arSubject && '' !== $arBody ) {
				$arSubject = self::replaceTags( $arSubject, $tags );
				$arBody    = self::replaceTags( $arBody, $tags );
				wp_mail( $replyEmail, $arSubject, $arBody, array( 'Content-Type: text/plain; charset=UTF-8' ) );
			}
		}

		return $sent;
	}

	/**
	 * Build merge tag replacements.
	 *
	 * @param array  $fieldValues Field values.
	 * @param int    $postId      Listing post ID.
	 * @param string $formId      Form ID (inquiry, test_drive, etc).
	 * @param array  $formConfig  Form config (for {form_title}).
	 * @return array<string, string> Tag => replacement.
	 */
	private static function buildMergeTags( array $fieldValues, int $postId, string $formId = '', array $formConfig = array() ): array {
		$tags = array(
			'{site_name}'      => (string) get_bloginfo( 'name' ),
			'{customer_name}'  => (string) ( $fieldValues['name'] ?? '' ),
			'{customer_email}' => (string) ( $fieldValues['email'] ?? '' ),
			'{form_type}'      => $formId,
			'{form_title}'     => (string) ( $formConfig['title'] ?? $formId ),
		);

		// Field tags.
		foreach ( $fieldValues as $id => $value ) {
			$tags[ '{field:' . $id . '}' ] = (string) $value;
		}

		// Vehicle tags.
		if ( $postId > 0 ) {
			$specs = getVehicleSpecs( $postId );
			$specs = is_array( $specs ) ? $specs : array();

			$year = (string) ( $specs['year'] ?? '' );
			// Prefer taxonomy term names (proper case "Ford"/"F-150") over
			// the lowercase slugs stored in the specs row. Fixes lowercase
			// "{vehicle_title}" in inquiry email subjects.
			$make  = self::pickTermName( $postId, 'trx_ad_make', (string) ( $specs['make'] ?? '' ) );
			$model = self::pickTermName( $postId, 'trx_ad_model', (string) ( $specs['model'] ?? '' ) );

			// Require ≥2 non-empty parts before trusting the joined string —
			// "{year}  " from a year-only listing was the session-24 card-
			// title bug that left "2015" alone in the email subject.
			$parts = array_values(
				array_filter(
					array( $year, $make, $model ),
					static fn( $p ) => '' !== trim( $p )
				)
			);
			$title = count( $parts ) >= 2 ? implode( ' ', $parts ) : get_the_title( $postId );

			$price = isset( $specs['price'] ) ? formatPrice( (float) $specs['price'], false ) : '';

			$tags['{vehicle_title}'] = $title;
			$tags['{vehicle_url}']   = get_permalink( $postId ) ?: '';
			$tags['{vehicle_price}'] = $price;
			$tags['{vehicle_stock}'] = (string) ( $specs['stock_number'] ?? '' );

			// Dealer info — read from specs row first (canonical), fall back
			// to post meta. Empty {dealer_name} in customer auto-replies was
			// the session-25 finding when post meta was 0 but specs.dealer_id
			// pointed at a real dealer.
			$dealerId = self::resolveDealerId( $postId );
			if ( $dealerId > 0 ) {
				$tags['{dealer_name}']  = get_the_title( $dealerId );
				$tags['{dealer_phone}'] = (string) get_post_meta( $dealerId, '_trx_ad_phone', true );
				$tags['{dealer_email}'] = (string) get_post_meta( $dealerId, '_trx_ad_email', true );
			} else {
				$tags['{dealer_name}']  = '';
				$tags['{dealer_phone}'] = '';
				$tags['{dealer_email}'] = '';
			}
		}

		return $tags;
	}

	/**
	 * Resolve a listing's dealer ID. Specs row is canonical (per session 9
	 * fix — post meta drifts when seed scripts trash dealers); fall back to
	 * post meta only when specs is empty.
	 */
	private static function resolveDealerId( int $postId ): int {
		$specs = getVehicleSpecs( $postId );
		if ( is_array( $specs ) && ! empty( $specs['dealer_id'] ) ) {
			return (int) $specs['dealer_id'];
		}
		return (int) get_post_meta( $postId, '_trx_ad_dealer_id', true );
	}

	/**
	 * Pick a taxonomy term name for the listing, falling back to the slug
	 * from the specs row when no terms are assigned. Returns proper-case
	 * "Ford" / "F-150" instead of lowercase "ford" / "f-150".
	 */
	private static function pickTermName( int $postId, string $taxonomy, string $specSlugFallback ): string {
		$names = wp_get_object_terms( $postId, $taxonomy, array( 'fields' => 'names' ) );
		if ( ! is_wp_error( $names ) && ! empty( $names ) ) {
			return (string) $names[0];
		}
		return $specSlugFallback;
	}

	/**
	 * Replace merge tags in a string.
	 *
	 * @param string $text Text with tags.
	 * @param array  $tags Tag => replacement.
	 * @return string
	 */
	private static function replaceTags( string $text, array $tags ): string {
		return str_replace( array_keys( $tags ), array_values( $tags ), $text );
	}
}
