<?php
/**
 * Customer emails for table reservations.
 *
 * @package FoodOrd
 */

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

/**
 * Class Foodord_Table_Reservation_Emails
 */
class Foodord_Table_Reservation_Emails {
	private const OPTION_CUSTOMER_CONTENT = 'foodord_table_reservation_email_content';

	/**
	 * Get customer reservation email content overrides.
	 *
	 * @return array{pending:array{subject:string,heading:string,message:string},confirmed:array{subject:string,heading:string,message:string},updated:array{subject:string,heading:string,message:string},cancelled:array{subject:string,heading:string,message:string},footer:string}
	 */
	public static function get_customer_content_settings(): array {
		$stored   = get_option( self::OPTION_CUSTOMER_CONTENT, array() );
		$stored   = is_array( $stored ) ? $stored : array();
		$settings = array();

		foreach ( array( 'pending', 'confirmed', 'updated', 'cancelled' ) as $status ) {
			$template            = isset( $stored[ $status ] ) && is_array( $stored[ $status ] ) ? $stored[ $status ] : array();
			$settings[ $status ] = array(
				'subject' => sanitize_text_field( (string) ( $template['subject'] ?? '' ) ),
				'heading' => sanitize_text_field( (string) ( $template['heading'] ?? '' ) ),
				'message' => sanitize_textarea_field( (string) ( $template['message'] ?? '' ) ),
			);
		}

		$settings['footer'] = sanitize_textarea_field( (string) ( $stored['footer'] ?? '' ) );

		return $settings;
	}

	/**
	 * Save customer reservation email content overrides.
	 *
	 * @param array<string,mixed> $settings Raw settings.
	 */
	public static function save_customer_content_settings( array $settings ): void {
		$sanitized = array();

		foreach ( array( 'pending', 'confirmed', 'updated', 'cancelled' ) as $status ) {
			$template             = isset( $settings[ $status ] ) && is_array( $settings[ $status ] ) ? $settings[ $status ] : array();
			$sanitized[ $status ] = array(
				'subject' => sanitize_text_field( (string) ( $template['subject'] ?? '' ) ),
				'heading' => sanitize_text_field( (string) ( $template['heading'] ?? '' ) ),
				'message' => sanitize_textarea_field( (string) ( $template['message'] ?? '' ) ),
			);
		}

		$sanitized['footer'] = sanitize_textarea_field( (string) ( $settings['footer'] ?? '' ) );

		update_option( self::OPTION_CUSTOMER_CONTENT, $sanitized, false );
	}

	/**
	 * Send the initial pending reservation email.
	 *
	 * @param object $reservation Reservation row.
	 */
	public function send_pending_email( $reservation ): bool {
		return $this->send_status_email( $reservation, 'pending' );
	}

	/**
	 * Send the confirmed reservation email.
	 *
	 * @param object $reservation Reservation row.
	 */
	public function send_confirmed_email( $reservation ): bool {
		return $this->send_status_email( $reservation, 'confirmed' );
	}

	/**
	 * Send a customer reservation details updated email.
	 *
	 * @param object $reservation Reservation row.
	 */
	public function send_updated_email( $reservation ): bool {
		return $this->send_status_email( $reservation, 'updated' );
	}

	/**
	 * Send the cancelled reservation email.
	 *
	 * @param object $reservation Reservation row.
	 */
	public function send_cancelled_email( $reservation ): bool {
		return $this->send_status_email( $reservation, 'cancelled' );
	}

	/**
	 * Send a new reservation notification to the site admin.
	 *
	 * @param object $reservation Reservation row.
	 */
	public function send_admin_notification_email( $reservation ): bool {
		$email = $this->get_admin_email();
		if ( '' === $email ) {
			return false;
		}

		return $this->send_email(
			$email,
			__( 'New reservation received', 'foodord' ),
			$this->get_admin_email_body( $reservation )
		);
	}

	/**
	 * Send an updated reservation notification to the site admin.
	 *
	 * @param object            $reservation   Reservation row.
	 * @param array<int,string> $changed_fields Changed reservation fields.
	 */
	public function send_admin_updated_email( $reservation, array $changed_fields ): bool {
		$email = $this->get_admin_email();
		if ( '' === $email ) {
			return false;
		}

		return $this->send_email(
			$email,
			__( 'Reservation details updated', 'foodord' ),
			$this->get_admin_updated_email_body( $reservation, $changed_fields )
		);
	}

	/**
	 * Notify the previous customer email address after an address change.
	 *
	 * @param object $reservation Reservation row.
	 * @param string $old_email   Previous customer email address.
	 */
	public function send_previous_email_change_notice( $reservation, string $old_email ): bool {
		$old_email = sanitize_email( $old_email );
		if ( '' === $old_email || ! is_email( $old_email ) ) {
			return false;
		}

		return $this->send_email(
			$old_email,
			__( 'Reservation email address changed', 'foodord' ),
			$this->get_previous_email_change_notice_body( $reservation )
		);
	}

	/**
	 * Send a cancelled reservation notification to the site admin.
	 *
	 * @param object $reservation Reservation row.
	 */
	public function send_admin_cancelled_email( $reservation ): bool {
		$email = $this->get_admin_email();
		if ( '' === $email ) {
			return false;
		}

		return $this->send_email(
			$email,
			__( 'Reservation cancelled', 'foodord' ),
			$this->get_admin_cancelled_email_body( $reservation )
		);
	}

	/**
	 * Send a reservation status email.
	 *
	 * @param object $reservation Reservation row.
	 * @param string $status      Reservation status.
	 */
	private function send_status_email( $reservation, string $status ): bool {
		$email = $this->get_customer_email( $reservation );
		if ( '' === $email ) {
			return false;
		}

		$calendar_attachment = '';

		try {
			$calendar_attachment = Foodord_Table_Reservation_Calendar::create_email_attachment( $reservation, $status );
		} catch ( Throwable $calendar_error ) {
			$calendar_attachment = '';
		}

		$attachments = '' !== $calendar_attachment ? array( $calendar_attachment ) : array();
		$subject     = $this->get_subject( $reservation, $status );
		$message     = $this->get_email_body( $reservation, $status );

		try {
			return $this->send_email( $email, $subject, $message, $attachments );
		} finally {
			Foodord_Table_Reservation_Calendar::delete_email_attachment( $calendar_attachment );
		}
	}

	/**
	 * Send an HTML email.
	 *
	 * @param string            $email       Recipient email address.
	 * @param string            $subject     Email subject.
	 * @param string            $message     HTML body.
	 * @param array<int,string> $attachments Attachment file paths.
	 */
	private function send_email( string $email, string $subject, string $message, array $attachments = array() ): bool {
		$headers = array( 'Content-Type: text/html; charset=UTF-8' );
		if ( function_exists( 'WC' ) && WC() && WC()->mailer() ) {
			return (bool) WC()->mailer()->send( $email, $subject, $message, $headers, $attachments );
		}

		return (bool) wp_mail( $email, $subject, $message, $headers, $attachments );
	}

	/**
	 * Get the customer email address from a reservation row.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_customer_email( $reservation ): string {
		$email = isset( $reservation->customer_email ) ? sanitize_email( (string) $reservation->customer_email ) : '';

		return is_email( $email ) ? $email : '';
	}

	/**
	 * Get the site admin email address.
	 */
	private function get_admin_email(): string {
		$email = sanitize_email( (string) get_option( 'admin_email', '' ) );

		return is_email( $email ) ? $email : '';
	}

	/**
	 * Get the email subject for a reservation status.
	 *
	 * @param object $reservation Reservation row.
	 * @param string $status      Reservation status.
	 */
	private function get_subject( $reservation, string $status ): string {
		$status_data = $this->get_status_email_data( $reservation, $status );

		return $status_data['subject'];
	}

	/**
	 * Build a modern HTML email body.
	 *
	 * @param object $reservation Reservation row.
	 * @param string $status      Reservation status.
	 */
	private function get_email_body( $reservation, string $status ): string {
		$status_data        = $this->get_status_email_data( $reservation, $status );
		$reservation_status = isset( $reservation->status ) ? Foodord_Table_Reservations::sanitize_status( (string) $reservation->status ) : 'pending';
		$statuses           = Foodord_Table_Reservations::get_statuses();
		$status_label       = $statuses[ $reservation_status ] ?? __( 'Pending', 'foodord' );

		return $this->render_email_body(
			$status_data,
			$this->get_detail_rows( $reservation, $reservation_status, $status_label ),
			$this->get_customer_footer_text( $reservation ),
			$this->get_customer_management_action( $reservation, $reservation_status ),
			$this->get_preorder_summary( $reservation )
		);
	}

	/**
	 * Build the admin notification HTML email body.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_admin_email_body( $reservation ): string {
		$status_data = $this->get_admin_email_data( $reservation );

		return $this->render_email_body(
			$status_data,
			$this->get_admin_detail_rows( $reservation, $status_data['label'] ),
			'',
			null,
			$this->get_preorder_summary( $reservation )
		);
	}

	/**
	 * Build the admin cancellation HTML email body.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_admin_cancelled_email_body( $reservation ): string {
		$status_data = $this->get_admin_cancelled_email_data();

		return $this->render_email_body(
			$status_data,
			$this->get_admin_detail_rows( $reservation, $status_data['label'] )
		);
	}

	/**
	 * Build the admin updated reservation email body.
	 *
	 * @param object            $reservation   Reservation row.
	 * @param array<int,string> $changed_fields Changed reservation fields.
	 */
	private function get_admin_updated_email_body( $reservation, array $changed_fields ): string {
		$status_data        = $this->get_admin_updated_email_data();
		$statuses           = Foodord_Table_Reservations::get_statuses();
		$reservation_status = isset( $reservation->status ) ? Foodord_Table_Reservations::sanitize_status( (string) $reservation->status ) : 'pending';
		$status_label       = $statuses[ $reservation_status ] ?? __( 'Pending', 'foodord' );
		$detail_rows        = $this->get_admin_detail_rows( $reservation, $status_label );
		$field_labels       = $this->get_changed_field_labels( $changed_fields );

		if ( ! empty( $field_labels ) ) {
			array_unshift(
				$detail_rows,
				array(
					'label' => __( 'Updated fields', 'foodord' ),
					'value' => implode( ', ', $field_labels ),
				)
			);
		}

		return $this->render_email_body( $status_data, $detail_rows );
	}

	/**
	 * Build the previous email address security notice body.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_previous_email_change_notice_body( $reservation ): string {
		$statuses = Foodord_Table_Reservations::get_statuses();
		$status   = isset( $reservation->status ) ? Foodord_Table_Reservations::sanitize_status( (string) $reservation->status ) : 'pending';
		$rows     = array(
			array(
				'label' => __( 'Reservation code', 'foodord' ),
				'value' => isset( $reservation->reservation_code ) ? (string) $reservation->reservation_code : '',
			),
			array(
				'label' => __( 'Status', 'foodord' ),
				'value' => $statuses[ $status ] ?? __( 'Pending', 'foodord' ),
			),
		);

		$status_data = array(
			'subject'    => __( 'Reservation email address changed', 'foodord' ),
			'label'      => __( 'Security notice', 'foodord' ),
			'title'      => __( 'Reservation email address changed', 'foodord' ),
			'message'    => __( 'The email address for your reservation was changed. If you did not make this change, please contact the restaurant.', 'foodord' ),
			'color'      => '#8a5a00',
			'soft_color' => '#fff6de',
		);

		return $this->render_email_body( $status_data, $rows, $this->get_customer_footer_text( $reservation ) );
	}

	/**
	 * Render the shared reservation email template.
	 *
	 * @param array{subject:string,label:string,title:string,message:string,color:string,soft_color:string} $status_data      Email status data.
	 * @param array<int,array{label:string,value:string}>                                                   $detail_rows     Email detail rows.
	 * @param string                                                                                        $footer_text     Footer text.
	 * @param array{url:string,label:string}|null                                                           $action          Action button data.
	 * @param array<string,mixed>|null                                                                      $preorder_summary Pre-order summary.
	 */
	private function render_email_body( array $status_data, array $detail_rows, string $footer_text = '', ?array $action = null, ?array $preorder_summary = null ): string {
		$business_name = $this->get_business_name();
		$logo_url      = $this->get_business_logo_url();
		$logo_width    = $this->get_business_logo_width();
		$has_logo      = '' !== $logo_url;
		$action_url     = is_array( $action ) && ! empty( $action['url'] ) ? esc_url_raw( (string) $action['url'] ) : '';
		$action_label   = is_array( $action ) && ! empty( $action['label'] ) ? (string) $action['label'] : '';
		ob_start();
		?>
		<!doctype html>
		<html>
		<head>
			<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
			<meta name="viewport" content="width=device-width, initial-scale=1.0">
			<title><?php echo esc_html( $status_data['subject'] ); ?></title>
			<style>
				.foodord-reservation-email-body {
					margin: 0;
					padding: 0;
					background: #ffffff;
					color: #12212b;
					font-family: Arial, Helvetica, sans-serif;
				}

				.foodord-reservation-email-table {
					width: 100%;
					border-collapse: collapse;
					background: #ffffff;
				}

				.foodord-reservation-email-shell {
					padding: 44px 16px 36px;
				}

				.foodord-reservation-email-inner {
					width: 100%;
					max-width: 640px;
					border-collapse: collapse;
				}

				.foodord-reservation-email-logo-cell {
					padding: 0 0 18px;
				}

				.foodord-reservation-email-brand {
					display: inline-block;
					max-width: 100%;
					height: auto;
					border: 0;
					vertical-align: middle;
				}

				.foodord-reservation-email-card {
					background: #ffffff;
					border-radius: 14px;
					padding: 34px 34px 30px;
				}

				.foodord-reservation-email-status {
					display: inline-block;
					margin: 0 0 16px;
					border-radius: 999px;
					background: <?php echo esc_html( $status_data['soft_color'] ); ?>;
					color: <?php echo esc_html( $status_data['color'] ); ?>;
					font-size: 13px;
					font-weight: 600;
					line-height: 1.3;
					padding: 7px 12px;
				}

				.foodord-reservation-email-title {
					margin: 0;
					color: #111827;
					font-size: 26px;
					font-weight: 600;
					line-height: 1.25;
					text-align: center;
				}

				.foodord-reservation-email-message {
					margin: 14px auto 26px;
					max-width: 520px;
					color: #40515f;
					font-size: 15px;
					font-weight: 400;
					line-height: 1.65;
					text-align: center;
				}

				.foodord-reservation-email-details {
					width: 100%;
					border-collapse: collapse;
					border-radius: 12px;
					overflow: hidden;
				}

				.foodord-reservation-email-detail-label,
				.foodord-reservation-email-detail-value {
					font-size: 14px;
					line-height: 1.45;
					padding: 13px 14px;
				}

				.foodord-reservation-email-detail-label {
					width: 38%;
					background: #f8fafc;
					color: #697586;
					font-weight: 400;
				}

				.foodord-reservation-email-detail-value {
					background: #ffffff;
					color: #17212b;
					font-weight: 600;
					text-align: right;
				}

				.foodord-reservation-email-preorder-title {
					padding: 26px 0 10px;
					color: #111827;
					font-size: 16px;
					font-weight: 600;
					line-height: 1.35;
					text-align: left;
				}

				.foodord-reservation-email-preorder {
					width: 100%;
					border-collapse: collapse;
					border-top: 1px solid #e5e7eb;
				}

				.foodord-reservation-email-preorder-item td,
				.foodord-reservation-email-preorder-total td {
					border-bottom: 1px solid #e5e7eb;
					padding: 13px 0;
					vertical-align: top;
				}

				.foodord-reservation-email-preorder-name {
					color: #17212b;
					font-size: 14px;
					font-weight: 600;
					line-height: 1.45;
				}

				.foodord-reservation-email-preorder-meta {
					margin-top: 4px;
					color: #697586;
					font-size: 12px;
					font-weight: 400;
					line-height: 1.5;
				}

				.foodord-reservation-email-preorder-meta p,
				.foodord-reservation-email-preorder-meta ul,
				.foodord-reservation-email-preorder-meta li {
					margin: 0;
					padding: 0;
					list-style: none;
				}

				.foodord-reservation-email-preorder-price {
					padding-left: 14px !important;
					color: #17212b;
					font-size: 14px;
					font-weight: 600;
					line-height: 1.45;
					text-align: right;
					white-space: nowrap;
				}

				.foodord-reservation-email-preorder-quantity {
					display: block;
					color: #697586;
					font-size: 12px;
					font-weight: 400;
				}

				.foodord-reservation-email-preorder-total td {
					border-bottom: 0;
					color: #111827;
					font-size: 14px;
					font-weight: 600;
				}

				.foodord-reservation-email-footer {
					margin: 22px auto 0;
					max-width: 500px;
					color: #697586;
					font-size: 13px;
					font-weight: 400;
					line-height: 1.55;
					text-align: center;
				}

				.foodord-reservation-email-action-cell {
					padding: 24px 0 0;
					text-align: center;
				}

				.foodord-reservation-email-action {
					background: #111827;
					border: 1px solid #111827;
					border-radius: 10px;
					color: #ffffff !important;
					display: inline-block;
					font-size: 14px;
					font-weight: 600;
					line-height: 1.2;
					margin: 4px;
					padding: 14px 22px;
					text-decoration: none;
				}

				@media only screen and (max-width: 600px) {
					.foodord-reservation-email-shell {
						padding: 28px 10px 26px !important;
					}

					.foodord-reservation-email-card {
						padding: 26px 16px 24px !important;
					}

					.foodord-reservation-email-title {
						font-size: 23px !important;
					}

					.foodord-reservation-email-message {
						font-size: 14px !important;
					}

					.foodord-reservation-email-detail-label,
					.foodord-reservation-email-detail-value {
						font-size: 13px !important;
						padding: 12px 10px !important;
					}

					.foodord-reservation-email-preorder-name,
					.foodord-reservation-email-preorder-price,
					.foodord-reservation-email-preorder-total td {
						font-size: 13px !important;
					}
				}
			</style>
		</head>
		<body class="foodord-reservation-email-body">
			<table class="foodord-reservation-email-table" role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
				<tr>
					<td align="center" class="foodord-reservation-email-shell">
						<table class="foodord-reservation-email-inner" role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
							<?php if ( $has_logo ) : ?>
								<tr>
									<td align="center" class="foodord-reservation-email-logo-cell">
										<img class="foodord-reservation-email-brand" src="<?php echo esc_url( $logo_url ); ?>" width="<?php echo esc_attr( (string) $logo_width ); ?>" alt="<?php echo esc_attr( $business_name ); ?>">
									</td>
								</tr>
							<?php endif; ?>
							<tr>
								<td align="center" class="foodord-reservation-email-card">
									<span class="foodord-reservation-email-status"><?php echo esc_html( $status_data['label'] ); ?></span>
									<h1 class="foodord-reservation-email-title"><?php echo esc_html( $status_data['title'] ); ?></h1>
									<p class="foodord-reservation-email-message"><?php echo esc_html( $status_data['message'] ); ?></p>
									<table class="foodord-reservation-email-details" role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
										<?php foreach ( $detail_rows as $row ) : ?>
											<tr class="foodord-reservation-email-detail-row">
												<td align="left" class="foodord-reservation-email-detail-label"><?php echo esc_html( $row['label'] ); ?></td>
												<td align="right" class="foodord-reservation-email-detail-value"><?php echo esc_html( $row['value'] ); ?></td>
											</tr>
										<?php endforeach; ?>
									</table>
									<?php if ( is_array( $preorder_summary ) && ! empty( $preorder_summary['items'] ) ) : ?>
										<div class="foodord-reservation-email-preorder-title"><?php esc_html_e( 'Pre-order', 'foodord' ); ?></div>
										<table class="foodord-reservation-email-preorder" role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
											<?php foreach ( $preorder_summary['items'] as $item ) : ?>
												<tr class="foodord-reservation-email-preorder-item">
													<td align="left">
														<div class="foodord-reservation-email-preorder-name"><?php echo wp_kses_post( $item['name'] ); ?></div>
														<?php if ( '' !== $item['meta'] || '' !== $item['extra_meta'] ) : ?>
															<div class="foodord-reservation-email-preorder-meta">
																<?php echo wp_kses_post( $item['meta'] ); ?>
																<?php echo nl2br( esc_html( $item['extra_meta'] ) ); ?>
															</div>
														<?php endif; ?>
													</td>
													<td align="right" class="foodord-reservation-email-preorder-price">
														<span class="foodord-reservation-email-preorder-quantity"><?php echo esc_html( (string) $item['quantity'] ); ?> &times;</span>
														<?php echo wp_kses_post( $item['price'] ); ?>
													</td>
												</tr>
											<?php endforeach; ?>
											<tr class="foodord-reservation-email-preorder-total">
												<td align="left"><?php esc_html_e( 'Total', 'foodord' ); ?></td>
												<td align="right"><?php echo wp_kses_post( $preorder_summary['total'] ); ?></td>
											</tr>
										</table>
									<?php endif; ?>
									<?php if ( '' !== $action_url && '' !== $action_label ) : ?>
										<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
											<tr>
												<td class="foodord-reservation-email-action-cell">
													<a class="foodord-reservation-email-action" href="<?php echo esc_url( $action_url ); ?>"><?php echo esc_html( $action_label ); ?></a>
												</td>
											</tr>
										</table>
									<?php endif; ?>
									<?php if ( '' !== $footer_text ) : ?>
										<p class="foodord-reservation-email-footer"><?php echo esc_html( $footer_text ); ?></p>
									<?php endif; ?>
								</td>
							</tr>
						</table>
					</td>
				</tr>
			</table>
		</body>
		</html>
		<?php
		return (string) ob_get_clean();
	}

	/**
	 * Build the pre-order summary shown in customer reservation emails.
	 *
	 * @param object $reservation Reservation row.
	 * @return array{items:array<int,array{name:string,quantity:int,price:string,meta:string,extra_meta:string}>,total:string}|null
	 */
	private function get_preorder_summary( $reservation ): ?array {
		$order_id = isset( $reservation->order_id ) ? absint( $reservation->order_id ) : 0;
		if ( $order_id <= 0 || ! function_exists( 'wc_get_order' ) || ! class_exists( 'Foodord_Table_Reservation_Deposits' ) ) {
			return null;
		}

		$order = wc_get_order( $order_id );
		if ( ! $order instanceof WC_Order || ! Foodord_Table_Reservation_Deposits::is_preorder_order( $order ) ) {
			return null;
		}

		$items          = array();
		$meta_formatter = class_exists( 'Foodord_Product_Modal' ) ? new Foodord_Product_Modal() : null;
		foreach ( $order->get_items( 'line_item' ) as $item ) {
			if ( ! $item instanceof WC_Order_Item_Product ) {
				continue;
			}

			$name = $item->get_name();
			ob_start();
			wc_display_item_meta(
				$item,
				array(
					'before'       => '',
					'after'        => '',
					'separator'    => '<br>',
					'label_before' => '<strong>',
					'label_after'  => ':</strong> ',
				)
			);
			$meta = (string) ob_get_clean();

			$extra_meta = $meta_formatter instanceof Foodord_Product_Modal
				? $meta_formatter->get_order_item_additional_meta_text( $item )
				: '';

			$items[] = array(
				'name'       => (string) $name,
				'quantity'   => max( 1, (int) $item->get_quantity() ),
				'price'      => $order->get_formatted_line_subtotal( $item ),
				'meta'       => (string) $meta,
				'extra_meta' => $extra_meta,
			);
		}

		if ( empty( $items ) ) {
			return null;
		}

		return array(
			'items' => $items,
			'total' => $order->get_formatted_order_total(),
		);
	}

	/**
	 * Get the customer management email action.
	 *
	 * @param object $reservation Reservation row.
	 * @param string $status      Reservation status.
	 * @return array{url:string,label:string}|null
	 */
	private function get_customer_management_action( $reservation, string $status ): ?array {
		if ( ! in_array( $status, array( 'pending', 'confirmed' ), true ) ) {
			return null;
		}

		$reservations = new Foodord_Table_Reservations();
		if ( ! $reservations->can_customer_manage_reservation( $reservation ) && ! $reservations->can_customer_cancel_reservation( $reservation ) ) {
			return null;
		}

		$token = isset( $reservation->manage_token ) ? sanitize_text_field( (string) $reservation->manage_token ) : '';
		if ( '' === $token || ! Foodord_Table_Reservations::is_customer_management_enabled() ) {
			return null;
		}

		$url = Foodord_Table_Reservations::get_manage_reservation_url( $reservation, $token );
		if ( '' === $url ) {
			return null;
		}

		return array(
			'url'   => $url,
			'label' => 'pending' === $status ? __( 'Edit or cancel request', 'foodord' ) : __( 'Manage reservation', 'foodord' ),
		);
	}

	/**
	 * Get status-specific email content.
	 *
	 * @param object $reservation Reservation row.
	 * @param string $status      Reservation status.
	 * @return array{subject:string,label:string,title:string,message:string,color:string,soft_color:string}
	 */
	private function get_status_email_data( $reservation, string $status ): array {
		$name = $this->get_customer_name( $reservation );

		if ( 'updated' === $status ) {
			return $this->apply_customer_content_overrides(
				array(
					'subject'    => __( 'Reservation details updated', 'foodord' ),
					'label'      => __( 'Updated', 'foodord' ),
					'title'      => __( 'Your reservation has been updated', 'foodord' ),
					/* translators: %s: customer name. */
					'message'    => sprintf( __( 'Thanks %s, your reservation details have been updated. Please review the latest information below.', 'foodord' ), $name ),
					'color'      => '#3858e9',
					'soft_color' => '#eef2ff',
				),
				$reservation,
				$status
			);
		}

		if ( 'confirmed' === $status ) {
			$party_size = isset( $reservation->party_size ) ? max( 1, absint( $reservation->party_size ) ) : 1;

			return $this->apply_customer_content_overrides(
				array(
					'subject'    => __( 'Your reservation is confirmed', 'foodord' ),
					'label'      => __( 'Confirmed', 'foodord' ),
					'title'      => __( 'Your reservation is confirmed', 'foodord' ),
					'message'    => sprintf(
						/* translators: %s: customer name. */
						_n(
							'Good news %s, your reservation is confirmed. We look forward to welcoming you.',
							'Good news %s, your reservation is confirmed. We look forward to welcoming you and your guests.',
							$party_size,
							'foodord'
						),
						$name
					),
					'color'      => '#176b4d',
					'soft_color' => '#eaf7f0',
				),
				$reservation,
				$status
			);
		}

		if ( 'cancelled' === $status ) {
			return $this->apply_customer_content_overrides(
				array(
					'subject'    => __( 'Your reservation has been cancelled', 'foodord' ),
					'label'      => __( 'Cancelled', 'foodord' ),
					'title'      => __( 'Your reservation has been cancelled', 'foodord' ),
					/* translators: %s: customer name. */
					'message'    => sprintf( __( '%s, your reservation has been cancelled. Please contact us if you have any questions.', 'foodord' ), $name ),
					'color'      => '#b42318',
					'soft_color' => '#fff1f0',
				),
				$reservation,
				$status
			);
		}

		return $this->apply_customer_content_overrides(
			array(
				'subject'    => __( 'Reservation request received', 'foodord' ),
				'label'      => __( 'Pending', 'foodord' ),
				'title'      => __( 'We received your reservation request', 'foodord' ),
				/* translators: %s: customer name. */
				'message'    => sprintf( __( 'Thanks %s, we have received your reservation request and will review it shortly.', 'foodord' ), $name ),
				'color'      => '#8a5a00',
				'soft_color' => '#fff6de',
			),
			$reservation,
			'pending'
		);
	}

	/**
	 * Apply customer email content overrides to status data.
	 *
	 * @param array{subject:string,label:string,title:string,message:string,color:string,soft_color:string} $status_data Default status data.
	 * @param object                                                                                        $reservation Reservation row.
	 * @param string                                                                                        $status      Reservation status.
	 * @return array{subject:string,label:string,title:string,message:string,color:string,soft_color:string}
	 */
	private function apply_customer_content_overrides( array $status_data, $reservation, string $status ): array {
		$settings = self::get_customer_content_settings();
		$template = isset( $settings[ $status ] ) && is_array( $settings[ $status ] ) ? $settings[ $status ] : array();

		if ( ! empty( $template['subject'] ) ) {
			$status_data['subject'] = $this->replace_customer_content_placeholders( (string) $template['subject'], $reservation );
		}

		if ( ! empty( $template['heading'] ) ) {
			$status_data['title'] = $this->replace_customer_content_placeholders( (string) $template['heading'], $reservation );
		}

		if ( ! empty( $template['message'] ) ) {
			$status_data['message'] = $this->replace_customer_content_placeholders( (string) $template['message'], $reservation );
		}

		return $status_data;
	}

	/**
	 * Replace supported customer email content placeholders.
	 *
	 * @param string $content     Email content.
	 * @param object $reservation Reservation row.
	 */
	private function replace_customer_content_placeholders( string $content, $reservation ): string {
		return strtr(
			$content,
			array(
				'{customer_name}'   => $this->get_customer_name( $reservation ),
				'{restaurant_name}' => $this->get_business_name(),
			)
		);
	}

	/**
	 * Get admin notification email content.
	 *
	 * @param object $reservation Reservation row.
	 * @return array{subject:string,label:string,title:string,message:string,color:string,soft_color:string}
	 */
	private function get_admin_email_data( $reservation ): array {
		$statuses = Foodord_Table_Reservations::get_statuses();
		$status   = isset( $reservation->status ) ? Foodord_Table_Reservations::sanitize_status( (string) $reservation->status ) : 'pending';
		$label    = $statuses[ $status ] ?? __( 'Pending', 'foodord' );

		return array(
			'subject'    => __( 'New reservation received', 'foodord' ),
			'label'      => $label,
			'title'      => __( 'New reservation received', 'foodord' ),
			'message'    => __( 'A new table reservation has been submitted. Review the details below.', 'foodord' ),
			'color'      => '#3858e9',
			'soft_color' => '#eef2ff',
		);
	}

	/**
	 * Get admin cancellation email content.
	 *
	 * @return array{subject:string,label:string,title:string,message:string,color:string,soft_color:string}
	 */
	private function get_admin_cancelled_email_data(): array {
		return array(
			'subject'    => __( 'Reservation cancelled', 'foodord' ),
			'label'      => __( 'Cancelled', 'foodord' ),
			'title'      => __( 'Reservation cancelled', 'foodord' ),
			'message'    => __( 'A table reservation has been cancelled. Review the details below.', 'foodord' ),
			'color'      => '#b42318',
			'soft_color' => '#fff1f0',
		);
	}

	/**
	 * Get admin updated reservation email content.
	 *
	 * @return array{subject:string,label:string,title:string,message:string,color:string,soft_color:string}
	 */
	private function get_admin_updated_email_data(): array {
		return array(
			'subject'    => __( 'Reservation details updated', 'foodord' ),
			'label'      => __( 'Updated', 'foodord' ),
			'title'      => __( 'Reservation details updated', 'foodord' ),
			'message'    => __( 'A reservation has been updated. Review the latest details below.', 'foodord' ),
			'color'      => '#3858e9',
			'soft_color' => '#eef2ff',
		);
	}

	/**
	 * Get translated labels for changed reservation fields.
	 *
	 * @param array<int,string> $changed_fields Changed reservation fields.
	 * @return array<int,string>
	 */
	private function get_changed_field_labels( array $changed_fields ): array {
		$labels         = array(
			'date'       => __( 'Date', 'foodord' ),
			'time'       => __( 'Time', 'foodord' ),
			'party_size' => __( 'Guests', 'foodord' ),
			'name'       => __( 'Name', 'foodord' ),
			'email'      => __( 'Email', 'foodord' ),
			'phone'      => __( 'Phone', 'foodord' ),
			'note'       => __( 'Additional Request', 'foodord' ),
		);
		$changed_labels = array();

		foreach ( array_unique( array_map( 'sanitize_key', $changed_fields ) ) as $field ) {
			if ( isset( $labels[ $field ] ) ) {
				$changed_labels[] = $labels[ $field ];
			}
		}

		return $changed_labels;
	}

	/**
	 * Get reservation detail rows for the email.
	 *
	 * @param object $reservation  Reservation row.
	 * @param string $status       Reservation status.
	 * @param string $status_label Status label.
	 * @return array<int,array{label:string,value:string}>
	 */
	private function get_detail_rows( $reservation, string $status, string $status_label ): array {
		$rows = array(
			array(
				'label' => __( 'Date', 'foodord' ),
				'value' => $this->format_reservation_date( $reservation ),
			),
			array(
				'label' => __( 'Time', 'foodord' ),
				'value' => $this->format_reservation_time( $reservation ),
			),
			array(
				'label' => __( 'Guests', 'foodord' ),
				'value' => isset( $reservation->party_size ) ? (string) absint( $reservation->party_size ) : '',
			),
		);

		$table_label = $this->format_table_location( $reservation );
		if ( in_array( $status, array( 'confirmed', 'cancelled' ), true ) && '' !== $table_label ) {
			$rows[] = array(
				'label' => __( 'Table', 'foodord' ),
				'value' => $table_label,
			);
		}

		$note = isset( $reservation->customer_note ) ? trim( wp_strip_all_tags( (string) $reservation->customer_note ) ) : '';
		if ( '' !== $note ) {
			$rows[] = array(
				'label' => __( 'Additional Request', 'foodord' ),
				'value' => $note,
			);
		}

		$rows = array_merge( $rows, $this->get_deposit_detail_rows( $reservation ) );

		$cancellation_reason = $this->get_cancellation_reason( $reservation );
		if ( 'cancelled' === $status && '' !== $cancellation_reason ) {
			$rows[] = array(
				'label' => __( 'Cancellation reason', 'foodord' ),
				'value' => $cancellation_reason,
			);
		}

		$rows[] = array(
			'label' => __( 'Reservation code', 'foodord' ),
			'value' => isset( $reservation->reservation_code ) ? (string) $reservation->reservation_code : '',
		);

		$rows[] = array(
			'label' => __( 'Status', 'foodord' ),
			'value' => $status_label,
		);

		return $rows;
	}

	/**
	 * Get reservation detail rows for admin notifications.
	 *
	 * @param object $reservation  Reservation row.
	 * @param string $status_label Status label.
	 * @return array<int,array{label:string,value:string}>
	 */
	private function get_admin_detail_rows( $reservation, string $status_label ): array {
		$rows = array(
			array(
				'label' => __( 'Date', 'foodord' ),
				'value' => $this->format_reservation_date( $reservation ),
			),
			array(
				'label' => __( 'Time', 'foodord' ),
				'value' => $this->format_reservation_time( $reservation ),
			),
			array(
				'label' => __( 'Guests', 'foodord' ),
				'value' => isset( $reservation->party_size ) ? (string) absint( $reservation->party_size ) : '',
			),
		);

		$table_label = $this->format_table_location( $reservation );
		if ( '' !== $table_label ) {
			$rows[] = array(
				'label' => __( 'Table', 'foodord' ),
				'value' => $table_label,
			);
		}

		$rows[] = array(
			'label' => __( 'Customer', 'foodord' ),
			'value' => $this->get_admin_customer_name( $reservation ),
		);

		$rows[] = array(
			'label' => __( 'Email', 'foodord' ),
			'value' => $this->get_admin_customer_email( $reservation ),
		);

		$rows[] = array(
			'label' => __( 'Phone', 'foodord' ),
			'value' => $this->get_admin_customer_phone( $reservation ),
		);

		$note = isset( $reservation->customer_note ) ? trim( wp_strip_all_tags( (string) $reservation->customer_note ) ) : '';
		if ( '' !== $note ) {
			$rows[] = array(
				'label' => __( 'Additional Request', 'foodord' ),
				'value' => $note,
			);
		}

		$rows = array_merge( $rows, $this->get_deposit_detail_rows( $reservation ) );

		$cancellation_reason = $this->get_cancellation_reason( $reservation );
		if ( '' !== $cancellation_reason ) {
			$rows[] = array(
				'label' => __( 'Cancellation reason', 'foodord' ),
				'value' => $cancellation_reason,
			);
		}

		$rows[] = array(
			'label' => __( 'Reservation code', 'foodord' ),
			'value' => isset( $reservation->reservation_code ) ? (string) $reservation->reservation_code : '',
		);

		$rows[] = array(
			'label' => __( 'Status', 'foodord' ),
			'value' => $status_label,
		);

		return $rows;
	}

	/**
	 * Get reservation deposit detail rows.
	 *
	 * @param object $reservation Reservation row.
	 * @return array<int,array{label:string,value:string}>
	 */
	private function get_deposit_detail_rows( $reservation ): array {
		$amount = isset( $reservation->deposit_amount ) ? (float) $reservation->deposit_amount : 0.0;
		if ( $amount <= 0 ) {
			return array();
		}

		return array(
			array(
				'label' => __( 'Deposit', 'foodord' ),
				'value' => Foodord_Table_Reservations::format_money( $amount ),
			),
			array(
				'label' => __( 'Payment', 'foodord' ),
				'value' => $this->get_payment_status_label( $reservation ),
			),
		);
	}

	/**
	 * Get a readable payment status label.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_payment_status_label( $reservation ): string {
		$payment_status = isset( $reservation->payment_status ) ? sanitize_key( (string) $reservation->payment_status ) : '';
		$labels         = array(
			'requires_payment' => __( 'Payment required', 'foodord' ),
			'paid'             => __( 'Paid', 'foodord' ),
			'expired'          => __( 'Expired', 'foodord' ),
			'failed'           => __( 'Failed', 'foodord' ),
			'none'             => __( 'No payment', 'foodord' ),
		);

		return $labels[ $payment_status ] ?? __( 'Payment required', 'foodord' );
	}

	/**
	 * Get the customer email footer text.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_customer_footer_text( $reservation ): string {
		$settings = self::get_customer_content_settings();
		if ( ! empty( $settings['footer'] ) ) {
			return $this->replace_customer_content_placeholders( (string) $settings['footer'], $reservation );
		}

		return sprintf(
			/* translators: %s: restaurant name. */
			__( 'Need help? Please contact %s directly.', 'foodord' ),
			$this->get_business_name()
		);
	}

	/**
	 * Format a floor/table label for customer emails.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function format_table_location( $reservation ): string {
		$table_name = isset( $reservation->table_name ) ? trim( wp_strip_all_tags( (string) $reservation->table_name ) ) : '';
		$floor_name = isset( $reservation->floor_name ) ? trim( wp_strip_all_tags( (string) $reservation->floor_name ) ) : '';

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

		if ( '' === $floor_name ) {
			return $table_name;
		}

		return $floor_name . ' - ' . $table_name;
	}

	/**
	 * Get a readable customer name.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_customer_name( $reservation ): string {
		$name = isset( $reservation->customer_name ) ? trim( wp_strip_all_tags( (string) $reservation->customer_name ) ) : '';

		if ( '' === $name ) {
			return __( 'there', 'foodord' );
		}

		return $name;
	}

	/**
	 * Get a readable customer name for admin notifications.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_admin_customer_name( $reservation ): string {
		$name = isset( $reservation->customer_name ) ? trim( wp_strip_all_tags( (string) $reservation->customer_name ) ) : '';

		if ( '' === $name ) {
			return '-';
		}

		return $name;
	}

	/**
	 * Get a readable customer email for admin notifications.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_admin_customer_email( $reservation ): string {
		$email = $this->get_customer_email( $reservation );

		return '' !== $email ? $email : '-';
	}

	/**
	 * Get a readable customer phone for admin notifications.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_admin_customer_phone( $reservation ): string {
		$phone = isset( $reservation->customer_phone ) ? trim( wp_strip_all_tags( (string) $reservation->customer_phone ) ) : '';

		return '' !== $phone ? $phone : '-';
	}

	/**
	 * Get a sanitized cancellation reason from a reservation row.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function get_cancellation_reason( $reservation ): string {
		$reason = isset( $reservation->cancellation_reason ) ? (string) $reservation->cancellation_reason : '';

		return Foodord_Table_Reservations::sanitize_cancellation_reason( $reason );
	}

	/**
	 * Format reservation date.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function format_reservation_date( $reservation ): string {
		$date = isset( $reservation->reservation_date ) ? (string) $reservation->reservation_date : '';
		if ( '' === $date ) {
			return '';
		}

		$datetime = $this->parse_reservation_datetime( $date, '00:00:00' );
		if ( ! $datetime instanceof DateTimeImmutable ) {
			return $date;
		}

		return wp_date( get_option( 'date_format' ), $datetime->getTimestamp(), wp_timezone() );
	}

	/**
	 * Format reservation time.
	 *
	 * @param object $reservation Reservation row.
	 */
	private function format_reservation_time( $reservation ): string {
		$date = isset( $reservation->reservation_date ) ? (string) $reservation->reservation_date : '';
		$time = isset( $reservation->reservation_time ) ? (string) $reservation->reservation_time : '';
		if ( '' === $time ) {
			return '';
		}

		$datetime = $this->parse_reservation_datetime( '' !== $date ? $date : '1970-01-01', $time );
		if ( ! $datetime instanceof DateTimeImmutable ) {
			return substr( $time, 0, 5 );
		}

		return wp_date( get_option( 'time_format' ), $datetime->getTimestamp(), wp_timezone() );
	}

	/**
	 * Parse reservation date and time in the WordPress timezone.
	 *
	 * @param string $date Reservation date in Y-m-d format.
	 * @param string $time Reservation time in H:i or H:i:s format.
	 */
	private function parse_reservation_datetime( string $date, string $time ): ?DateTimeImmutable {
		$date = trim( $date );
		$time = trim( $time );

		if ( preg_match( '/^\d{2}:\d{2}$/', $time ) ) {
			$time .= ':00';
		} elseif ( preg_match( '/^\d{2}:\d{2}:\d{2}/', $time ) ) {
			$time = substr( $time, 0, 8 );
		}

		$datetime = DateTimeImmutable::createFromFormat( '!Y-m-d H:i:s', $date . ' ' . $time, wp_timezone() );
		$errors   = DateTimeImmutable::getLastErrors();
		if (
			! $datetime instanceof DateTimeImmutable ||
			(
				false !== $errors &&
				(
					! empty( $errors['warning_count'] ) ||
					! empty( $errors['error_count'] )
				)
			)
		) {
			return null;
		}

		return $datetime;
	}

	/**
	 * Get the restaurant name used in emails.
	 */
	private function get_business_name(): string {
		$name = trim( (string) get_option( 'wr_home_banner_company_name', '' ) );
		if ( '' === $name ) {
			$name = wp_strip_all_tags( (string) get_bloginfo( 'name' ) );
		}

		return '' !== $name ? $name : __( 'our restaurant', 'foodord' );
	}

	/**
	 * Get the WooCommerce email logo URL.
	 */
	private function get_business_logo_url(): string {
		return esc_url_raw( (string) get_option( 'woocommerce_email_header_image', '' ) );
	}

	/**
	 * Get the WooCommerce email logo width.
	 */
	private function get_business_logo_width(): int {
		$width = absint( get_option( 'woocommerce_email_header_image_width', '120' ) );
		if ( $width <= 0 ) {
			return 120;
		}

		return min( 220, max( 40, $width ) );
	}
}
