<?php

declare( strict_types=1 );

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

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

/**
 * Sends transactional emails to customers and the facility owner.
 * All hook registrations live in the constructor — just instantiate once.
 *
 * Reply-to header: playard_get_reply_to() — returns facility email or WP admin email.
 */
class Playard_Notification {

	public function __construct() {
		// Booking confirmation — fires for both Stripe (confirmed) and cash (created)
		add_action( 'playard_booking_confirmed', [ $this, 'send_booking_confirmation' ] );
		add_action( 'playard_booking_created',   [ $this, 'send_booking_confirmation' ] );

		// Payment receipt — fires after any successful payment
		add_action( 'playard_payment_received', [ $this, 'send_receipt' ] );

		// Reminders — runs every 5 minutes via cron
		add_action( 'playard_send_reminders', [ $this, 'process_reminders' ] );

		// Cancellation confirmation
		add_action( 'playard_booking_cancelled', [ $this, 'send_cancellation_email' ] );

		// Owner new booking alert
		add_action( 'playard_booking_confirmed', [ $this, 'send_owner_new_booking' ] );
		add_action( 'playard_booking_created',   [ $this, 'send_owner_new_booking' ] );

		// Owner cancellation alert — only when customer cancels (accepts 2 args)
		add_action( 'playard_booking_cancelled', [ $this, 'maybe_send_owner_cancellation' ], 10, 2 );

		// Booking status changed — admin action
		add_action( 'playard_booking_status_changed', [ $this, 'send_status_changed_email' ], 10, 3 );

		// New customer account welcome email
		add_action( 'playard_customer_registered', [ $this, 'send_welcome_email' ] );

		// Set-password invitation — fires for accounts created via any route
		add_action( 'playard_customer_created',    [ $this, 'send_set_password_invitation' ] );
		add_action( 'playard_customer_registered', [ $this, 'send_set_password_invitation' ] );

		// Walk-in Stripe booking: send the payment link to the customer's email
		add_action( 'playard_walkin_payment_link_created', [ $this, 'send_payment_link_email' ], 10, 3 );
	}

	// ── Settings helpers ──────────────────────────────────────────────────────────

	/**
	 * Returns the notification settings array from playard_settings.
	 *
	 * @return array<string,mixed>
	 */
	private function get_notif_settings(): array {
		$settings = (array) get_option( 'playard_settings', [] );
		return (array) ( $settings['notifications'] ?? [] );
	}

	/**
	 * Returns true when the given notification key is enabled (default: true).
	 */
	private function is_enabled( string $key ): bool {
		$notif = $this->get_notif_settings();
		return (bool) ( $notif[ $key ] ?? true );
	}

	/**
	 * Returns the From header name for outgoing emails.
	 */
	private function get_from_name(): string {
		$notif = $this->get_notif_settings();
		if ( ! empty( $notif['from_name'] ) ) {
			return sanitize_text_field( (string) $notif['from_name'] );
		}
		$facility = playard_get_facility();
		return $facility ? sanitize_text_field( (string) $facility->name ) : (string) get_bloginfo( 'name' );
	}

	/**
	 * Returns the From header email address.
	 */
	private function get_from_email(): string {
		$notif = $this->get_notif_settings();
		if ( ! empty( $notif['from_email'] ) && is_email( $notif['from_email'] ) ) {
			return sanitize_email( (string) $notif['from_email'] );
		}
		return (string) get_option( 'admin_email', '' );
	}

	// ── Low-level send ────────────────────────────────────────────────────────────

	/**
	 * Sends an HTML email and logs the result.
	 *
	 * @param int    $booking_id  0 for non-booking emails (OTP, etc.)
	 * @param string $log_type    Notification type slug for deduplication.
	 */
	public function send( string $to, string $subject, string $message, int $booking_id = 0, string $log_type = '' ): bool {
		$from_name  = $this->get_from_name();
		$from_email = $this->get_from_email();
		$reply_to   = playard_get_reply_to();

		$headers = [ 'Content-Type: text/html; charset=UTF-8' ];

		if ( $from_name && $from_email ) {
			$headers[] = 'From: ' . $from_name . ' <' . $from_email . '>';
		} elseif ( $from_email ) {
			$headers[] = 'From: ' . $from_email;
		}

		if ( $reply_to ) {
			$headers[] = 'Reply-To: ' . $reply_to;
		}

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

		do_action( 'playard_email_sent', $to, $subject, $sent );

		return $sent;
	}

	// ── 8.1 — Booking confirmation ────────────────────────────────────────────────

	/**
	 * Sends a booking confirmation / reservation email to the customer.
	 * Silently skips if the customer has no email on file.
	 * Hooked to 'playard_booking_confirmed' and 'playard_booking_created'.
	 */
	public function send_booking_confirmation( int $booking_id ): void {
		if ( ! $this->is_enabled( 'booking_confirmed_customer' ) ) {
			return;
		}

		global $wpdb;

		$booking = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT b.*, f.name AS field_name, c.name AS customer_name, c.email
				 FROM {$wpdb->prefix}playard_bookings b
				 LEFT JOIN {$wpdb->prefix}playard_fields f    ON b.field_id    = f.id
				 LEFT JOIN {$wpdb->prefix}playard_customers c ON b.customer_id = c.id
				 WHERE b.id = %d
				 LIMIT 1",
				$booking_id
			)
		);

		if ( ! $booking || empty( $booking->email ) ) {
			return;
		}

		$facility = playard_get_facility();
		$currency = $facility ? playard_get_currency_symbol( (string) $facility->currency ) : '';
		$is_paid  = ( $booking->payment_status === 'paid' );

		$subject = sprintf(
			$is_paid
				/* translators: %s: booking reference */
				? __( 'Booking Confirmed — %s', 'playard' )
				/* translators: %s: booking reference */
				: __( 'Booking Reserved — %s', 'playard' ),
			$booking->booking_ref
		);

		// Generate a random, time-limited cancel token (90 days). Stored in a transient so it
		// can be invalidated after use and is not guessable from public booking data.
		$cancel_token = wp_generate_password( 32, false );
		set_transient( 'playard_cancel_' . (string) $booking->booking_ref, $cancel_token, 90 * DAY_IN_SECONDS );

		$cancel_url = add_query_arg(
			[
				'playard_action' => 'cancel',
				'ref'            => $booking->booking_ref,
				'token'          => $cancel_token,
			],
			home_url()
		);

		$body = $this->build_confirmation_email( $booking, $facility, $currency, $cancel_url, $is_paid );

		$this->send( (string) $booking->email, $subject, $body, (int) $booking->id, 'booking_confirmed' );

		do_action( 'playard_confirmation_email_sent', $booking_id );
	}

	/**
	 * Builds the HTML body for the booking confirmation / reservation email.
	 *
	 * @param object      $booking    Booking row joined with fields + customers.
	 * @param object|null $facility   Facility row.
	 * @param string      $currency   Currency symbol (e.g. '$', '৳').
	 * @param string      $cancel_url Tokenised cancel link.
	 * @param bool        $is_paid    True for Stripe; false for cash.
	 */
	private function build_confirmation_email(
		object $booking,
		?object $facility,
		string $currency,
		string $cancel_url,
		bool $is_paid
	): string {
		$facility_name  = $facility ? (string) $facility->name    : '';
		$facility_addr  = $facility ? (string) ( $facility->address ?? '' ) : '';
		$facility_phone = $facility ? (string) ( $facility->phone  ?? '' ) : '';

		$status_text  = $is_paid ? '✅ Confirmed' : '⏳ Reserved';
		$status_color = $is_paid ? '#22C55E' : '#F59E0B';

		$payment_text = $is_paid
			? sprintf( '%s%s — Paid', $currency, number_format( (float) $booking->amount_paid, 2 ) )
			: sprintf( '%s%s — Pay on arrival', $currency, number_format( (float) $booking->amount_total, 2 ) );

		$pay_on_arrival_note = ! $is_paid
			? sprintf(
				'<p style="background:#FEF3C7;border-radius:6px;padding:12px 16px;font-size:13px;color:#92400E;margin:0 0 16px;">💵 Please pay %s%s when you arrive at the facility.</p>',
				esc_html( $currency ),
				esc_html( number_format( (float) $booking->amount_total, 2 ) )
			)
			: '';

		$address_line = $facility_addr
			? '<p style="margin:0;">📍 ' . esc_html( $facility_addr ) . '</p>'
			: '';

		$phone_line = $facility_phone
			? '<p style="margin:4px 0 0;">📞 ' . esc_html( $facility_phone ) . '</p>'
			: '';

		$heading = $is_paid
			? esc_html__( 'Booking Confirmed! ✓', 'playard' )
			: esc_html__( 'Booking Reserved!', 'playard' );

		/* translators: %s: facility name */
		$intro_confirmed = esc_html( sprintf( __( 'Your booking at %s is confirmed!', 'playard' ), $facility_name ) );
		/* translators: %s: facility name */
		$intro_reserved  = esc_html( sprintf( __( 'Your booking at %s is reserved!', 'playard' ), $facility_name ) );
		$intro           = $is_paid ? $intro_confirmed : $intro_reserved;

		$template = '<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#F8FAFC;font-family:Arial,sans-serif;">
<div style="max-width:560px;margin:0 auto;background:#FFFFFF;border-radius:8px;padding:32px;color:#0F172A;line-height:1.6;">

  <h2 style="margin:0 0 4px;font-size:22px;">%1$s</h2>
  <p style="margin:0 0 20px;color:#475569;font-size:14px;">Hi %2$s,<br>%3$s</p>

  <table style="width:100%%;border-collapse:collapse;background:#F8FAFC;border-radius:8px;overflow:hidden;margin-bottom:20px;">
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Booking Reference</td>
      <td style="padding:10px 16px;text-align:right;font-weight:600;font-family:Arial,sans-serif;font-size:13px;border-bottom:1px solid #E2E8F0;">%4$s</td>
    </tr>
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Field</td>
      <td style="padding:10px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">%5$s</td>
    </tr>
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Date</td>
      <td style="padding:10px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">%6$s</td>
    </tr>
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Time</td>
      <td style="padding:10px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">%7$s – %8$s</td>
    </tr>
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Sport</td>
      <td style="padding:10px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">%9$s</td>
    </tr>
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Status</td>
      <td style="padding:10px 16px;text-align:right;font-size:13px;font-weight:500;border-bottom:1px solid #E2E8F0;">%10$s</td>
    </tr>
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;">Payment</td>
      <td style="padding:10px 16px;text-align:right;font-size:13px;font-weight:600;color:%11$s;">%12$s</td>
    </tr>
  </table>

  %13$s

  <div style="font-size:13px;color:#64748B;margin-bottom:20px;">
    %14$s
    %15$s
  </div>

  <p style="font-size:13px;margin:0 0 24px;">
    <a href="%16$s" style="color:#EF4444;text-decoration:none;">Need to cancel? Click here</a>
  </p>

  <p style="margin:0;font-size:14px;">
    Thank you for your booking!<br>
    <strong>— %17$s</strong>
  </p>

</div>
</body>
</html>';

		return sprintf(
			$template,
			$heading,
			esc_html( (string) $booking->customer_name ),
			$intro,
			esc_html( (string) $booking->booking_ref ),
			esc_html( (string) $booking->field_name ),
			esc_html( playard_format_date( (string) $booking->booking_date ) ),
			esc_html( playard_format_time( (string) $booking->start_time ) ),
			esc_html( playard_format_time( (string) $booking->end_time ) ),
			esc_html( (string) $booking->sport ),
			esc_html( $status_text ),
			esc_attr( $status_color ),
			esc_html( $payment_text ),
			$pay_on_arrival_note,
			$address_line,
			$phone_line,
			esc_url( $cancel_url ),
			esc_html( $facility_name )
		);
	}

	// ── 8.4 — Owner new booking alert ────────────────────────────────────────────

	/**
	 * Notifies the facility owner when a new booking arrives.
	 * Hooked to 'playard_booking_confirmed' (Stripe) and 'playard_booking_created' (cash).
	 */
	public function send_owner_new_booking( int $booking_id ): void {
		if ( ! $this->is_enabled( 'booking_confirmed_owner' ) ) {
			return;
		}

		global $wpdb;

		$booking = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT b.*, f.name AS field_name,
				        c.name  AS customer_name,
				        c.phone AS customer_phone,
				        c.email AS customer_email
				 FROM {$wpdb->prefix}playard_bookings b
				 LEFT JOIN {$wpdb->prefix}playard_fields f    ON b.field_id    = f.id
				 LEFT JOIN {$wpdb->prefix}playard_customers c ON b.customer_id = c.id
				 WHERE b.id = %d
				 LIMIT 1",
				$booking_id
			)
		);

		if ( ! $booking ) {
			return;
		}

		$settings    = (array) get_option( 'playard_settings', [] );
		$owner_email = (string) ( $settings['admin_email'] ?? get_option( 'admin_email' ) );

		if ( ! $owner_email ) {
			return;
		}

		$facility = playard_get_facility();
		$currency = $facility ? playard_get_currency_symbol( (string) $facility->currency ) : '';
		$is_paid  = ( $booking->payment_status === 'paid' );

		$payment_text = $is_paid
			? sprintf( '%s%s — Paid (Stripe)', $currency, number_format( (float) $booking->amount_paid, 2 ) )
			: sprintf( '%s%s — Cash (pay on arrival)', $currency, number_format( (float) $booking->amount_total, 2 ) );

		$payment_color = $is_paid ? '#22C55E' : '#F59E0B';

		$dashboard_url = admin_url( 'admin.php?page=playard#/bookings/' . $booking->booking_ref );

		$subject = sprintf(
			/* translators: 1: booking reference, 2: field name */
			__( 'New Booking — %1$s — %2$s', 'playard' ),
			$booking->booking_ref,
			$booking->field_name
		);

		$body = '<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#F8FAFC;font-family:Arial,sans-serif;">
<div style="max-width:560px;margin:0 auto;background:#FFFFFF;border-radius:8px;padding:32px;color:#0F172A;line-height:1.6;">

  <h2 style="margin:0 0 16px;">New Booking! 🎉</h2>

  <table style="width:100%;background:#F8FAFC;border-radius:8px;border-collapse:collapse;margin-bottom:20px;">
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Reference</td>
      <td style="padding:8px 16px;text-align:right;font-family:Arial,sans-serif;font-weight:600;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->booking_ref ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Field</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->field_name ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Date</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( playard_format_date( (string) $booking->booking_date ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Time</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( playard_format_time( (string) $booking->start_time ) ) . ' – ' . esc_html( playard_format_time( (string) $booking->end_time ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Sport</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->sport ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;border-top:2px solid #E2E8F0;">Customer</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;font-weight:500;border-bottom:1px solid #E2E8F0;border-top:2px solid #E2E8F0;">' . esc_html( (string) $booking->customer_name ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Phone</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) ( $booking->customer_phone ?? '—' ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Email</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) ( $booking->customer_email ?: '—' ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-top:2px solid #E2E8F0;">Payment</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;font-weight:600;color:' . esc_attr( $payment_color ) . ';border-top:2px solid #E2E8F0;">' . esc_html( $payment_text ) . '</td>
    </tr>
  </table>

  <p style="margin:0;">
    <a href="' . esc_url( $dashboard_url ) . '" style="display:inline-block;background:#0F172A;color:#FFFFFF;padding:10px 20px;border-radius:8px;text-decoration:none;font-size:14px;font-weight:600;">View in Dashboard →</a>
  </p>

</div>
</body>
</html>';

		$this->send( $owner_email, $subject, $body, (int) $booking->id, 'owner_new_booking' );

		do_action( 'playard_owner_notified_new_booking', $booking_id );
	}

	// ── 8.3 — Cancellation ───────────────────────────────────────────────────────

	/**
	 * Sends a cancellation confirmation email to the customer.
	 * Silently skips if the customer has no email.
	 * Hooked to 'playard_booking_cancelled' (fires for both customer and admin cancellations).
	 */
	public function send_cancellation_email( int $booking_id ): void {
		if ( ! $this->is_enabled( 'booking_cancelled_customer' ) ) {
			return;
		}

		global $wpdb;

		$booking = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT b.*, f.name AS field_name, c.name AS customer_name, c.email
				 FROM {$wpdb->prefix}playard_bookings b
				 LEFT JOIN {$wpdb->prefix}playard_fields f    ON b.field_id    = f.id
				 LEFT JOIN {$wpdb->prefix}playard_customers c ON b.customer_id = c.id
				 WHERE b.id = %d
				 LIMIT 1",
				$booking_id
			)
		);

		if ( ! $booking || empty( $booking->email ) ) {
			return;
		}

		$facility = playard_get_facility();
		$currency = $facility ? playard_get_currency_symbol( (string) $facility->currency ) : '';

		// Look up the actual refund amount from transactions (may be partial).
		$actual_refund = 0.0;
		if ( $booking->payment_status === 'refunded' ) {
			$actual_refund = (float) $wpdb->get_var( $wpdb->prepare(
				"SELECT COALESCE(SUM(amount),0) FROM {$wpdb->prefix}playard_payments
				 WHERE booking_id = %d AND type = 'refund' AND status = 'success'",
				(int) $booking->id
			) );
		}

		// Choose refund note based on gateway and payment status.
		if ( $booking->payment_gateway === 'cash' ) {
			$refund_note = __( '💵 No payment was charged.', 'playard' );
			$note_bg     = '#F1F5F9';
			$note_color  = '#475569';
		} elseif ( (float) $booking->amount_paid > 0 && $booking->payment_status === 'refunded' ) {
			$display_amount = $actual_refund > 0 ? $actual_refund : (float) $booking->amount_paid;
			$refund_note    = sprintf(
				/* translators: 1: currency symbol, 2: refund amount */
				__( '💳 Refund of %1$s%2$s has been issued to your original payment method. Please allow 5–10 business days.', 'playard' ),
				$currency,
				number_format( $display_amount, 2 )
			);
			$note_bg    = '#EFF6FF';
			$note_color = '#1E40AF';
		} elseif ( (float) $booking->amount_paid > 0 ) {
			$refund_note = __( '⚠️ No refund — cancellation was outside the refund window.', 'playard' );
			$note_bg     = '#FEF3C7';
			$note_color  = '#92400E';
		} else {
			$refund_note = '';
			$note_bg     = '';
			$note_color  = '';
		}

		$refund_block = $refund_note
			? sprintf(
				'<p style="background:%s;border-radius:6px;padding:10px 14px;font-size:13px;color:%s;margin:0 0 16px;">%s</p>',
				esc_attr( $note_bg ),
				esc_attr( $note_color ),
				esc_html( $refund_note )
			)
			: '';

		$facility_phone = $facility ? esc_html( (string) ( $facility->phone ?? '' ) ) : '';
		$facility_email = esc_html( playard_get_reply_to() );
		$facility_name  = $facility ? esc_html( (string) $facility->name ) : '';

		$subject = sprintf(
			/* translators: %s: booking reference */
			__( 'Booking Cancelled — %s', 'playard' ),
			$booking->booking_ref
		);

		$body = '<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#F8FAFC;font-family:Arial,sans-serif;">
<div style="max-width:560px;margin:0 auto;background:#FFFFFF;border-radius:8px;padding:32px;color:#0F172A;line-height:1.6;">

  <h2 style="margin:0 0 4px;color:#EF4444;">Booking Cancelled</h2>
  <p style="margin:0 0 16px;color:#475569;">Hi ' . esc_html( (string) $booking->customer_name ) . ',<br>
  Your booking at <strong>' . $facility_name . '</strong> has been cancelled.</p>

  <table style="width:100%;background:#F8FAFC;border-radius:8px;border-collapse:collapse;margin-bottom:16px;">
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Reference</td>
      <td style="padding:8px 16px;text-align:right;font-family:Arial,sans-serif;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->booking_ref ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Field</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->field_name ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Date</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( playard_format_date( (string) $booking->booking_date ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Time</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( playard_format_time( (string) $booking->start_time ) ) . ' – ' . esc_html( playard_format_time( (string) $booking->end_time ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;">Status</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;font-weight:500;color:#EF4444;">❌ Cancelled</td>
    </tr>
  </table>

  ' . $refund_block . '

  <p style="font-size:13px;color:#64748B;margin:0 0 24px;">
    Questions? Contact us:<br>
    ' . ( $facility_phone ? '📞 ' . $facility_phone . '<br>' : '' ) . '
    ' . ( $facility_email ? '📧 ' . $facility_email : '' ) . '
  </p>

  <p style="margin:0;">— <strong>' . $facility_name . '</strong></p>

</div>
</body>
</html>';

		$this->send( (string) $booking->email, $subject, $body, (int) $booking->id, 'booking_cancelled' );

		do_action( 'playard_cancellation_email_sent', $booking_id );
	}

	// ── 8.2 — Reminders ──────────────────────────────────────────────────────────

	/**
	 * Entry point for the playard_send_reminders cron.
	 * Runs every 5 minutes; individual methods are idempotent via the log table.
	 */
	public function process_reminders(): void {
		if ( ! $this->is_enabled( 'reminders_enabled' ) ) {
			return;
		}
		$this->send_day_before_reminders();
		$this->send_hours_before_reminders();
	}

	/**
	 * Sends a 1-day-before reminder for each confirmed/pending booking tomorrow
	 * that has not yet received a reminder_1day notification.
	 * Deduplication uses a transient so the log table is not required.
	 */
	private function send_day_before_reminders(): void {
		global $wpdb;

		$facility = playard_get_facility();
		$tz       = new DateTimeZone( $facility ? (string) $facility->timezone : 'UTC' );
		$tomorrow = ( new DateTime( 'tomorrow', $tz ) )->format( 'Y-m-d' );

		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
		$bookings = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT b.*, f.name AS field_name, c.name AS customer_name, c.email
				 FROM {$wpdb->prefix}playard_bookings b
				 LEFT JOIN {$wpdb->prefix}playard_fields f    ON b.field_id    = f.id
				 LEFT JOIN {$wpdb->prefix}playard_customers c ON b.customer_id = c.id
				 WHERE b.booking_date = %s
				   AND b.booking_status IN ('confirmed','pending')",
				$tomorrow
			)
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter

		foreach ( $bookings as $booking ) {
			if ( empty( $booking->email ) ) {
				continue;
			}
			$key = 'playard_reminder_' . (int) $booking->id . '_1day';
			if ( get_transient( $key ) ) {
				continue;
			}
			$this->send_reminder_email( $booking, '1day' );
			set_transient( $key, 1, 2 * DAY_IN_SECONDS );
		}
	}

	/**
	 * Sends a 2-hour-before reminder for bookings starting within the next 2 hours
	 * that have not yet received a reminder_2hr notification.
	 */
	private function send_hours_before_reminders(): void {
		global $wpdb;

		$facility  = playard_get_facility();
		$tz        = new DateTimeZone( $facility ? (string) $facility->timezone : 'UTC' );
		$now_dt    = new DateTime( 'now', $tz );
		$today     = $now_dt->format( 'Y-m-d' );
		$now_mysql = $now_dt->format( 'Y-m-d H:i:s' );
		$two_dt    = clone $now_dt;
		$two_dt->modify( '+2 hours' );
		$two_hours = $two_dt->format( 'Y-m-d H:i:s' );

		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
		$bookings = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT b.*, f.name AS field_name, c.name AS customer_name, c.email
				 FROM {$wpdb->prefix}playard_bookings b
				 LEFT JOIN {$wpdb->prefix}playard_fields f    ON b.field_id    = f.id
				 LEFT JOIN {$wpdb->prefix}playard_customers c ON b.customer_id = c.id
				 WHERE b.booking_date = %s
				   AND CONCAT(b.booking_date, ' ', b.start_time) <= %s
				   AND CONCAT(b.booking_date, ' ', b.start_time) > %s
				   AND b.booking_status IN ('confirmed','pending')",
				$today,
				$two_hours,
				$now_mysql
			)
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter

		foreach ( $bookings as $booking ) {
			if ( empty( $booking->email ) ) {
				continue;
			}
			$key = 'playard_reminder_' . (int) $booking->id . '_2hr';
			if ( get_transient( $key ) ) {
				continue;
			}
			$this->send_reminder_email( $booking, '2hr' );
			set_transient( $key, 1, 3 * HOUR_IN_SECONDS );
		}
	}

	/**
	 * Sends a single reminder email for a booking.
	 *
	 * @param object $booking  Booking row joined with fields + customers.
	 * @param string $type     '1day' or '2hr'.
	 */
	private function send_reminder_email( object $booking, string $type ): void {
		$facility = playard_get_facility();
		$currency = $facility ? playard_get_currency_symbol( (string) $facility->currency ) : '';

		$subject = $type === '1day'
			? sprintf(
				/* translators: %s: facility name */
				__( 'Reminder — Your booking tomorrow at %s', 'playard' ),
				$facility ? $facility->name : ''
			)
			: __( 'Reminder — Your booking starts in 2 hours!', 'playard' );

		$cash_note = ( $booking->payment_status !== 'paid' )
			? sprintf(
				'<p style="background:#FEF3C7;border-radius:6px;padding:10px 14px;font-size:13px;color:#92400E;margin:16px 0 0;">💵 Please bring %s%s — payment on arrival.</p>',
				esc_html( $currency ),
				esc_html( number_format( (float) $booking->amount_total, 2 ) )
			)
			: '';

		$address_line = $facility && ! empty( $facility->address )
			? '📍 ' . esc_html( (string) $facility->address ) . '<br>'
			: '';

		$phone_line = $facility && ! empty( $facility->phone )
			? '📞 ' . esc_html( (string) $facility->phone )
			: '';

		$heading = $type === '1day'
			? esc_html__( 'Booking Tomorrow! 📅', 'playard' )
			: esc_html__( 'Booking in 2 Hours! ⏰', 'playard' );

		$body = '<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#F8FAFC;font-family:Arial,sans-serif;">
<div style="max-width:560px;margin:0 auto;background:#FFFFFF;border-radius:8px;padding:32px;color:#0F172A;line-height:1.6;">
  <h2 style="margin:0 0 8px;">' . $heading . '</h2>
  <p style="margin:0 0 16px;color:#475569;">Hi ' . esc_html( (string) $booking->customer_name ) . ',<br>
  Just a reminder about your upcoming booking:</p>

  <table style="width:100%;background:#F8FAFC;border-radius:8px;border-collapse:collapse;margin-bottom:16px;">
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Field</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->field_name ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Date</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( playard_format_date( (string) $booking->booking_date ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Time</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( playard_format_time( (string) $booking->start_time ) ) . ' – ' . esc_html( playard_format_time( (string) $booking->end_time ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;">Sport</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;">' . esc_html( (string) $booking->sport ) . '</td>
    </tr>
  </table>

  ' . $cash_note . '

  <p style="font-size:13px;color:#64748B;margin:16px 0 0;">' . $address_line . $phone_line . '</p>

  <p style="margin:24px 0 0;">See you there!<br><strong>— ' . esc_html( $facility ? (string) $facility->name : '' ) . '</strong></p>
</div>
</body>
</html>';

		$this->send( (string) $booking->email, $subject, $body );

		do_action( 'playard_reminder_sent', (int) $booking->id, $type );
	}

	// ── 7.4 — Payment receipt ────────────────────────────────────────────────────

	/**
	 * Sends a payment receipt email after a booking is paid.
	 * Silently skips if the customer has no email on file.
	 * Hooked to 'playard_payment_received'.
	 */
	public function send_receipt( int $booking_id ): void {
		global $wpdb;

		$booking = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT b.*, f.name AS field_name, c.name AS customer_name, c.email
				 FROM {$wpdb->prefix}playard_bookings b
				 LEFT JOIN {$wpdb->prefix}playard_fields f    ON b.field_id    = f.id
				 LEFT JOIN {$wpdb->prefix}playard_customers c ON b.customer_id = c.id
				 WHERE b.id = %d
				 LIMIT 1",
				$booking_id
			)
		);

		if ( ! $booking || empty( $booking->email ) ) {
			return;
		}

		$facility = playard_get_facility();
		$currency = $facility ? playard_get_currency_symbol( (string) $facility->currency ) : '';

		$subject = sprintf(
			/* translators: %s: booking reference */
			__( 'Payment Receipt — %s', 'playard' ),
			$booking->booking_ref
		);

		$body = $this->build_receipt_body( $booking, $facility, $currency );

		$this->send( (string) $booking->email, $subject, $body );

		do_action( 'playard_receipt_sent', $booking_id );
	}

	// ── Walk-in Stripe payment link ──────────────────────────────────────────────

	/**
	 * Sends a Stripe payment link to the customer after an admin creates a walk-in booking.
	 * Hooked to 'playard_walkin_payment_link_created'.
	 *
	 * @param int    $booking_id   Booking row ID.
	 * @param string $checkout_url Stripe Checkout Session URL.
	 * @param string $email        Customer's email address (empty string = skip).
	 */
	public function send_payment_link_email( int $booking_id, string $checkout_url, string $email ): void {
		if ( empty( $email ) || empty( $checkout_url ) ) {
			return;
		}

		global $wpdb;

		$booking = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT b.*, f.name AS field_name, c.name AS customer_name
				 FROM {$wpdb->prefix}playard_bookings b
				 LEFT JOIN {$wpdb->prefix}playard_fields f    ON b.field_id    = f.id
				 LEFT JOIN {$wpdb->prefix}playard_customers c ON b.customer_id = c.id
				 WHERE b.id = %d LIMIT 1",
				$booking_id
			)
		);

		if ( ! $booking ) {
			return;
		}

		$facility = playard_get_facility();
		$currency = $facility ? playard_get_currency_symbol( (string) $facility->currency ) : '';
		$amount   = $currency . number_format( (float) $booking->amount_total, 2 );

		$subject = sprintf(
			/* translators: %s: facility name */
			__( 'Complete your payment — %s', 'playard' ),
			$facility ? esc_html( $facility->name ) : __( 'Booking', 'playard' )
		);

		$date_fmt = gmdate( 'D, M j, Y', strtotime( (string) $booking->booking_date ) );
		$time_fmt = substr( (string) $booking->start_time, 0, 5 ) . ' – ' . substr( (string) $booking->end_time, 0, 5 );

		$body  = '<div style="font-family:Arial,sans-serif;max-width:560px;margin:0 auto;">';
		$body .= '<div style="background:#6366F1;padding:28px 32px;border-radius:12px 12px 0 0;">';
		$body .= '<p style="color:#FFFFFF;font-size:22px;font-weight:700;margin:0;">Complete Your Payment</p>';
		$body .= '<p style="color:#C7D2FE;font-size:14px;margin:6px 0 0;">' . esc_html( $facility ? $facility->name : '' ) . '</p>';
		$body .= '</div>';
		$body .= '<div style="background:#FFFFFF;padding:28px 32px;border:1px solid #E2E8F0;border-top:none;border-radius:0 0 12px 12px;">';
		$body .= '<p style="color:#0F172A;font-size:16px;margin:0 0 20px;">Hi ' . esc_html( (string) $booking->customer_name ) . ',</p>';
		$body .= '<p style="color:#475569;font-size:14px;line-height:1.6;margin:0 0 20px;">Your booking has been created. Please complete your payment to confirm your slot.</p>';
		$body .= '<div style="background:#F8FAFC;border:1px solid #E2E8F0;border-radius:8px;padding:16px 20px;margin:0 0 24px;">';
		$body .= '<p style="color:#64748B;font-size:12px;text-transform:uppercase;letter-spacing:.06em;margin:0 0 10px;">Booking Summary</p>';
		$body .= '<p style="color:#0F172A;font-size:14px;font-weight:600;margin:0 0 4px;">' . esc_html( (string) $booking->field_name ) . '</p>';
		$body .= '<p style="color:#475569;font-size:13px;margin:0 0 4px;">' . esc_html( $date_fmt ) . '</p>';
		$body .= '<p style="color:#475569;font-size:13px;margin:0 0 12px;">' . esc_html( $time_fmt ) . '</p>';
		$body .= '<p style="color:#0F172A;font-size:18px;font-weight:700;margin:0;">Amount Due: ' . esc_html( $amount ) . '</p>';
		$body .= '</div>';
		$body .= '<div style="text-align:center;margin:0 0 24px;">';
		$body .= '<a href="' . esc_url( $checkout_url ) . '" style="display:inline-block;background:#6366F1;color:#FFFFFF;text-decoration:none;font-size:15px;font-weight:600;padding:14px 36px;border-radius:8px;">Pay Now →</a>';
		$body .= '</div>';
		$body .= '<p style="color:#94A3B8;font-size:12px;text-align:center;margin:0;">This payment link expires in 24 hours.</p>';
		$body .= '</div>';
		$body .= '</div>';

		$this->send( $email, $subject, $body, $booking_id, 'payment_link' );
	}

	// ── 8.5 — Owner cancellation alert ──────────────────────────────────────────

	/**
	 * Fires on 'playard_booking_cancelled' (2 args).
	 * Only notifies the owner when the customer cancelled — owner already knows
	 * when they cancel themselves.
	 */
	public function maybe_send_owner_cancellation( int $booking_id, string $cancelled_by ): void {
		if ( $cancelled_by === 'customer' ) {
			$this->send_owner_cancellation( $booking_id );
		}
	}

	/**
	 * Sends the cancellation alert email to the facility owner.
	 */
	private function send_owner_cancellation( int $booking_id ): void {
		global $wpdb;

		$booking = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT b.*, f.name AS field_name,
				        c.name  AS customer_name,
				        c.phone AS customer_phone
				 FROM {$wpdb->prefix}playard_bookings b
				 LEFT JOIN {$wpdb->prefix}playard_fields f    ON b.field_id    = f.id
				 LEFT JOIN {$wpdb->prefix}playard_customers c ON b.customer_id = c.id
				 WHERE b.id = %d
				 LIMIT 1",
				$booking_id
			)
		);

		if ( ! $booking ) {
			return;
		}

		$settings    = (array) get_option( 'playard_settings', [] );
		$owner_email = (string) ( $settings['admin_email'] ?? get_option( 'admin_email' ) );

		if ( ! $owner_email ) {
			return;
		}

		$facility = playard_get_facility();
		$currency = $facility ? playard_get_currency_symbol( (string) $facility->currency ) : '';

		if ( $booking->payment_status === 'refunded' ) {
			$refund_text  = sprintf( '%s%s — Refunded', $currency, number_format( (float) $booking->amount_paid, 2 ) );
			$refund_color = '#3B82F6';
		} elseif ( $booking->payment_gateway === 'cash' ) {
			$refund_text  = __( 'No refund — Cash booking', 'playard' );
			$refund_color = '#64748B';
		} else {
			$refund_text  = __( 'No refund — Outside refund window', 'playard' );
			$refund_color = '#F59E0B';
		}

		$reason        = ! empty( $booking->cancellation_reason ) ? esc_html( (string) $booking->cancellation_reason ) : '—';
		$dashboard_url = admin_url( 'admin.php?page=playard#/bookings/' . $booking->booking_ref );

		$subject = sprintf(
			/* translators: 1: booking reference, 2: field name */
			__( 'Booking Cancelled — %1$s — %2$s', 'playard' ),
			$booking->booking_ref,
			$booking->field_name
		);

		$body = '<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#F8FAFC;font-family:Arial,sans-serif;">
<div style="max-width:560px;margin:0 auto;background:#FFFFFF;border-radius:8px;padding:32px;color:#0F172A;line-height:1.6;">

  <h2 style="margin:0 0 16px;color:#EF4444;">Booking Cancelled &#10060;</h2>
  <p style="margin:0 0 16px;color:#475569;">A booking has been cancelled by the customer.</p>

  <table style="width:100%;background:#F8FAFC;border-radius:8px;border-collapse:collapse;margin-bottom:20px;">
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Reference</td>
      <td style="padding:8px 16px;text-align:right;font-family:Arial,sans-serif;font-weight:600;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->booking_ref ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Field</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->field_name ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Date</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( playard_format_date( (string) $booking->booking_date ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Time</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( playard_format_time( (string) $booking->start_time ) ) . ' &#8211; ' . esc_html( playard_format_time( (string) $booking->end_time ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Sport</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->sport ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;border-top:2px solid #E2E8F0;">Customer</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;font-weight:500;border-bottom:1px solid #E2E8F0;border-top:2px solid #E2E8F0;">' . esc_html( (string) $booking->customer_name ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Phone</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) ( $booking->customer_phone ?? '—' ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;border-top:2px solid #E2E8F0;">Reason</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;border-top:2px solid #E2E8F0;">' . $reason . '</td>
    </tr>
    <tr>
      <td style="padding:8px 16px;color:#64748B;font-size:13px;">Refund</td>
      <td style="padding:8px 16px;text-align:right;font-size:13px;font-weight:500;color:' . esc_attr( $refund_color ) . ';">' . esc_html( $refund_text ) . '</td>
    </tr>
  </table>

  <p style="background:#DCFCE7;border-radius:6px;padding:10px 14px;font-size:13px;color:#166534;margin:0 0 20px;">
    &#10003; This slot is now available for new bookings.
  </p>

  <p style="margin:0;">
    <a href="' . esc_url( $dashboard_url ) . '" style="display:inline-block;background:#0F172A;color:#FFFFFF;padding:10px 20px;border-radius:8px;text-decoration:none;font-size:14px;font-weight:600;">View in Dashboard &#8594;</a>
  </p>

</div>
</body>
</html>';

		$this->send( $owner_email, $subject, $body, (int) $booking->id, 'owner_cancellation' );

		do_action( 'playard_owner_notified_cancellation', $booking_id );
	}

	// ── Booking status changed ────────────────────────────────────────────────────

	/**
	 * Sends a notification to the customer when an admin changes the booking status.
	 * Hooked to 'playard_booking_status_changed' (booking_id, old_status, new_status).
	 */
	public function send_status_changed_email( int $booking_id, string $old_status, string $new_status ): void {
		if ( ! $this->is_enabled( 'booking_status_changed' ) ) {
			return;
		}

		// Skip internal transitions that already have dedicated emails.
		if ( in_array( $new_status, [ 'confirmed', 'cancelled' ], true ) ) {
			return;
		}

		global $wpdb;

		$booking = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT b.*, f.name AS field_name, c.name AS customer_name, c.email
				 FROM {$wpdb->prefix}playard_bookings b
				 LEFT JOIN {$wpdb->prefix}playard_fields f    ON b.field_id    = f.id
				 LEFT JOIN {$wpdb->prefix}playard_customers c ON b.customer_id = c.id
				 WHERE b.id = %d LIMIT 1",
				$booking_id
			)
		);

		if ( ! $booking || empty( $booking->email ) ) {
			return;
		}

		$facility      = playard_get_facility();
		$facility_name = $facility ? esc_html( (string) $facility->name ) : '';

		$status_labels = [
			'pending'   => 'Pending',
			'confirmed' => 'Confirmed',
			'completed' => 'Completed',
			'no_show'   => 'No Show',
		];

		$new_label   = $status_labels[ $new_status ] ?? ucfirst( $new_status );
		$status_icon = $new_status === 'completed' ? '✅' : ( $new_status === 'no_show' ? '⚠️' : 'ℹ️' );

		$subject = sprintf(
			/* translators: 1: booking reference, 2: status label */
			__( 'Booking Update — %1$s — %2$s', 'playard' ),
			$booking->booking_ref,
			$new_label
		);

		$body = '<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#F8FAFC;font-family:Arial,sans-serif;">
<div style="max-width:560px;margin:0 auto;background:#FFFFFF;border-radius:8px;padding:32px;color:#0F172A;line-height:1.6;">
  <h2 style="margin:0 0 4px;">Booking Status Update</h2>
  <p style="margin:0 0 20px;color:#475569;">Hi ' . esc_html( (string) $booking->customer_name ) . ',<br>Your booking at <strong>' . $facility_name . '</strong> has been updated.</p>

  <table style="width:100%;background:#F8FAFC;border-radius:8px;border-collapse:collapse;margin-bottom:16px;">
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Reference</td>
      <td style="padding:10px 16px;text-align:right;font-weight:600;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->booking_ref ) . '</td>
    </tr>
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Date</td>
      <td style="padding:10px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( playard_format_date( (string) $booking->booking_date ) ) . '</td>
    </tr>
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;">Field</td>
      <td style="padding:10px 16px;text-align:right;font-size:13px;border-bottom:1px solid #E2E8F0;">' . esc_html( (string) $booking->field_name ) . '</td>
    </tr>
    <tr>
      <td style="padding:10px 16px;color:#64748B;font-size:13px;">New Status</td>
      <td style="padding:10px 16px;text-align:right;font-size:14px;font-weight:600;">' . esc_html( $status_icon . ' ' . $new_label ) . '</td>
    </tr>
  </table>

  <p style="margin:0;font-size:14px;">— <strong>' . $facility_name . '</strong></p>
</div>
</body>
</html>';

		$this->send( (string) $booking->email, $subject, $body, (int) $booking->id, 'status_changed_' . $new_status );

		do_action( 'playard_status_changed_email_sent', $booking_id, $new_status );
	}

	/**
	 * Builds the HTML body for the payment receipt email.
	 *
	 * @param object      $booking  Booking row joined with fields + customers.
	 * @param object|null $facility Facility row.
	 * @param string      $currency Currency symbol.
	 */
	private function build_receipt_body( object $booking, ?object $facility, string $currency ): string {
		$facility_name  = $facility ? esc_html( (string) $facility->name )  : '';
		$facility_phone = $facility ? esc_html( (string) ( $facility->phone ?? '' ) ) : '';
		$facility_email = esc_html( playard_get_reply_to() );

		$template = '<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#F8FAFC;font-family:Inter,sans-serif;">
<div style="max-width:560px;margin:0 auto;background:#FFFFFF;border-radius:8px;padding:32px;color:#0F172A;">
  <h2 style="margin:0 0 8px;font-size:20px;">Payment Receipt</h2>
  <p style="color:#64748B;font-size:14px;margin:0 0 24px;">Hi %1$s,<br>
  Here is your payment receipt for your booking at <strong>%2$s</strong>.</p>

  <table style="width:100%%;border-collapse:collapse;">
    <tr>
      <td style="padding:8px 0;font-size:13px;color:#64748B;border-bottom:1px solid #F1F5F9;">Booking Reference</td>
      <td style="padding:8px 0;font-size:13px;font-weight:600;text-align:right;border-bottom:1px solid #F1F5F9;">%3$s</td>
    </tr>
    <tr>
      <td style="padding:8px 0;font-size:13px;color:#64748B;border-bottom:1px solid #F1F5F9;">Field</td>
      <td style="padding:8px 0;font-size:13px;text-align:right;border-bottom:1px solid #F1F5F9;">%4$s</td>
    </tr>
    <tr>
      <td style="padding:8px 0;font-size:13px;color:#64748B;border-bottom:1px solid #F1F5F9;">Date</td>
      <td style="padding:8px 0;font-size:13px;text-align:right;border-bottom:1px solid #F1F5F9;">%5$s</td>
    </tr>
    <tr>
      <td style="padding:8px 0;font-size:13px;color:#64748B;border-bottom:1px solid #F1F5F9;">Time</td>
      <td style="padding:8px 0;font-size:13px;text-align:right;border-bottom:1px solid #F1F5F9;">%6$s – %7$s</td>
    </tr>
    <tr>
      <td style="padding:8px 0;font-size:13px;color:#64748B;border-bottom:1px solid #E2E8F0;">Sport</td>
      <td style="padding:8px 0;font-size:13px;text-align:right;border-bottom:1px solid #E2E8F0;">%8$s</td>
    </tr>
    <tr>
      <td style="padding:12px 0;font-size:15px;font-weight:600;color:#0F172A;">Amount Paid</td>
      <td style="padding:12px 0;font-size:15px;font-weight:700;color:#22C55E;text-align:right;">%9$s%10$s</td>
    </tr>
  </table>

  <p style="margin:24px 0 8px;font-size:13px;color:#64748B;">
    Questions? Contact us:<br>
    %11$s%12$s
  </p>

  <p style="margin:24px 0 0;font-size:14px;">
    Thank you for your booking!<br>
    <strong>— %13$s</strong>
  </p>
</div>
</body>
</html>';

		return sprintf(
			$template,
			esc_html( (string) $booking->customer_name ),
			$facility_name,
			esc_html( (string) $booking->booking_ref ),
			esc_html( (string) $booking->field_name ),
			esc_html( playard_format_date( (string) $booking->booking_date ) ),
			esc_html( playard_format_time( (string) $booking->start_time ) ),
			esc_html( playard_format_time( (string) $booking->end_time ) ),
			esc_html( (string) $booking->sport ),
			esc_html( $currency ),
			esc_html( number_format( (float) $booking->amount_paid, 2 ) ),
			$facility_phone ? '📞 ' . $facility_phone . '<br>' : '',
			$facility_email ? '📧 ' . $facility_email : '',
			$facility_name
		);
	}

	// ── Set-password invitation ───────────────────────────────────────────────────

	/**
	 * Sends a "set your password" invitation to a newly created customer.
	 * Hooked to 'playard_customer_created' and 'playard_customer_registered'.
	 * The email contains a one-click link that opens the set-password form directly.
	 */
	public function send_set_password_invitation( int $customer_id ): void {
		if ( ! $this->is_enabled( 'set_password_invitation' ) ) {
			return;
		}

		global $wpdb;

		$customer = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT id, name, email FROM {$wpdb->prefix}playard_customers WHERE id = %d LIMIT 1",
				$customer_id
			)
		);

		if ( ! $customer || empty( $customer->email ) ) {
			return;
		}

		$facility  = playard_get_facility();
		$site_name = $facility ? sanitize_text_field( (string) $facility->name ) : (string) get_bloginfo( 'name' );

		// Generate a 24-hour reset token — reuses the same transient format as forgot-password
		$reset_token = 'erst_' . wp_generate_password( 24, false );
		set_transient( 'playard_emailreset_token_' . $reset_token, sanitize_email( (string) $customer->email ), 24 * HOUR_IN_SECONDS );

		// Use the portal page URL if configured, otherwise fall back to home URL.
		$portal_base = playard_get_portal_url();
		$base_url    = $portal_base ?: home_url( '/' );

		$set_pw_url = add_query_arg(
			[
				'playard_action' => 'set-password',
				'token'          => $reset_token,
				'email'          => rawurlencode( (string) $customer->email ),
			],
			$base_url
		);

		$subject = sprintf(
			/* translators: %s: site/facility name */
			__( 'Set your password for %s', 'playard' ),
			$site_name
		);

		$body = $this->build_set_password_email( (string) $customer->name, $site_name, $set_pw_url );

		$this->send( (string) $customer->email, $subject, $body, 0, 'set_password_invitation' );
	}

	/**
	 * Builds the HTML "set your password" invitation email.
	 */
	private function build_set_password_email( string $name, string $site_name, string $set_pw_url ): string {
		$greeting = $name
			/* translators: %s: customer first name */
			? sprintf( __( 'Hi %s,', 'playard' ), esc_html( $name ) )
			: __( 'Hi there,', 'playard' );

		return '<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#F8FAFC;font-family:Arial,sans-serif;">
<div style="max-width:520px;margin:0 auto;background:#FFFFFF;border-radius:12px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.06);">

  <div style="background:#0F172A;padding:28px 32px;">
    <h1 style="margin:0;font-size:20px;font-weight:700;color:#FFFFFF;">' . esc_html( $site_name ) . '</h1>
    <p style="margin:6px 0 0;font-size:13px;color:#94A3B8;">' . esc_html( __( 'Account Security', 'playard' ) ) . '</p>
  </div>

  <div style="padding:32px;">
    <p style="margin:0 0 16px;font-size:15px;color:#0F172A;line-height:1.6;">' . $greeting . '</p>
    <p style="margin:0 0 24px;font-size:14px;color:#475569;line-height:1.75;">'
		. esc_html( __( 'Your account has been created. Set a password so you can sign in quickly with just your email and password — no code needed each time.', 'playard' ) ) .
		'</p>

    <div style="text-align:center;margin-bottom:28px;">
      <a href="' . esc_url( $set_pw_url ) . '" style="display:inline-block;background:#3B82F6;color:#FFFFFF;text-decoration:none;font-size:14px;font-weight:700;border-radius:8px;padding:13px 28px;">
        ' . esc_html( __( 'Set My Password', 'playard' ) ) . '
      </a>
    </div>

    <div style="background:#F8FAFC;border:1px solid #E2E8F0;border-radius:8px;padding:14px 18px;margin-bottom:24px;">
      <p style="margin:0;font-size:13px;color:#64748B;line-height:1.6;">'
		. esc_html( __( 'This link expires in 24 hours. You can also skip this and continue signing in with email codes — we\'ll always send one on request.', 'playard' ) ) .
		'</p>
    </div>

    <p style="font-size:12px;color:#94A3B8;margin:0;line-height:1.6;">'
		. esc_html( __( 'If you did not create this account, please ignore this email.', 'playard' ) ) .
		'</p>
  </div>

</div>
</body>
</html>';
	}

	// ── Welcome email ─────────────────────────────────────────────────────────────

	/**
	 * Sends a congratulations welcome email when a new customer creates an account.
	 * Hooked to 'playard_customer_registered'.
	 */
	public function send_welcome_email( int $customer_id ): void {
		if ( ! $this->is_enabled( 'account_created' ) ) {
			return;
		}

		global $wpdb;

		$customer = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT id, name, email FROM {$wpdb->prefix}playard_customers WHERE id = %d LIMIT 1",
				$customer_id
			)
		);

		if ( ! $customer || empty( $customer->email ) ) {
			return;
		}

		$facility  = playard_get_facility();
		$site_name = $facility ? sanitize_text_field( (string) $facility->name ) : (string) get_bloginfo( 'name' );

		$subject = sprintf(
			/* translators: %s: site/facility name */
			__( 'Welcome to %s! Your account is ready', 'playard' ),
			$site_name
		);

		$body = $this->build_welcome_email( (string) $customer->name, $site_name );

		$this->send( (string) $customer->email, $subject, $body, 0, 'account_created' );
	}

	/**
	 * Builds the congratulations HTML welcome email.
	 */
	private function build_welcome_email( string $name, string $site_name ): string {
		$greeting = $name
			/* translators: %s: customer first name */
			? sprintf( __( 'Hi %s,', 'playard' ), esc_html( $name ) )
			: __( 'Hi there,', 'playard' );

		return '<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#F8FAFC;font-family:Arial,sans-serif;">
<div style="max-width:520px;margin:0 auto;background:#FFFFFF;border-radius:12px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.06);">

  <div style="background:linear-gradient(135deg,#22C55E 0%,#16A34A 100%);padding:40px 32px;text-align:center;">
    <div style="width:72px;height:72px;background:rgba(255,255,255,0.2);border-radius:50%;display:inline-flex;align-items:center;justify-content:center;margin-bottom:16px;font-size:32px;line-height:72px;">
      &#127881;
    </div>
    <h1 style="margin:0;font-size:26px;font-weight:700;color:#FFFFFF;">' . esc_html( __( 'Welcome aboard!', 'playard' ) ) . '</h1>
    <p style="margin:8px 0 0;font-size:15px;color:rgba(255,255,255,0.88);">' . esc_html( $site_name ) . '</p>
  </div>

  <div style="padding:32px;">
    <p style="margin:0 0 14px;font-size:15px;color:#0F172A;line-height:1.6;">' . $greeting . '</p>
    <p style="margin:0 0 24px;font-size:14px;color:#475569;line-height:1.75;">'
		. esc_html(
			sprintf(
				/* translators: %s: site/facility name */
				__( 'Congratulations! Your account at %s has been created successfully. You\'re all set to start booking your favourite sports sessions.', 'playard' ),
				$site_name
			)
		)
		. '</p>

    <div style="background:#F0FDF4;border:1px solid #BBF7D0;border-radius:10px;padding:20px 24px;margin-bottom:28px;">
      <p style="margin:0 0 12px;font-size:12px;font-weight:700;color:#166534;text-transform:uppercase;letter-spacing:0.06em;">'
		. esc_html( __( 'What you can do now', 'playard' ) ) .
		'</p>
      <ul style="margin:0;padding:0 0 0 20px;font-size:14px;color:#15803D;line-height:2.2;">
        <li>' . esc_html( __( 'Browse available fields and time slots', 'playard' ) ) . '</li>
        <li>' . esc_html( __( 'Book a session in just a few clicks', 'playard' ) ) . '</li>
        <li>' . esc_html( __( 'Manage all your bookings in one place', 'playard' ) ) . '</li>
        <li>' . esc_html( __( 'View your booking history any time', 'playard' ) ) . '</li>
      </ul>
    </div>

    <div style="background:#F8FAFC;border:1px solid #E2E8F0;border-radius:8px;padding:14px 18px;margin-bottom:24px;">
      <p style="margin:0;font-size:13px;color:#475569;line-height:1.6;">
        <strong style="color:#0F172A;">' . esc_html( __( 'How to sign in:', 'playard' ) ) . '</strong><br>'
		. esc_html( __( 'Just enter your email on the sign-in page and we\'ll send you a secure 6-digit code — no password needed.', 'playard' ) ) .
		'</p>
    </div>

    <p style="font-size:12px;color:#94A3B8;margin:0;line-height:1.6;">'
		. esc_html( __( 'If you did not create this account, please ignore this email.', 'playard' ) ) .
		'</p>
  </div>

</div>
</body>
</html>';
	}
}
