<?php
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals
// phpcs:disable WordPress.Security.NonceVerification
// phpcs:disable WordPress.Security.ValidatedSanitizedInput
// phpcs:disable WordPress.WP.EnqueuedResourceParameters
// phpcs:disable WordPress.DB.PreparedSQL
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders
// phpcs:disable WordPress.DB.DirectDatabaseQuery
// phpcs:disable WordPress.DB.SlowDBQuery
// phpcs:disable PluginCheck.Security.DirectDB
// phpcs:disable PluginCheck.CodeAnalysis
// phpcs:disable WordPressVIPMinimum

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

/**
 * GT BOGO Engine — Customer Email System
 *
 * Sends white-labelled transactional emails to store customers.
 * All branding is pulled from WooCommerce / WordPress settings.
 * Store owners never expose GT BOGO Engine branding to their customers.
 *
 * Delivery: wp_mail() by default (uses store's own SMTP/mail config).
 * Extended delivery via Mailchimp/Klaviyo configured in Integrations page.
 */
class GT_BOGO_Email_System {
 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound

	private static $instance = null;

	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	private function __construct() {
		add_action( 'woocommerce_order_status_completed', array( $this, 'send_bogo_confirmation' ), 20 );
		add_action( 'woocommerce_order_status_processing', array( $this, 'send_bogo_confirmation' ), 20 );
		add_action( 'gtbogo_tier_upgraded', array( $this, 'send_tier_upgrade' ), 10, 3 );
		add_action( 'gtbogo_streak_bonus', array( $this, 'send_streak_bonus' ), 10, 3 );
		add_action( 'gtbogo_referral_success', array( $this, 'send_referral_reward' ), 10, 3 );
		add_action( 'gtbogo_vip_unlocked', array( $this, 'send_vip_unlocked' ), 10, 2 );
		add_action( 'gtbogo_free_gift_added', array( $this, 'send_free_gift' ), 10, 2 );
		add_action( 'gtbogo_deal_expiring', array( $this, 'send_deal_expiry' ), 10, 3 );
		add_action( 'gtbogo_winback_trigger', array( $this, 'send_winback' ), 10, 2 );

		// ── New hooks v3.11.0 ─────────────────────────────────────────────────
		add_action( 'woocommerce_order_status_completed', array( $this, 'send_new_customer_welcome' ), 25 );
		add_action( 'woocommerce_order_status_processing', array( $this, 'send_new_customer_welcome' ), 25 );
		add_action( 'gtbogo_post_purchase_followup', array( $this, 'send_post_purchase' ), 10, 1 );
		add_action( 'gtbogo_streak_at_risk', array( $this, 'send_streak_at_risk' ), 10, 3 );
		add_action( 'gtbogo_subscriber_at_risk', array( $this, 'send_subscriber_at_risk' ), 10, 2 );
		add_action( 'gtbogo_anniversary', array( $this, 'send_anniversary' ), 10, 3 );
		add_action( 'gtbogo_order_milestone', array( $this, 'send_order_milestone' ), 10, 3 );
		add_action( 'gtbogo_lapsed_reactivation', array( $this, 'send_lapsed_reactivation' ), 10, 4 );
		add_action( 'gtbogo_deal_unlocked_email', array( $this, 'send_deal_unlocked' ), 10, 5 );
			// Birthday + flash sale hooks (GAP-A fix v5.0.0)
			add_action( 'gtbogo_birthday_deal', array( $this, 'send_birthday_deal' ), 10, 1 );
			add_action( 'gtbogo_birthday_triggered', array( $this, 'send_birthday_deal' ), 10, 1 );
			add_action( 'gtbogo_flash_sale_alert', array( $this, 'send_flash_sale_alert' ), 10, 3 );
		// Wave 5 email hooks v3.23.3
		add_action( 'gtbogo_send_loyalty_nudge_email', array( $this, 'dispatch_loyalty_nudge' ), 10, 3 );
		add_action( 'gtbogo_bundle_recommendation', array( $this, 'send_bundle_recommendation' ), 10, 2 );
		add_action( 'gtbogo_intent_based_offer', array( $this, 'send_intent_based_offer' ), 10, 4 );

		// Schedule 3-day post-purchase follow-up
		add_action(
			'woocommerce_order_status_completed',
			function ( int $order_id ) {
				if ( ! wp_next_scheduled( 'gtbogo_post_purchase_followup', array( $order_id ) ) ) {
					wp_schedule_single_event( time() + ( 3 * DAY_IN_SECONDS ), 'gtbogo_post_purchase_followup', array( $order_id ) );
				}
			},
			30
		);
	}

	// ── Branding helpers ──────────────────────────────────────────────────────

	private function get_store_name() {
		return get_bloginfo( 'name' ) ?: get_option( 'woocommerce_email_from_name', get_bloginfo( 'name' ) );
	}

	private function get_store_email() {
		return get_option( 'woocommerce_email_from_address', get_option( 'admin_email' ) );
	}

	private function get_store_logo_url(): string {
		$custom = get_option( 'gtbogo_email_logo_url', '' );
		if ( ! empty( $custom ) ) {
			return esc_url( $custom );
		}
		$site_logo = get_option( 'site_icon' );
		if ( $site_logo ) {
			$url = wp_get_attachment_image_url( $site_logo, 'thumbnail' );
			if ( $url ) {
				return $url;
			}
		}
		return 'https://graphictshirts.shop/bogo/icon-128x128.png';
	}


	private function get_store_url() {
		return home_url();
	}

	private static function get_themes(): array {
		return array(
			'midnight' => array(
				'name'   => 'Midnight',
				'bg1'    => '#070C1C',
				'bg2'    => '#050a16',
				'bg3'    => '#03060f',
				'accent' => '#E10000',
				'border' => '#0f1a30',
				'muted'  => '#4a6a8a',
			),
			'ocean'    => array(
				'name'   => 'Ocean',
				'bg1'    => '#040d1a',
				'bg2'    => '#030b16',
				'bg3'    => '#020810',
				'accent' => '#0ea5e9',
				'border' => '#0a2040',
				'muted'  => '#3a6080',
			),
			'forest'   => array(
				'name'   => 'Forest',
				'bg1'    => '#04100a',
				'bg2'    => '#030d08',
				'bg3'    => '#020905',
				'accent' => '#16a34a',
				'border' => '#0a2814',
				'muted'  => '#2a5a3a',
			),
			'royal'    => array(
				'name'   => 'Royal',
				'bg1'    => '#0a0714',
				'bg2'    => '#080510',
				'bg3'    => '#05030c',
				'accent' => '#7c3aed',
				'border' => '#1a0d40',
				'muted'  => '#4a3070',
			),
			'obsidian' => array(
				'name'   => 'Obsidian',
				'bg1'    => '#0a0a08',
				'bg2'    => '#070706',
				'bg3'    => '#040403',
				'accent' => '#d97706',
				'border' => '#1a1a14',
				'muted'  => '#4a4a38',
			),
			'ember'    => array(
				'name'   => 'Ember',
				'bg1'    => '#110804',
				'bg2'    => '#0d0603',
				'bg3'    => '#090402',
				'accent' => '#ea580c',
				'border' => '#301408',
				'muted'  => '#5a3020',
			),
			'slate'    => array(
				'name'   => 'Slate',
				'bg1'    => '#080d14',
				'bg2'    => '#060a10',
				'bg3'    => '#04070c',
				'accent' => '#06b6d4',
				'border' => '#0d2030',
				'muted'  => '#2a4a5a',
			),
			'rose'     => array(
				'name'   => 'Rose',
				'bg1'    => '#120608',
				'bg2'    => '#0e0406',
				'bg3'    => '#0a0304',
				'accent' => '#e11d48',
				'border' => '#300a14',
				'muted'  => '#5a2030',
			),
		);
	}

	private function get_theme(): array {
		$themes = self::get_themes();
		$key    = get_option( 'gtbogo_email_theme', 'midnight' );
		$theme  = $themes[ $key ] ?? $themes['midnight'];
		// Allow per-store accent override within the theme
		$custom = sanitize_hex_color( get_option( 'gtbogo_email_accent_color', '' ) );
		if ( $custom ) {
			$theme['accent'] = $custom;
		}
		return $theme;
	}

	private function get_store_color(): string {
		return $this->get_theme()['accent'];
	}

	private function emails_enabled(): bool {
		// Must be explicitly enabled by store owner
		$s = get_option( 'gtbogo_settings', array() );
		if ( ! get_option( 'gtbogo_customer_emails_enabled', false ) && empty( $s['customer_emails_enabled'] ) ) {
			return false;
		}

		// Guard: store must have a real name configured (not default WordPress)
		$store_name = get_bloginfo( 'name' );
		if ( empty( $store_name ) || in_array(
			strtolower( trim( $store_name ) ),
			array( 'wordpress', 'my wordpress blog', 'my blog', 'just another wordpress site', '' ),
			true
		) ) {
			return false;
		}

		// Guard: store must have a from email that is not the WordPress default
		$from_email = get_option( 'woocommerce_email_from_address', get_option( 'admin_email', '' ) );
		if ( empty( $from_email ) || strpos( $from_email, 'WordPress' ) !== false ) {
			return false;
		}

		return true;
	}

	/**
	 * Check if store is ready to send customer emails.
	 * Used by admin to show a setup prompt if emails are enabled but store
	 * info has not been configured yet.
	 */
	public function get_readiness_status(): array {
		$issues     = array();
		$store_name = get_bloginfo( 'name' );
		if ( empty( $store_name ) || in_array(
			strtolower( trim( $store_name ) ),
			array( 'wordpress', 'my wordpress blog', 'my blog', 'just another wordpress site', '' ),
			true
		) ) {
			$issues[] = 'Store name is not configured — go to Settings > General and set your site name.';
		}
		$logo        = get_option( 'site_icon' );
		$custom_logo = get_option( 'gtbogo_email_logo_url', '' );
		if ( ! $logo && ! $custom_logo ) {
			$issues[] = 'No store logo set — go to Appearance > Customize > Site Identity to add a site icon, or set a logo URL in GT BOGO Engine > Settings.';
		}
		$from_email = get_option( 'woocommerce_email_from_address', '' );
		if ( empty( $from_email ) ) {
			$issues[] = 'WooCommerce from email not set — go to WooCommerce > Settings > Emails.';
		}
		return array(
			'ready'  => empty( $issues ),
			'issues' => $issues,
		);
	}

	// ── Email template engine ─────────────────────────────────────────────────

	private function wrap( string $body, string $preheader = '' ): string {
		$year  = gmdate( 'Y' );
		$theme = $this->get_theme();
		$bg1   = $theme['bg1'];
		$bg2   = $theme['bg2'];
		$bg3   = $theme['bg3'];
		$acc   = $theme['accent'];
		$bdr   = $theme['border'];
		$mut   = $theme['muted'];
		$logo  = $this->get_store_logo_url();
		$sname = $this->get_store_name();
		$surl  = $this->get_store_url();
		ob_start();
		echo '<!DOCTYPE html><html lang="en">';
		echo '<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>' . esc_html( $sname ) . '</title></head>';
		echo '<body style="margin:0;padding:0;background-color:' . esc_attr( $bg1 ) . ';">';
		echo '<table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="' . esc_attr( $bg1 ) . '" style="background-color:' . esc_attr( $bg1 ) . ';padding:0;">';
		echo '<tr><td align="center" style="padding:32px 16px 48px 16px;">';
		echo '<table width="620" cellpadding="0" cellspacing="0" border="0" style="max-width:620px;width:100%;background-color:' . esc_attr( $bg1 ) . ';">';
		echo '<tr><td><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="height:5px;background:' . esc_attr( $acc ) . ';font-size:0;line-height:0;">&nbsp;</td></tr></table></td></tr>';
		echo '<tr><td style="background-color:' . esc_attr( $bg1 ) . ';padding:36px 48px 28px 48px;border-left:1px solid ' . esc_attr( $bdr ) . ';border-right:1px solid ' . esc_attr( $bdr ) . ';text-align:center;">';
		echo '<a href="' . esc_url( $surl ) . '" style="text-decoration:none;">';
		echo '<img src="' . esc_url( $logo ) . '" width="72" height="72" alt="' . esc_attr( $sname ) . '" style="display:block;margin:0 auto 14px;border-radius:10px;" />';
		echo '<p style="margin:0;font-family:Georgia,\'Times New Roman\',serif;font-size:20px;font-weight:700;color:#ffffff;letter-spacing:0.5px;line-height:1;">' . esc_html( $sname ) . '</p>';
		echo '</a></td></tr>';
		echo '<tr><td style="padding:0 48px;border-left:1px solid ' . esc_attr( $bdr ) . ';border-right:1px solid ' . esc_attr( $bdr ) . ';"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="height:1px;background:' . esc_attr( $acc ) . ';font-size:0;line-height:0;">&nbsp;</td></tr></table></td></tr>';
		echo '<tr><td style="background-color:' . esc_attr( $bg1 ) . ';padding:44px 48px 40px 48px;border-left:1px solid ' . esc_attr( $bdr ) . ';border-right:1px solid ' . esc_attr( $bdr ) . ';">';
		echo wp_kses_post( $body );
		echo '</td></tr>';
		echo '<tr><td style="border-left:1px solid ' . esc_attr( $bdr ) . ';border-right:1px solid ' . esc_attr( $bdr ) . ';"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="height:1px;background:' . esc_attr( $acc ) . ';font-size:0;line-height:0;">&nbsp;</td></tr></table></td></tr>';
		echo '<tr><td style="background-color:' . esc_attr( $bg3 ) . ';padding:24px 48px 28px 48px;border-left:1px solid ' . esc_attr( $bdr ) . ';border-right:1px solid ' . esc_attr( $bdr ) . ';text-align:center;">';
		echo '<a href="' . esc_url( $surl ) . '" style="font-family:\'Helvetica Neue\',Helvetica,Arial,sans-serif;font-size:13px;font-weight:600;color:' . esc_attr( $acc ) . ';text-decoration:none;">' . esc_html( $sname ) . '</a>';
		echo '<p style="margin:8px 0 0 0;font-family:\'Helvetica Neue\',Helvetica,Arial,sans-serif;font-size:11px;color:' . esc_attr( $mut ) . ';">&copy; ' . esc_html( $year ) . ' ' . esc_html( $sname ) . '. All rights reserved.</p>';
		echo '<p style="margin:6px 0 0 0;font-family:\'Helvetica Neue\',Helvetica,Arial,sans-serif;font-size:10px;color:' . esc_attr( $bdr ) . ';">Powered by <a href="https://graphictshirts.shop/bogo" style="color:' . esc_attr( $bdr ) . ';text-decoration:none;">GT BOGO Engine</a></p>';
		echo '</td></tr>';
		echo '<tr><td><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="height:5px;background:' . esc_attr( $acc ) . ';font-size:0;line-height:0;">&nbsp;</td></tr></table></td></tr>';
		echo '</table></td></tr></table></body></html>';
		return ob_get_clean();
	}

	private function heading( string $text ): string {
		return '<h2 style="margin:0 0 16px;color:#f1f5f9;font-size:22px;font-weight:700;">' . esc_html( $text ) . '</h2>';
	}

	private function para( string $text ): string {
		return '<p style="margin:0 0 16px;color:#94a3b8;font-size:15px;line-height:1.7;">' . esc_html( $text ) . '</p>';
	}

	private function highlight_box( string $label, string $value ): string {
		$theme  = $this->get_theme();
		$accent = $theme['accent'];
		$bg2    = $theme['bg2'];
		$bdr    = $theme['border'];
		return '<div style="background:' . esc_html( $bg2 ) . ';border:1px solid ' . esc_html( $bdr ) . ';border-left:4px solid ' . esc_html( $accent ) . ';border-radius:6px;padding:18px 22px;margin:0 0 20px;">
            <p style="margin:0 0 4px;color:#64748b;font-size:11px;text-transform:uppercase;letter-spacing:0.08em;font-weight:600;">' . esc_html( $label ) . '</p>
            <p style="margin:0;color:#f1f5f9;font-size:24px;font-weight:800;">' . esc_html( $value ) . '</p>
        </div>';
	}

	private function cta_button( string $text, string $url ): string {
		$accent = $this->get_store_color();
		return '<div style="text-align:center;margin:28px 0;">
            <a href="' . esc_url( $url ) . '" style="display:inline-block;background:' . esc_html( $accent ) . ';color:#ffffff;padding:14px 32px;border-radius:999px;text-decoration:none;font-size:15px;font-weight:700;">' . esc_html( $text ) . '</a>
        </div>';
	}

	private function step( string $num, string $title, string $body ): string {
		$theme  = $this->get_theme();
		$accent = $theme['accent'];
		$bg2    = $theme['bg2'];
		return '<div style="margin:0 0 12px;padding:16px;background:' . esc_html( $bg2 ) . ';border-radius:6px;border-left:3px solid ' . esc_html( $accent ) . ';">
            <p style="margin:0 0 4px;color:#f1f5f9;font-size:14px;font-weight:700;">' . esc_html( $num ) . '. ' . esc_html( $title ) . '</p>
            <p style="margin:0;color:#94a3b8;font-size:13px;">' . esc_html( $body ) . '</p>
        </div>';
	}

	/**
	 * Check dedup and log send via GT_BOGO_Email_Dedup_Engine.
	 * Returns false if email is blocked by cooldown.
	 */
	private function dedup_check( int $customer_id, string $template, ?int $rule_id = null ): bool {
		// Bar/email coordination: suppress promotional emails when bar was recently shown
		$bar_suppress_types = array( 'deal_expiry', 'cart_nudge', 'threshold_nudge', 'winback', 'flash_sale_alert' );
		if ( $customer_id > 0 && in_array( $template, $bar_suppress_types, true ) && get_transient( 'gtbogo_bar_shown_' . $customer_id ) ) {
			return false;
		}
		if ( ! class_exists( 'GT_BOGO_Email_Dedup_Engine' ) ) {
			return true;
		}
		return GT_BOGO_Email_Dedup_Engine::can_send( $customer_id, $template, $rule_id );
	}

	private function send( string $to, string $subject, string $html ): bool {
		$from_name  = $this->get_store_name();
		$from_email = $this->get_store_email();
		$headers    = array(
			'Content-Type: text/html; charset=UTF-8',
			'From: ' . esc_html( $from_name ) . ' <' . esc_html( $from_email ) . '>',
		);
		return (bool) wp_mail( $to, $subject, $html, $headers );
	}

	// ── Email senders ─────────────────────────────────────────────────────────

	/**
	 * 1. BOGO Deal Confirmation — sent when order completes with BOGO applied
	 */
	public function send_bogo_confirmation( int $order_id ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return;
		}
		if ( ! $order->get_meta( '_gtbogo_applied', true ) ) {
			return;
		}
		if ( $order->get_meta( '_gtbogo_confirm_email_sent', true ) ) {
			return;
		}
		$_cid = (int) $order->get_customer_id();
		if ( ! $this->dedup_check( $_cid, 'bogo_confirmation' ) ) {
			return;
		}

		$name    = $order->get_billing_first_name();
		$savings = (float) $order->get_meta( '_gtbogo_savings', true );
		$store   = $this->get_store_name();
		$subject = $store . ': You saved ' . wc_price( $savings ) . ' on your order';

		// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'bogo_confirmation',
				array(
					'name'    => $name,
					'store'   => $store,
					'dynamic' => wc_price( $savings ),
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? ( 'Your savings are confirmed, ' . esc_html( $name ) . '.' ) );
		$body .= $this->para( 'Your deal was applied to your order at ' . esc_html( $store ) . '. Your savings are locked in.' );
		$body .= $this->highlight_box( 'Total Savings', wc_price( $savings ) );
		$body .= $this->para( 'Your order is being processed and you will receive a shipping confirmation shortly.' );
		$body .= $this->cta_button( $_ic['cta'] ?? 'View Your Order', $order->get_view_order_url() );
		$this->apply_custom_template( 'bogo_confirmation', $subject, $body );
		$this->send( $order->get_billing_email(), $subject, $this->wrap( $body, 'You saved ' . wc_price( $savings ) . ' on your order!' ) );
		$order->update_meta_data( '_gtbogo_confirm_email_sent', '1' );
		$order->save_meta_data();
	}

	/**
	 * 2. Loyalty Tier Upgrade
	 */
	public function send_tier_upgrade( int $user_id, string $old_tier, string $new_tier ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'tier_upgrade' ) ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': You\'ve reached ' . esc_html( $new_tier ) . ' status';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'tier_upgrade',
				array(
					'name'    => $user->first_name,
					'store'   => $store,
					'dynamic' => $new_tier,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Congratulations, ' . $user->first_name . '!' );
		$body .= $this->para( 'You\'ve unlocked <strong>' . esc_html( $new_tier ) . '</strong> loyalty status at ' . esc_html( $store ) . '. Your loyalty is rewarded with exclusive perks starting immediately.' );
		$body .= $this->highlight_box( 'Your New Status', esc_html( $new_tier ) );
		$body .= $this->para( 'Log in and shop to start enjoying your ' . esc_html( $new_tier ) . ' member benefits.' );
		$body .= $this->cta_button( 'Shop Now', $this->get_store_url() );

		$this->apply_custom_template( 'tier_upgrade', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body, 'You\'ve unlocked ' . esc_html( $new_tier ) . ' status!' ) );
	}

	/**
	 * 3. Streak Bonus Applied
	 */
	public function send_streak_bonus( int $user_id, int $streak, string $reward ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'streak_bonus' ) ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': Your ' . esc_html( $streak ) . '-order streak reward is ready';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'streak_bonus',
				array(
					'name'    => $user->first_name,
					'store'   => $store,
					'dynamic' => (string) $streak,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Your streak reward is here.' );
		$body .= $this->para( 'You\'ve placed <strong>' . (int) $streak . ' orders</strong> in a row at ' . esc_html( $store ) . '. Here\'s your reward:' );
		$body .= $this->highlight_box( 'Your Reward', esc_html( $reward ) );
		$body .= $this->para( 'Your reward has been applied to your account and is ready to use on your next order.' );
		$body .= $this->cta_button( 'Claim Your Reward', $this->get_store_url() );

		$this->apply_custom_template( 'streak_bonus', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body, 'Your streak reward is ready' ) );
	}

	/**
	 * 4. Referral Reward Earned
	 */
	public function send_referral_reward( int $user_id, string $referred_name, string $reward ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'referral_reward' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': You earned a referral reward';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'referral_reward',
				array(
					'name'    => $user->first_name,
					'store'   => $store,
					'dynamic' => $referred_name,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Your referral reward is ready' );
		$body .= $this->para( esc_html( $referred_name ) . ' just made their first purchase at ' . esc_html( $store ) . ' using your referral. Here\'s your reward:' );
		$body .= $this->highlight_box( 'Referral Reward', esc_html( $reward ) );
		$body .= $this->para( 'Your reward is ready to use on your next order. Keep sharing and keep earning.' );
		$body .= $this->cta_button( 'Shop Now', $this->get_store_url() );

		$this->apply_custom_template( 'referral_reward', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body, 'Your referral reward is ready!' ) );
	}

	/**
	 * 5. VIP Status Unlocked
	 */
	public function send_vip_unlocked( int $user_id, string $tier ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'vip_unlocked' ) ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': VIP status unlocked';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'vip_unlocked',
				array(
					'name'    => $user->first_name,
					'store'   => $store,
					'dynamic' => $tier,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Welcome to VIP, ' . $user->first_name . '.' );
		$body .= $this->para( 'You\'ve earned <strong>' . esc_html( $tier ) . ' VIP status</strong> at ' . esc_html( $store ) . '. From now on, exclusive deals and early access are reserved for you.' );
		$body .= $this->highlight_box( 'VIP Tier', esc_html( $tier ) );
		$body .= $this->para( 'Your VIP perks are active immediately. Log in to see your exclusive offers.' );
		$body .= $this->cta_button( 'Access VIP Deals', $this->get_store_url() );

		$this->apply_custom_template( 'vip_unlocked', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body, 'Your VIP status is now active' ) );
	}

	/**
	 * 6. Free Gift Unlocked
	 */
	public function send_free_gift( int $order_id, string $gift_name ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$order = wc_get_order( $order_id );
		$_cid  = $order ? (int) $order->get_customer_id() : 0;
		if ( ! $this->dedup_check( $_cid, 'free_gift', null ) ) {
			return;
		}
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return;
		}
		$store   = $this->get_store_name();
		$name    = $order->get_billing_first_name();
		$subject = $store . ': A free gift has been added to your order';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'free_gift',
				array(
					'name'    => $name,
					'store'   => $store,
					'dynamic' => $gift_name,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'A gift for you, ' . esc_html( $name ) . '.' );
		$body .= $this->para( 'You\'ve qualified for a free gift on your order at ' . esc_html( $store ) . ':' );
		$body .= $this->highlight_box( 'Your Free Gift', esc_html( $gift_name ) );
		$body .= $this->para( 'Your gift has been added to your order automatically. No action needed.' );
		$body .= $this->cta_button( 'View Your Order', $order->get_view_order_url() );

		$this->apply_custom_template( 'free_gift', $subject, $body );
		$this->send( $order->get_billing_email(), $subject, $this->wrap( $body, 'Your free item has been applied' ) );
	}

	/**
	 * 7. Deal Expiry Warning
	 */
	public function send_deal_expiry( string $email, string $deal_name, string $expiry_date ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$cid = (int) email_exists( $email );
		if ( ! $this->dedup_check( $cid, 'deal_expiry' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': Your deal expires soon';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'deal_expiry',
				array(
					'name'    => '',
					'store'   => $store,
					'dynamic' => $expiry_date,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Your deal is expiring soon.' );
		$body .= $this->para( 'Don\'t miss out — your <strong>' . esc_html( $deal_name ) . '</strong> deal at ' . esc_html( $store ) . ' expires on <strong>' . esc_html( $expiry_date ) . '</strong>.' );
		$body .= $this->highlight_box( 'Deal Expires', esc_html( $expiry_date ) );
		$body .= $this->para( 'Complete your purchase before the deal ends to lock in your savings.' );
		$body .= $this->cta_button( 'Shop Before It Expires', $this->get_store_url() );

		$this->apply_custom_template( 'deal_expiry', $subject, $body );
		$this->send( $email, $subject, $this->wrap( $body, 'Your deal expires on ' . $expiry_date ) );
	}

	/**
	 * 8. Winback Campaign
	 */
	public function send_winback( string $email, string $first_name ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$cid = email_exists( $email ) ? (int) email_exists( $email ) : 0;
		if ( ! $this->dedup_check( $cid, 'winback' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': We\'ve saved something for you';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'winback',
				array(
					'name'  => $first_name,
					'store' => $store,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'We miss you, ' . esc_html( $first_name ) . '.' );
		$body .= $this->para( 'It\'s been a while since your last order at ' . esc_html( $store ) . '. We\'ve set aside an exclusive deal just for you — no strings attached.' );
		$body .= $this->para( 'Come back and take a look. Your deal is waiting.' );
		$body .= $this->cta_button( 'Claim Your Deal', $this->get_store_url() );

		$this->apply_custom_template( 'winback', $subject, $body );
		$this->send( $email, $subject, $this->wrap( $body, 'An exclusive deal is waiting for you.' ) );
	}

	/**
	 * 9. Cart Abandonment Nudge (called by Cart Abandonment superpower)
	 */
	public function send_cart_nudge( string $email, string $first_name, string $deal_name, string $cart_url ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$cid = email_exists( $email ) ? (int) email_exists( $email ) : 0;
		if ( ! $this->dedup_check( $cid, 'cart_nudge' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': Your deal is still waiting in your cart';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'cart_nudge',
				array(
					'name'    => $first_name,
					'store'   => $store,
					'dynamic' => $deal_name,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'You left something behind.' );
		$body .= $this->para( 'Hi ' . esc_html( $first_name ) . ', your cart at ' . esc_html( $store ) . ' still has an active deal:' );
		$body .= $this->highlight_box( 'Active Deal', esc_html( $deal_name ) );
		$body .= $this->para( 'Your deal is reserved but won\'t last forever. Complete your purchase to lock in your savings.' );
		$body .= $this->cta_button( 'Return to Cart', $cart_url );

		$this->apply_custom_template( 'cart_nudge', $subject, $body );
		$this->send( $email, $subject, $this->wrap( $body, 'Your deal is still waiting.' ) );
	}

	/**
	 * 10. Threshold Almost Reached
	 */
	public function send_threshold_nudge( string $email, string $first_name, string $amount_needed, string $reward ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$cid = email_exists( $email ) ? (int) email_exists( $email ) : 0;
		if ( ! $this->dedup_check( $cid, 'threshold_nudge' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': You\'re ' . esc_html( $amount_needed ) . ' away from a reward';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'threshold_nudge',
				array(
					'name'    => $first_name,
					'store'   => $store,
					'dynamic' => $amount_needed,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Almost there, ' . esc_html( $first_name ) . '.' );
		$body .= $this->para( 'You\'re just <strong>' . esc_html( $amount_needed ) . '</strong> away from unlocking a reward at ' . esc_html( $store ) . ':' );
		$body .= $this->highlight_box( 'Reward Waiting', esc_html( $reward ) );
		$body .= $this->para( 'Add a little more to your cart and the reward is yours automatically.' );
		$body .= $this->cta_button( 'Complete Your Order', $this->get_store_url() );

		$this->apply_custom_template( 'threshold_nudge', $subject, $body );
		$this->send( $email, $subject, $this->wrap( $body, 'You\'re ' . esc_html( $amount_needed ) . ' away from a reward.' ) );
	}

	/**
	 * Send referral progress email — fires after every successful referral
	 * Shows progress toward reward and celebrates when threshold is reached
	 */
	public function send_referral_progress( int $user_id, int $referral_count ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! get_option( 'gtbogo_referral_enabled', false ) ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'referral_progress' ) ) {
			return;
		}

		$needed  = (int) get_option( 'gtbogo_referral_reward_count', 3 );
		$label   = get_option( 'gtbogo_referral_reward_label', '' );
		$store   = $this->get_store_name();
		$reached = ( $referral_count >= $needed );

		if ( $reached ) {
			// Generate WooCommerce coupon automatically
			$coupon_code = $this->generate_referral_coupon( $user_id );
			$subject     = $store . ': Your referral reward is ready';
			$body_inner  = $this->heading( 'Your reward is here, ' . $user->first_name . '.' );
			$body_inner .= $this->para( 'You have successfully referred <strong>' . (int) $referral_count . '</strong> customers to ' . esc_html( $store ) . '. Your reward has been applied.' );
			$body_inner .= $this->highlight_box( 'Your Reward', esc_html( $label ?: ( get_option( 'gtbogo_referral_reward_value', 20 ) . '% off your next order' ) ) );
			if ( $coupon_code ) {
				$body_inner .= $this->highlight_box( 'Your Coupon Code', esc_html( $coupon_code ) );
				$body_inner .= $this->para( 'Enter this code at checkout to claim your reward. Single use, applied to your next order.' );
			}
			$body_inner .= $this->cta_button( 'Shop Now', $this->get_store_url() );
			$_cs         = get_option( 'gtbogo_email_referral_progress_subject', '' );
			if ( $_cs ) {
				$subject = sanitize_text_field( $_cs );
			} $_cb = get_option( 'gtbogo_email_referral_progress_body', '' );
			if ( $_cb ) {
				$body_inner = $this->para( wp_kses_post( $_cb ) );
			} unset( $_cs, $_cb );
			$this->send( $user->user_email, $subject, $this->wrap( $body_inner ) );
			// Reset referral count for next cycle
			update_user_meta( $user_id, '_gtbogo_referral_count', 0 );
			update_user_meta( $user_id, '_gtbogo_referral_cycles', (int) get_user_meta( $user_id, '_gtbogo_referral_cycles', true ) + 1 );
		} else {
			$remaining   = $needed - $referral_count;
			$subject     = $store . ': ' . esc_html( $referral_count ) . ' of ' . esc_html( $needed ) . ' referrals — ' . esc_html( $remaining ) . ' to go';
			$body_inner  = $this->heading( 'Great work, ' . $user->first_name . '.' );
			$body_inner .= $this->para( 'You just earned a referral credit at ' . esc_html( $store ) . '. Here is your progress:' );
			$body_inner .= $this->highlight_box( 'Referrals Completed', $referral_count . ' of ' . $needed );
			// Visual progress bar
			$pct         = $needed > 0 ? round( ( $referral_count / $needed ) * 100 ) : 0;
			$accent      = $this->get_store_color();
			$body_inner .= '<div style="background:rgba(255,255,255,0.04);border:1px solid #1e293b;border-radius:8px;padding:16px;margin:0 0 20px;">
                <div style="display:flex;justify-content:space-between;margin-bottom:8px;">
                    <span style="color:#94a3b8;font-size:13px;">' . esc_html__( 'Progress to reward', 'gt-bogo-engine' ) . '</span>
                    <span style="color:#f1f5f9;font-size:13px;font-weight:700;">' . esc_html( $pct ) . '%</span>
                </div>
                <div style="height:8px;background:rgba(255,255,255,0.08);border-radius:4px;overflow:hidden;">
                    <div style="height:100%;width:' . esc_html( $pct ) . '%;background:' . esc_html( $accent ) . ';border-radius:4px;"></div>
                </div>
                <p style="margin:10px 0 0;color:#94a3b8;font-size:12px;">' . sprintf( // translators: %1$d is the count, %2$s is the reward label.
						esc_html__( '%1$d more referral(s) to unlock: %2$s', 'gt-bogo-engine' ),
						$remaining,
						esc_html( $label ?: 'your reward' )
					) . '</p>
            </div>';
			$body_inner .= $this->para( 'Keep sharing your referral link to earn your reward faster.' );
			$body_inner .= $this->cta_button( 'Share Your Link', $this->get_store_url() );
			$_cs         = get_option( 'gtbogo_email_referral_progress_subject', '' );
			if ( $_cs ) {
				$subject = sanitize_text_field( $_cs );
			} $_cb = get_option( 'gtbogo_email_referral_progress_body', '' );
			if ( $_cb ) {
				$body_inner = $this->para( wp_kses_post( $_cb ) );
			} unset( $_cs, $_cb );
			$this->send( $user->user_email, $subject, $this->wrap( $body_inner ) );
		}
	}

	/**
	 * Generate a WooCommerce coupon for referral reward
	 */
	private function generate_referral_coupon( int $user_id ): string {
		if ( ! class_exists( 'WC_Coupon' ) ) {
			return '';
		}
		$type  = get_option( 'gtbogo_referral_reward_type', 'percent' );
		$value = (float) get_option( 'gtbogo_referral_reward_value', 20 );
		$user  = get_userdata( $user_id );
		if ( ! $user ) {
			return '';
		}
		$code    = 'GTREF-' . strtoupper( substr( md5( $user_id . time() ), 0, 8 ) );
		$wc_type = $type === 'fixed' ? 'fixed_cart' : 'percent';
		$coupon  = new WC_Coupon();
		$coupon->set_code( $code );
		$coupon->set_discount_type( $wc_type );
		$coupon->set_amount( $value );
		$coupon->set_usage_limit( 1 );
		$coupon->set_usage_limit_per_user( 1 );
		$coupon->set_email_restrictions( array( $user->user_email ) );
		$coupon->set_individual_use( true );
		$coupon->set_description( 'GT BOGO Referral Reward — auto-generated' );
		$coupon->save();
		return $code;
	}

	/**
	 * Generic send — used by other classes
	 */
	public function send_email( string $to, string $template, array $data = array() ): bool {
		switch ( $template ) {
			case 'bogo-confirmation':
				if ( isset( $data['order_id'] ) ) {
					$this->send_bogo_confirmation( (int) $data['order_id'] );
				}
				return true;
			case 'cart-nudge':
				$this->send_cart_nudge(
					$to,
					$data['first_name'] ?? '',
					$data['deal_name'] ?? '',
					$data['cart_url'] ?? home_url( '/cart' )
				);
				return true;
			case 'threshold-nudge':
				$this->send_threshold_nudge(
					$to,
					$data['first_name'] ?? '',
					$data['amount_needed'] ?? '',
					$data['reward'] ?? ''
				);
				return true;
			case 'winback':
				$this->send_winback( $to, $data['first_name'] ?? '' );
				return true;
			default:
				return false;
		}
	}

	// ══════════════════════════════════════════════════════════════════════════
	// SYSTEM B ADDITIONS — Store Customer Emails (added v3.11.0)
	// ══════════════════════════════════════════════════════════════════════════

	/**
	 * 16. Order Milestone — fires at 3rd, 5th, 10th, 15th, 20th, 25th orders.
	 * Triggered by: order_3, order_5, order_10, order_15, order_20, order_25 roles.
	 */
	public function send_order_milestone( int $user_id, int $order_count, string $reward ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'order_milestone' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': ' . esc_html( $store ) . ' — order ' . esc_html( $order_count ) . ' milestone';

		$ordinal = $this->ordinal( $order_count );
				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'order_milestone',
				array(
					'name'    => $user->first_name,
					'store'   => $store,
					'dynamic' => (string) $order_count,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Your ' . esc_html( $ordinal ) . ' order.' );
		$body .= $this->para( 'You have placed your ' . esc_html( $ordinal ) . ' order with ' . esc_html( $store ) . '. That is worth celebrating — here is something for you:' );
		$body .= $this->highlight_box( 'Your Milestone Reward', esc_html( $reward ) );
		$body .= $this->para( 'Your reward is applied to your account and ready to use on your next order. Thank you for being a loyal customer.' );
		$body .= $this->cta_button( 'Shop Now', $this->get_store_url() );

		$this->apply_custom_template( 'order_milestone', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body ) );
	}

	/**
	 * 17. Streak At-Risk — fires when a customer is at risk of losing their
	 * purchase streak (e.g. no order placed within the streak window).
	 * Triggered by: streak_at_risk role.
	 */
	public function send_streak_at_risk( int $user_id, int $streak, string $deadline ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'streak_at_risk' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': Your ' . esc_html( $streak ) . '-order streak is at risk';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'streak_at_risk',
				array(
					'name'      => $user->first_name,
					'store'     => $store,
					'dynamic'   => (string) $streak,
					'dynamic_2' => $deadline,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Your streak ends ' . esc_html( $deadline ) . '.' );
		$body .= $this->para( 'You are on a <strong>' . (int) $streak . '-order streak</strong> at ' . esc_html( $store ) . '. Place one more order by <strong>' . esc_html( $deadline ) . '</strong> to keep it alive and protect your rewards.' );
		$body .= $this->highlight_box( 'Streak', $streak . ' orders — place one more to continue' );
		$body .= $this->para( 'Your streak rewards reset if you miss the window. A single order is all it takes.' );
		$body .= $this->cta_button( 'Place an Order', $this->get_store_url() );

		$this->apply_custom_template( 'streak_at_risk', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body ) );
	}

	/**
	 * 18. Subscriber At-Risk — fires when a subscriber's renewal is approaching
	 * or their engagement has dropped below the at-risk threshold.
	 * Triggered by: subscriber_at_risk role.
	 */
	public function send_subscriber_at_risk( int $user_id, string $offer ): void {
		// Callers may pass user_email as $offer — normalise to offer string
		if ( false !== strpos( $offer, '@' ) ) {
			$offer = __( 'An exclusive renewal offer', 'gt-bogo-engine' );
		}
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'subscriber_at_risk' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': We have something reserved for you';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'subscriber_at_risk',
				array(
					'name'  => $user->first_name,
					'store' => $store,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Stay with us, ' . $user->first_name . '.' );
		$body .= $this->para( 'We noticed you have not been active lately at ' . esc_html( $store ) . '. We want to keep you — so we have set aside an exclusive retention offer:' );
		$body .= $this->highlight_box( 'Exclusive Offer', esc_html( $offer ) );
		$body .= $this->para( 'This offer is reserved for you personally. It will not appear anywhere else on the site.' );
		$body .= $this->cta_button( 'Claim Your Offer', $this->get_store_url() );

		$this->apply_custom_template( 'subscriber_at_risk', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body ) );
	}

	/**
	 * 19. Anniversary — fires on the customer's one-year (and subsequent)
	 * anniversary as a registered customer.
	 * Triggered by: anniversary_member role.
	 */
	public function send_anniversary( int $user_id, int $years, string $gift ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'anniversary' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$label   = $years === 1 ? 'one year' : $years . ' years';
		$subject = $store . ': ' . esc_html( $label ) . ' with ' . esc_html( $store ) . ' — thank you';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'anniversary',
				array(
					'name'    => $user->first_name,
					'store'   => $store,
					'dynamic' => (string) $years,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? $label . ' with us.' );
		$body .= $this->para( 'It has been <strong>' . esc_html( $label ) . '</strong> since you joined ' . esc_html( $store ) . '. That deserves recognition — here is our thank you:' );
		$body .= $this->highlight_box( 'Anniversary Gift', esc_html( $gift ) );
		$body .= $this->para( 'Your gift has been applied to your account automatically. It is our way of saying thank you for another year with us.' );
		$body .= $this->cta_button( 'Shop Now', $this->get_store_url() );

		$this->apply_custom_template( 'anniversary', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body ) );
	}

	/**
	 * 20. Post-Purchase Follow-Up — fires 3 days after an order is completed
	 * with BOGO applied. Introduces the customer to other active deals.
	 * Triggered by: post_purchase role or order complete + 3-day delay hook.
	 */
	public function send_post_purchase( int $order_id ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return;
		}
		if ( $order->get_meta( '_gtbogo_post_purchase_sent', true ) ) {
			return;
		}
		$_cid = (int) $order->get_customer_id();
		if ( ! $this->dedup_check( $_cid, 'post_purchase' ) ) {
			return;
		}

		$store   = $this->get_store_name();
		$name    = $order->get_billing_first_name();
		$subject = $store . ': More deals waiting for you';

		// Get current active deals for display
		global $wpdb;
		$rules = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
			"SELECT name, buy_quantity, get_quantity, discount_value, discount_type
             FROM {$wpdb->prefix}gtbogo_rules
             WHERE status = 'active'
             ORDER BY priority ASC
             LIMIT 3",
			ARRAY_A
		);

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'post_purchase',
				array(
					'name'  => $name,
					'store' => $store,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'More deals for you, ' . esc_html( $name ) . '.' );
		$body .= $this->para( 'Your recent order at ' . esc_html( $store ) . ' qualified for a deal. Here is what else is active right now:' );

		if ( ! empty( $rules ) ) {
			foreach ( $rules as $rule ) {
				$disc  = (float) $rule['discount_value'];
				$type  = $rule['discount_type'] ?? 'percent';
				$label = $disc >= 100 ? 'Free'
					: ( $type === 'percent' ? $disc . '% off' : wc_price( $disc ) . ' off' );
				$body .= $this->step(
					'Buy ' . (int) $rule['buy_quantity'] . ' Get ' . (int) $rule['get_quantity'],
					esc_html( $rule['name'] ),
					$label . ' — applied automatically at checkout'
				);
			}
		} else {
			$body .= $this->para( 'Our current deals are applied automatically at checkout — no code needed.' );
		}

		$body .= $this->cta_button( 'Shop Again', $this->get_store_url() );

		$this->apply_custom_template( 'post_purchase', $subject, $body );
		$this->send( $order->get_billing_email(), $subject, $this->wrap( $body ) );
		$order->update_meta_data( '_gtbogo_post_purchase_sent', '1' );
		$order->save_meta_data();
	}

	/**
	 * 21. Lapsed Reactivation — fires when a customer has not ordered in 90+
	 * days. More targeted than winback — attaches a named deal.
	 * Triggered by: lapsed_90 role.
	 */
	public function send_lapsed_reactivation( string $email, string $first_name, string $deal_name, string $deal_label ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$cid = (int) email_exists( $email );
		if ( ! $this->dedup_check( $cid, 'lapsed_reactivation' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': Your exclusive offer — we saved it for you';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'lapsed_reactivation',
				array(
					'name'  => $first_name,
					'store' => $store,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'It has been a while, ' . esc_html( $first_name ) . '.' );
		$body .= $this->para( 'We have reserved an exclusive deal for you at ' . esc_html( $store ) . '. It is not available anywhere else on the site — only for you.' );
		$body .= $this->highlight_box( esc_html( $deal_name ), esc_html( $deal_label ) );
		$body .= $this->para( 'Your deal is applied automatically at checkout. No code needed — just add qualifying items and it fires.' );
		$body .= $this->cta_button( 'Claim Your Deal', $this->get_store_url() );

		$this->apply_custom_template( 'lapsed_reactivation', $subject, $body );
		$this->send( $email, $subject, $this->wrap( $body ) );
	}

	/**
	 * 22. New Customer Welcome — fires after a first order is completed.
	 * Introduces the customer to active deals and how they work.
	 */
	public function send_new_customer_welcome( int $order_id ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return;
		}
		if ( $order->get_meta( '_gtbogo_welcome_sent', true ) ) {
			return;
		}
		if ( $order->get_meta( '_gtbogo_customer_order_count', 1 ) > 1 ) {
			return;
		}
		$_cid = (int) $order->get_customer_id();
		if ( ! $this->dedup_check( $_cid, 'new_customer_welcome' ) ) {
			return;
		}

		$store   = $this->get_store_name();
		$name    = $order->get_billing_first_name();
		$subject = $store . ': Welcome — here is how deals work at ' . $store;

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'new_customer_welcome',
				array(
					'name'  => $name,
					'store' => $store,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Welcome, ' . esc_html( $name ) . '.' );
		$body .= $this->para( 'Thank you for your first order at ' . esc_html( $store ) . '. Here is something you should know — every time you shop with us, active deals fire automatically at checkout.' );
		$body .= $this->highlight_box( 'How Deals Work', 'Add qualifying items to your cart. The deal applies at checkout automatically — no code needed.' );
		$body .= $this->step( '1', 'Browse the shop', 'Add items that qualify for the active deal.' );
		$body .= $this->step( '2', 'Watch the cart', 'A progress bar shows how close you are to unlocking the deal.' );
		$body .= $this->step( '3', 'Checkout', 'Your deal fires automatically. Nothing to enter.' );
		$body .= $this->cta_button( 'Start Shopping', $this->get_store_url() );

		$this->apply_custom_template( 'new_customer_welcome', $subject, $body );
		$this->send( $order->get_billing_email(), $subject, $this->wrap( $body ) );
		$order->update_meta_data( '_gtbogo_welcome_sent', '1' );
		$order->save_meta_data();
	}

	/**
	 * 23. Deal Unlock Confirmation — fires when a cart threshold is crossed
	 * and a deal unlocks mid-session. Confirms to the customer what they earned.
	 */
	public function send_deal_unlocked( string $email, string $first_name, string $deal_name, string $deal_label, string $cart_url ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$cid = (int) email_exists( $email );
		if ( ! $this->dedup_check( $cid, 'deal_unlocked' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': Your deal is unlocked';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'deal_unlocked',
				array(
					'name'    => $first_name,
					'store'   => $store,
					'dynamic' => $deal_name,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Deal unlocked, ' . esc_html( $first_name ) . '.' );
		$body .= $this->para( 'You have qualified for a deal at ' . esc_html( $store ) . '. It is already applied to your cart — just complete your order to lock in the saving.' );
		$body .= $this->highlight_box( esc_html( $deal_name ), esc_html( $deal_label ) );
		$body .= $this->para( 'Your deal applies automatically at checkout. No code needed.' );
		$body .= $this->cta_button( 'Complete Your Order', $cart_url );

		$this->apply_custom_template( 'deal_unlocked', $subject, $body );
		$this->send( $email, $subject, $this->wrap( $body ) );
	}

	// ── Private helper — ordinal suffix ────────────────────────────────────────
	private function ordinal( int $n ): string {
		$suffix = array( 'th', 'st', 'nd', 'rd' );
		$v      = $n % 100;
		return $n . ( $suffix[ ( $v - 20 ) % 10 ] ?? $suffix[ min( $v, 4 ) ] ?? 'th' );
	}

	// ══════════════════════════════════════════════════════════════════════════
	// Role event handlers — auto-fire on account changes
	// ══════════════════════════════════════════════════════════════════════════

	/**
	 * Fires when a customer's loyalty tier is upgraded.
	 * Updates account meta and sends tier upgrade email.
	 */
	public function on_tier_upgraded( int $user_id, string $old_tier, string $new_tier ): void {
		update_user_meta( $user_id, 'gtbogo_loyalty_tier', $new_tier );
		update_user_meta( $user_id, 'gtbogo_loyalty_tier_prev', $old_tier );
		update_user_meta( $user_id, 'gtbogo_loyalty_tier_upgraded', current_time( 'mysql', true ) );
		$tier_labels = array(
			'bronze'   => 'Bronze',
			'silver'   => 'Silver',
			'gold'     => 'Gold',
			'platinum' => 'Platinum',
		);
		$this->send_tier_upgrade( $user_id, $tier_labels[ $new_tier ] ?? ucfirst( $new_tier ), $tier_labels[ $old_tier ] ?? ucfirst( $old_tier ) );
	}

	/**
	 * Fires when a customer unlocks VIP status.
	 * Updates account meta and sends VIP unlocked email.
	 */
	public function on_vip_unlocked( int $user_id, string $tier_name ): void {
		update_user_meta( $user_id, 'gtbogo_vip_tier', $tier_name );
		update_user_meta( $user_id, 'gtbogo_vip_unlocked_date', current_time( 'mysql', true ) );
		$this->send_vip_unlocked( $user_id, $tier_name );
	}

	/**
	 * Fires when a referral is confirmed.
	 * Awards credit to referrer account and sends reward email.
	 */
	public function on_referral_success( int $referrer_id, string $referred_name, string $reward ): void {
		// Increment referral count
		$count = (int) get_user_meta( $referrer_id, 'gtbogo_referral_count', true ) + 1;
		update_user_meta( $referrer_id, 'gtbogo_referral_count', $count );
		update_user_meta( $referrer_id, 'gtbogo_referral_last_date', current_time( 'mysql', true ) );
		update_user_meta( $referrer_id, 'gtbogo_referral_last_reward', $reward );

		// Auto-create WooCommerce coupon for referral reward if configured
		$coupon_value = (float) get_option( 'gtbogo_referral_coupon_value', 0 );
		if ( $coupon_value > 0 ) {
			$this->create_referral_coupon( $referrer_id, $count, $coupon_value );
		}

		// Send referral reward email
		$this->send_referral_reward( $referrer_id, $referred_name, $reward );

		// Send referral progress email
		$next_milestone = $count < 3 ? 3 : ( $count < 5 ? 5 : 10 );
		$this->send_referral_progress( $referrer_id, $count, $next_milestone, $reward );
	}

	/**
	 * Fires on any gtbogo role assignment — logs to account meta timeline.
	 */
	public function on_role_assigned( int $user_id, string $role ): void {
		$timeline   = get_user_meta( $user_id, 'gtbogo_role_timeline', true ) ?: array();
		$timeline[] = array(
			'role' => $role,
			'date' => current_time( 'mysql', true ),
		);
		// Keep last 50 events
		if ( count( $timeline ) > 50 ) {
			$timeline = array_slice( $timeline, -50 );
		}
		update_user_meta( $user_id, 'gtbogo_role_timeline', $timeline );
	}

	/**
	 * Create a WooCommerce coupon for a referral reward and assign to user.
	 */
	private function create_referral_coupon( int $user_id, int $referral_count, float $value ): void {
		if ( ! function_exists( 'wc_get_coupon_id_by_code' ) ) {
			return;
		}

		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}

		$code = 'GTBOGO-REF-' . esc_html( $user_id ) . '-' . esc_html( $referral_count ) . '-' . strtoupper( substr( md5( $user_id . $referral_count ), 0, 6 ) );

		// Don't create if already exists
		if ( wc_get_coupon_id_by_code( $code ) ) {
			return;
		}

		$coupon_type = get_option( 'gtbogo_referral_coupon_type', 'percent' );
		$post_id     = wp_insert_post(
			array(
				'post_title'   => $code,
				'post_name'    => $code,
				'post_status'  => 'publish',
				'post_type'    => 'shop_coupon',
				'post_excerpt' => 'GT BOGO Engine — Referral reward for user ' . $user_id,
			)
		);

		if ( is_wp_error( $post_id ) ) {
			return;
		}

		update_post_meta( $post_id, 'discount_type', $coupon_type );
		update_post_meta( $post_id, 'coupon_amount', $value );
		update_post_meta( $post_id, 'individual_use', 'yes' );
		update_post_meta( $post_id, 'usage_limit', 1 );
		update_post_meta( $post_id, 'customer_email', array( $user->user_email ) );
		update_post_meta( $post_id, 'date_expires', strtotime( '+90 days' ) );
		update_post_meta( $post_id, '_gtbogo_referral_coupon', '1' );

		// Store coupon code on user meta for display in My Account
		$coupons   = get_user_meta( $user_id, 'gtbogo_referral_coupons', true ) ?: array();
		$coupons[] = array(
			'code'    => $code,
			'value'   => $value,
			'type'    => $coupon_type,
			'expires' => gmdate( 'F j, Y', strtotime( '+90 days' ) ),
			'created' => current_time( 'mysql', true ),
		);
		update_user_meta( $user_id, 'gtbogo_referral_coupons', $coupons );
	}


	/**
	 * Option B — Custom email template override.
	 * Store owners paste their own subject/body in GT BOGO → Emails.
	 * Falls back to default English if no override is set.
	 */
	private function apply_custom_template( string $type, string &$subject, string &$body ): void {
		$custom_subject = get_option( 'gtbogo_email_' . $type . '_subject', '' );
		$custom_body    = get_option( 'gtbogo_email_' . $type . '_body', '' );
		if ( ! empty( $custom_subject ) ) {
			$subject = sanitize_text_field( $custom_subject );
		}
		if ( ! empty( $custom_body ) ) {
			$body = $this->para( wp_kses_post( $custom_body ) );
		}
	}



	/**
	 * 24. Birthday Deal — fires when birthday month is detected for a customer.
	 * Sends a personalised birthday deal email using the store theme.
	 */
	public function send_birthday_deal( int $user_id ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'birthday_deal' ) ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': A birthday gift from us';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'birthday_deal',
				array(
					'name'  => $user->first_name,
					'store' => $store,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Happy birthday, ' . $user->first_name . '.' );
		$body .= $this->para( 'Everyone at ' . esc_html( $store ) . ' wishes you a great birthday. To mark the occasion, we have activated an exclusive birthday deal on your account:' );
		$body .= $this->highlight_box( 'Birthday Deal', 'Active on your account this month — applied automatically at checkout.' );
		$body .= $this->para( 'No code needed. Just add qualifying items to your cart and the deal fires automatically.' );
		$body .= $this->cta_button( 'Shop Your Birthday Deal', $this->get_store_url() );

		$this->apply_custom_template( 'birthday_deal', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body, 'A birthday deal is waiting for you.' ) );
	}

	/**
	 * 25. Flash Sale Alert — fires when a flash sale rule is activated.
	 * Notifies opted-in customers that a time-limited deal is now live.
	 */
	// ═══════════════════════════════════════════════════════════════
	//  Inventory Intelligence Admin Alerts (7.2)
	// ═══════════════════════════════════════════════════════════════

	/**
	 * Send admin alert when targeted products are OOS or draft.
	 * Admin-facing only — uses store email, GT BOGO wrapper.
	 *
	 * @param array $issues Rule ID => array('rule_name'=>string,'problems'=>string[])
	 * @since 5.0.0
	 */
	public function send_inventory_oos_alert( array $issues ): void {
		$to      = get_option( 'admin_email' );
		$count   = count( $issues );
		$subject = sprintf(
			'Deal Alert: %d rule%s %s products that need attention',
			$count,
			$count === 1 ? '' : 's',
			$count === 1 ? 'has' : 'have'
		);
		$rules_url = admin_url( 'admin.php?page=gt-bogo-engine&tab=rules-manager' );
		// Build issue rows
		$issue_rows = '';
		foreach ( $issues as $rule_id => $data ) {
			$problems_html = '';
			foreach ( $data['problems'] as $p ) {
				$problems_html .= '<li style="margin:4px 0;font-size:13px;color:#94a3b8;">' . esc_html( $p ) . '</li>';
			}
			$issue_rows .= '
			<div style="background:#0f172a;border:1px solid #1e293b;border-left:4px solid #f59e0b;border-radius:6px;padding:16px 20px;margin:0 0 14px;">
				<p style="margin:0 0 8px;color:#f1f5f9;font-size:14px;font-weight:700;">' . esc_html( $data['rule_name'] ) . '</p>
				<ul style="margin:0;padding:0 0 0 16px;">' . $problems_html . '</ul>
			</div>';
		}
		$auto_pause = (bool) get_option( 'gtbogo_inv_auto_pause', false );
		$pause_note = $auto_pause
			? '<p style="margin:0 0 16px;color:#f59e0b;font-size:13px;font-weight:600;">These rules have been automatically paused until products are back in stock.</p>'
			: '<p style="margin:0 0 16px;color:#94a3b8;font-size:13px;">These rules are still active. Enable Auto-Pause in Settings to pause rules automatically when products go out of stock.</p>';
		$body  = $this->heading( 'Deal Rules Need Attention' );
		$body .= $this->para( 'GT BOGO Engine detected ' . $count . ' active deal rule' . ( $count === 1 ? '' : 's' ) . ' targeting products that are out of stock or unpublished. Your deals may not be firing correctly.' );
		$body .= $pause_note;
		$body .= $issue_rows;
		$body .= $this->cta_button( 'Review Rules', $rules_url );
		$body .= $this->para( 'This alert is sent once per issue. You will not receive duplicates unless new problems appear.' );
		$html  = $this->wrap( $body, 'Your deal rules have products that need attention.' );
		$this->send( $to, $subject, $html );
	}

	/**
	 * Send admin notification when auto-paused rules are resumed.
	 * Fires when all targeted products return to stock.
	 *
	 * @param array $resumed_rules array of array('id'=>int,'name'=>string)
	 * @since 5.0.0
	 */
	public function send_inventory_resume_alert( array $resumed_rules ): void {
		$to      = get_option( 'admin_email' );
		$count   = count( $resumed_rules );
		$subject = sprintf(
			'Deal Rules Resumed: %d rule%s automatically reactivated',
			$count,
			$count === 1 ? '' : 's'
		);
		$rules_url = admin_url( 'admin.php?page=gt-bogo-engine&tab=rules-manager' );
		$rule_rows = '';
		foreach ( $resumed_rules as $rule ) {
			$rule_rows .= '
			<div style="background:#0f172a;border:1px solid #1e293b;border-left:4px solid #22c55e;border-radius:6px;padding:14px 20px;margin:0 0 10px;">
				<p style="margin:0;color:#f1f5f9;font-size:14px;font-weight:700;">&#10003; ' . esc_html( $rule['name'] ) . '</p>
			</div>';
		}
		$body  = $this->heading( 'Deal Rules Resumed' );
		$body .= $this->para( 'Good news. ' . $count . ' deal rule' . ( $count === 1 ? '' : 's' ) . ' that ' . ( $count === 1 ? 'was' : 'were' ) . ' paused due to out-of-stock products ' . ( $count === 1 ? 'has' : 'have' ) . ' been automatically reactivated. All targeted products are back in stock.' );
		$body .= $rule_rows;
		$body .= $this->cta_button( 'View Active Rules', $rules_url );
		$html  = $this->wrap( $body, 'Your deal rules are back online.' );
		$this->send( $to, $subject, $html );
	}

	public function send_flash_sale_alert( array $rule, string $email, string $first_name ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		$cid = email_exists( $email ) ? (int) email_exists( $email ) : 0;
		if ( ! $this->dedup_check( $cid, 'flash_sale' ) ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': Flash deal — live now';

		$disc  = (float) ( $rule['discount_value'] ?? 50 );
		$type  = $rule['discount_type'] ?? 'percent';
		$label = $disc >= 100 ? 'Free' : ( $type === 'percent' ? $disc . '% off' : wc_price( $disc ) . ' off' );
		$name  = sanitize_text_field( $rule['name'] ?? 'Flash Deal' );

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'flash_sale',
				array(
					'name'    => $first_name,
					'store'   => $store,
					'dynamic' => $label,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'A deal just went live.' );
		$body .= $this->para( 'Hi ' . esc_html( $first_name ) . ', a limited flash deal is now active at ' . esc_html( $store ) . ':' );
		$body .= $this->highlight_box( esc_html( $name ), esc_html( $label ) . ' — applied automatically at checkout' );
		$body .= $this->para( 'This deal fires automatically when you add qualifying items. No code required. Act quickly — flash deals are time-limited.' );
		$body .= $this->cta_button( 'Shop the Flash Deal', $this->get_store_url() );

		$this->apply_custom_template( 'flash_sale', $subject, $body );
		$this->send( $email, $subject, $this->wrap( $body, 'A flash deal is live now.' ) );
	}

	/**
	 * 26. B2B Volume Tier Reached — fires when a B2B customer hits a volume tier.
	 * Confirms the new pricing tier and what it means for their account.
	 */
	public function send_b2b_tier_reached( int $user_id, string $tier_label, string $reward ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! $this->dedup_check( $user_id, 'b2b_tier' ) ) {
			return;
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return;
		}
		$store   = $this->get_store_name();
		$subject = $store . ': Your volume pricing tier has been upgraded';

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'b2b_tier',
				array(
					'name'    => $user->first_name,
					'store'   => $store,
					'dynamic' => $tier_label,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? 'Volume tier upgrade confirmed.' );
		$body .= $this->para( 'Your account at ' . esc_html( $store ) . ' has reached a new volume pricing tier based on your order history. Your new pricing is now active:' );
		$body .= $this->highlight_box( 'Your Tier', esc_html( $tier_label ) );
		$body .= $this->highlight_box( 'Tier Benefit', esc_html( $reward ) );
		$body .= $this->para( 'Your volume pricing applies automatically on every qualifying order from this point forward. No action required.' );
		$body .= $this->cta_button( 'Place Your Next Order', $this->get_store_url() );

		$this->apply_custom_template( 'b2b_tier', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body, 'Your volume pricing tier has been upgraded.' ) );
	}

	public function dispatch_loyalty_nudge( int $customer_id, string $nudge_type, array $nudge_config ): void {
		if ( $nudge_type === 'nudge_1' ) {
			$this->send_loyalty_nudge_1( $customer_id, $nudge_config );
		} elseif ( $nudge_type === 'nudge_2' ) {
			$this->send_loyalty_nudge_2( $customer_id, $nudge_config );
		}
	}

	/**
	 * Loyalty Nudge 1 — sent 7-12 days after first purchase.
	 *
	 * Introduces the loyalty programme, previews VIP benefits,
	 * and delivers a second-purchase incentive tied to their first order context.
	 * Includes: personalised greeting, order acknowledgment, loyalty progress
	 * visualisation, second-purchase offer, VIP benefits preview, and store
	 * social proof. Cadence-aware: references days since first order.
	 */
	public function send_loyalty_nudge_1( int $customer_id, array $nudge_config = array() ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! $this->dedup_check( $customer_id, 'loyalty_nudge_1' ) ) {
			return;
		}
		$user = get_userdata( $customer_id );
		if ( ! $user ) {
			return;
		}

		$store     = $this->get_store_name();
		$first     = esc_html( $user->first_name ?: $user->display_name );
		$deal      = $nudge_config['deal_name'] ?? __( 'an exclusive returning-customer offer', 'gt-bogo-engine' );
		$discount  = $nudge_config['discount'] ?? '';
		$threshold = (int) get_option( 'gtbogo_loyalty_vip_threshold', 4 );
		$remaining = max( 1, $threshold - 1 );

		// Pull most recent order details for personalisation
		$last_orders  = wc_get_orders(
			array(
				'customer' => $customer_id,
				'limit'    => 1,
				'orderby'  => 'date',
				'order'    => 'DESC',
				'status'   => array( 'wc-completed', 'wc-processing' ),
			)
		);
		$last_order   = ! empty( $last_orders ) ? $last_orders[0] : null;
		$order_total  = $last_order ? wp_strip_all_tags( wc_price( $last_order->get_total() ) ) : '';
		$order_date   = $last_order ? date_i18n( get_option( 'date_format' ), strtotime( $last_order->get_date_created() ) ) : '';
		$order_number = $last_order ? '#' . $last_order->get_order_number() : '';
		$days_since   = $last_order
			? (int) floor( ( time() - strtotime( $last_order->get_date_created() ) ) / DAY_IN_SECONDS )
			: 7;

		// VIP benefits preview
		$vip_benefits = array(
			__( 'Priority support', 'gt-bogo-engine' ),
			__( 'Exclusive early access to new products', 'gt-bogo-engine' ),
			__( 'Higher discount tiers on all deals', 'gt-bogo-engine' ),
		);

		$subject = $store . ': ' . sprintf( __( 'You were great to have — here is something for your next visit', 'gt-bogo-engine' ) );

				// Industry-aware copy
		$_ic = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'loyalty_nudge_1',
				array(
					'name'    => $first,
					'store'   => $store,
					'dynamic' => (string) $days_since,
				)
			)
			: array();
		// translators: %s and %d are replaced with dynamic values.
		$body  = $this->heading( $_ic['heading'] ?? sprintf( __( 'Welcome back, %s.', 'gt-bogo-engine' ), $first ) );
		$body .= $this->para(
			sprintf(
			/* translators: 1: days since order, 2: store name, 3: order number */                __( 'It has been %1$d day(s) since your first order at %2$s%3$s. We hope everything arrived perfectly.', 'gt-bogo-engine' ),
				$days_since,
				esc_html( $store ),
				$order_number ? ' (' . $order_number . ')' : ''
			)
		);

		// Order summary block
		if ( $order_total && $order_date ) {
			$body .= $this->highlight_box(
				__( 'Your First Order', 'gt-bogo-engine' ),
				$order_date . ( $order_total ? ' — ' . $order_total : '' )
			);
		}

		$body .= $this->para(
			sprintf(
			// translators: %s and %d are replaced with dynamic values.
				__( 'As a thank you for choosing %s, we have set aside a returning-customer offer available exclusively on your next purchase.', 'gt-bogo-engine' ),
				esc_html( $store )
			)
		);

		$body .= $this->highlight_box( __( 'Your Second-Purchase Offer', 'gt-bogo-engine' ), esc_html( $deal ) );

		if ( $discount ) {
			$body .= $this->highlight_box( __( 'Discount', 'gt-bogo-engine' ), esc_html( $discount ) );
		}

		// VIP progress visualisation
		$body .= $this->para(
			sprintf(
			// translators: %s and %d are replaced with dynamic values.
				__( 'You are 1 of %d orders away from VIP membership — our highest loyalty tier.', 'gt-bogo-engine' ),
				$threshold
			)
		);

		// Progress bar (text-based, email-safe)
		$progress_cells = '';
		for ( $i = 1; $i <= $threshold; $i++ ) {
			$done            = $i === 1;
			$progress_cells .= sprintf(
				'<td style="width:%d%%;padding:4px 2px;text-align:center;font-size:10px;color:%s;">%s</td>',
				(int) floor( 100 / $threshold ),
				$done ? '#10b981' : '#94a3b8',
				$done ? '✓' : $i
			);
		}
		$body .= '<table width="100%" style="border-collapse:collapse;margin:12px 0;"><tr>' . $progress_cells . '</tr></table>';

		// VIP benefits preview
		$body .= $this->heading( __( 'VIP membership includes:', 'gt-bogo-engine' ) );
		foreach ( $vip_benefits as $benefit ) {
			$body .= $this->para( '→ ' . $benefit );
		}

		$body .= $this->para( __( 'Your offer is waiting. Shop now and it applies automatically at checkout — no code needed.', 'gt-bogo-engine' ) );
		$body .= $this->cta_button( __( 'Claim Your Offer', 'gt-bogo-engine' ), $this->get_store_url() );
		$body .= $this->para(
			sprintf(
			// translators: %s and %d are replaced with dynamic values.
				__( 'Questions? Reply to this email or visit %s.', 'gt-bogo-engine' ),
				esc_html( $store )
			)
		);

		$this->apply_custom_template( 'loyalty_nudge_1', $subject, $body );
		$this->send( $user->user_email, $subject, $this->wrap( $body, __( 'Your returning-customer offer is waiting.', 'gt-bogo-engine' ) ) );
	}

	/**
	 * Loyalty Nudge 2 — sent 14-19 days after second purchase.
	 *
	 * Acknowledges two-order milestone with full VIP progress,
	 * reveals specific VIP benefits with concrete descriptions,
	 * delivers third-purchase incentive, and includes cadence insight
	 * (average days between purchases for this customer).
	 */
	public function send_loyalty_nudge_2( int $customer_id, array $nudge_config = array() ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! $this->dedup_check( $customer_id, 'loyalty_nudge_2' ) ) {
			return;
		}
		$user = get_userdata( $customer_id );
		if ( ! $user ) {
			return;
		}

		$store     = $this->get_store_name();
		$first     = esc_html( $user->first_name ?: $user->display_name );
		$threshold = (int) get_option( 'gtbogo_loyalty_vip_threshold', 4 );
		$remaining = max( 1, $threshold - 2 );
		$deal      = $nudge_config['deal_name'] ?? __( 'an exclusive third-order offer', 'gt-bogo-engine' );
		$discount  = $nudge_config['discount'] ?? '';

		// Pull loyalty state for cadence insight
		$state       = class_exists( 'GT_BOGO_Loyalty_Accelerator' )
			? GT_BOGO_Loyalty_Accelerator::get_state( $customer_id )
			: array();
		$avg_cadence = (int) ( $state['avg_cadence_days'] ?? 0 );
		$last_ts     = (int) ( $state['last_order_ts'] ?? 0 );
		$days_since  = $last_ts ? (int) floor( ( time() - $last_ts ) / DAY_IN_SECONDS ) : 14;

		// Full VIP benefits with concrete descriptions
		$vip_benefits = array(
			__( 'Priority queue — your questions answered within 2 hours', 'gt-bogo-engine' ),
			__( 'Early access — shop new arrivals 48 hours before everyone else', 'gt-bogo-engine' ),
			__( 'Maximum deals — automatically eligible for highest discount tiers', 'gt-bogo-engine' ),
			__( 'VIP badge — displayed on your account and in order confirmations', 'gt-bogo-engine' ),
		);

		$subject = $store . ': ' . sprintf(
			// translators: %s and %d are replaced with dynamic values.
			__( 'Two down — %d to go for VIP', 'gt-bogo-engine' ),
			$remaining
		);

				// Industry-aware copy
		$_ic = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'loyalty_nudge_2',
				array(
					'name'    => $first,
					'store'   => $store,
					'dynamic' => (string) $orders_to_vip,
				)
			)
			: array();
		// translators: %s and %d are replaced with dynamic values.
		$body  = $this->heading( $_ic['heading'] ?? sprintf( __( '%s — you are on a streak.', 'gt-bogo-engine' ), $first ) );
		$body .= $this->para(
			sprintf(
			/* translators: 1: store name, 2: orders remaining */                __( 'Two purchases at %1$s. You are in the top tier of our most engaged customers, and you are only %2$d order(s) away from VIP membership.', 'gt-bogo-engine' ),
				esc_html( $store ),
				$remaining
			)
		);

		// Progress visualisation
		$progress_cells = '';
		for ( $i = 1; $i <= $threshold; $i++ ) {
			$done            = $i <= 2;
			$progress_cells .= sprintf(
				'<td style="width:%d%%;padding:4px 2px;text-align:center;font-size:10px;color:%s;font-weight:%s;">%s</td>',
				(int) floor( 100 / $threshold ),
				$done ? '#10b981' : '#94a3b8',
				$done ? '700' : '400',
				$done ? '✓' : $i
			);
		}
		$body .= '<table width="100%" style="border-collapse:collapse;margin:12px 0;"><tr>' . $progress_cells . '</tr></table>';

		if ( $avg_cadence > 0 ) {
			$body .= $this->para(
				sprintf(
				// translators: %s and %d are replaced with dynamic values.
					__( 'Your average purchase interval is %d days. Based on your pattern, you are likely browsing again soon.', 'gt-bogo-engine' ),
					$avg_cadence
				)
			);
		}

		$body .= $this->highlight_box(
			__( 'VIP Status', 'gt-bogo-engine' ),
			// translators: %s and %d are replaced with dynamic values.
			sprintf( __( '%d more order(s) to unlock', 'gt-bogo-engine' ), $remaining )
		);

		// Third-order deal
		$body .= $this->para( __( 'To help you cross the finish line, here is an offer for your next purchase:', 'gt-bogo-engine' ) );
		$body .= $this->highlight_box( __( 'Your Third-Order Offer', 'gt-bogo-engine' ), esc_html( $deal ) );

		if ( $discount ) {
			$body .= $this->highlight_box( __( 'Discount Applied', 'gt-bogo-engine' ), esc_html( $discount ) );
		}

		// VIP benefits list
		$body .= $this->heading( __( 'When you reach VIP, you unlock:', 'gt-bogo-engine' ) );
		foreach ( $vip_benefits as $benefit ) {
			$body .= $this->para( '→ ' . $benefit );
		}

		$body .= $this->para( __( 'Your offer applies automatically at checkout — no code required.', 'gt-bogo-engine' ) );
		$body .= $this->cta_button( __( 'Continue to VIP', 'gt-bogo-engine' ), $this->get_store_url() );

		$this->apply_custom_template( 'loyalty_nudge_2', $subject, $body );
		$this->send(
			$user->user_email,
			$subject,
			$this->wrap(
				$body,
				// translators: %s and %d are replaced with dynamic values.
				sprintf( __( '%d more orders to VIP membership.', 'gt-bogo-engine' ), $remaining )
			)
		);
	}

	/**
	 * Bundle Recommendation — triggered by Bundle Intelligence V2.
	 *
	 * Sends a data-driven bundle recommendation based on real co-purchase
	 * affinity data. Includes: individual product breakdown, affinity
	 * explanation, bundle vs individual price comparison, discount details,
	 * expiry framing, and a direct CTA to the assembled bundle.
	 *
	 * @param int   $customer_id WooCommerce customer ID
	 * @param array $bundle      Bundle payload from GT_BOGO_Bundle_Intelligence_V2::assemble_bundle()
	 *                           Expected keys: name, products[], savings, discount, url,
	 *                           affinity_score, bundle_price, individual_price, expires_hours
	 */
	public function send_bundle_recommendation( int $customer_id, array $bundle = array() ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! $this->dedup_check( $customer_id, 'bundle_recommendation' ) ) {
			return;
		}
		$user = get_userdata( $customer_id );
		if ( ! $user ) {
			return;
		}

		$store            = $this->get_store_name();
		$first            = esc_html( $user->first_name ?: $user->display_name );
		$bundle_name      = $bundle['name'] ?? __( 'a curated bundle', 'gt-bogo-engine' );
		$savings          = $bundle['savings'] ?? '';
		$discount         = $bundle['discount'] ?? '';
		$bundle_price     = $bundle['bundle_price'] ?? 0;
		$individual_price = $bundle['individual_price'] ?? 0;
		$affinity_score   = (float) ( $bundle['affinity_score'] ?? 0 );
		$products         = (array) ( $bundle['products'] ?? array() );
		$expires_hours    = (int) ( $bundle['expires_hours'] ?? 48 );
		$url              = ! empty( $bundle['url'] ) ? esc_url( $bundle['url'] ) : $this->get_store_url();

		$subject = $store . ': ' . sprintf(
			// translators: %s and %d are replaced with dynamic values.
			__( 'These go together — a bundle picked for you, %s', 'gt-bogo-engine' ),
			$first
		);

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'bundle_recommendation',
				array(
					'name'    => '',
					'store'   => $store,
					'dynamic' => $bundle_name ?? '',
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? __( 'Picked for you: a bundle based on real data.', 'gt-bogo-engine' ) );
		$body .= $this->para(
			sprintf(
			/* translators: 1: customer first name, 2: store name */                __( 'Hi %1$s, our system analysed thousands of orders at %2$s and identified a product combination that customers with your taste frequently purchase together. This recommendation was generated specifically for you.', 'gt-bogo-engine' ),
				$first,
				esc_html( $store )
			)
		);

		$body .= $this->highlight_box( __( 'Your Bundle', 'gt-bogo-engine' ), esc_html( $bundle_name ) );

		// Individual product breakdown
		if ( ! empty( $products ) ) {
			$body .= $this->heading( __( 'What is included:', 'gt-bogo-engine' ) );
			foreach ( $products as $product ) {
				$p_name  = is_array( $product ) ? ( $product['name'] ?? '' ) : $product;
				$p_price = is_array( $product ) ? ( $product['price'] ?? '' ) : '';
				$p_desc  = is_array( $product ) ? ( $product['desc'] ?? '' ) : '';
				$line    = esc_html( $p_name );
				if ( $p_price ) {
					$line .= ' — ' . esc_html( $p_price );
				}
				if ( $p_desc ) {
					$line .= ': ' . esc_html( $p_desc );
				}
				$body .= $this->para( '→ ' . $line );
			}
		}

		// Price comparison
		if ( $bundle_price > 0 && $individual_price > 0 ) {
			$body .= $this->highlight_box(
				__( 'Individual Price', 'gt-bogo-engine' ),
				wp_strip_all_tags( wc_price( $individual_price ) )
			);
			$body .= $this->highlight_box(
				__( 'Bundle Price', 'gt-bogo-engine' ),
				wp_strip_all_tags( wc_price( $bundle_price ) )
			);
		}

		if ( $savings ) {
			$body .= $this->highlight_box( __( 'You Save', 'gt-bogo-engine' ), esc_html( $savings ) );
		}

		if ( $discount ) {
			$body .= $this->highlight_box( __( 'Bundle Discount', 'gt-bogo-engine' ), esc_html( $discount ) );
		}

		// Affinity explanation
		if ( $affinity_score > 0 ) {
			$pct   = min( 100, (int) round( $affinity_score * 100 ) );
			$body .= $this->para(
				sprintf(
				// translators: %s and %d are replaced with dynamic values.
					__( 'Affinity score: %d%% — products in this bundle are purchased together frequently by customers with your browsing and purchase history.', 'gt-bogo-engine' ),
					$pct
				)
			);
		} else {
			$body .= $this->para( __( 'This bundle is based on co-purchase patterns from customers with similar taste. Each product complements the others.', 'gt-bogo-engine' ) );
		}

		// Expiry framing (honest — not fake urgency)
		if ( $expires_hours > 0 ) {
			$body .= $this->para(
				sprintf(
				// translators: %s and %d are replaced with dynamic values.
					__( 'This bundle recommendation is available for the next %d hours, after which our system will generate a new recommendation based on updated data.', 'gt-bogo-engine' ),
					$expires_hours
				)
			);
		}

		$body .= $this->cta_button( __( 'View Bundle', 'gt-bogo-engine' ), $url );
		$body .= $this->para(
			sprintf(
			// translators: %s and %d are replaced with dynamic values.
				__( 'Questions about this bundle? Reply to this email or contact us at %s.', 'gt-bogo-engine' ),
				esc_html( $store )
			)
		);

		$this->apply_custom_template( 'bundle_recommendation', $subject, $body );
		$this->send(
			$user->user_email,
			$subject,
			$this->wrap(
				$body,
				__( 'A data-driven bundle picked for you.', 'gt-bogo-engine' )
			)
		);
	}

	/**
	 * Intent-Based Offer — sent when Intent Engine score exceeds threshold.
	 *
	 * Triggered by the Intent Engine when a visitor's behavioural score
	 * crosses the hot threshold (default: 75+) without a cart deal firing.
	 * Delivers a personalised offer tied to what they were browsing, explains
	 * why they received it, includes product context, clear expiry, and
	 * back-to-cart CTA. Uses journey stage for copy personalisation.
	 *
	 * @param int    $customer_id WooCommerce customer ID
	 * @param string $deal_name   Name of the deal offer
	 * @param string $discount    Discount description e.g. '10% off'
	 * @param string $expiry      Human-readable expiry e.g. '24 hours'
	 */
	public function send_intent_based_offer( int $customer_id, string $deal_name, string $discount, string $expiry ): void {
		if ( ! $this->emails_enabled() ) {
			return;
		}
		if ( ! $this->dedup_check( $customer_id, 'intent_based_offer' ) ) {
			return;
		}
		$user = get_userdata( $customer_id );
		if ( ! $user ) {
			return;
		}

		$store = $this->get_store_name();
		$first = esc_html( $user->first_name ?: $user->display_name );

		// Journey-stage aware copy
		$stage = 'unknown';
		if ( class_exists( 'GT_BOGO_Journey_State' ) ) {
			$stage = GT_BOGO_Journey_State::get_stage( $customer_id );
		}

		$stage_intros = array(
			'evaluation' => __( 'You have been looking at us carefully — and we noticed.', 'gt-bogo-engine' ),
			'intent'     => __( 'You were close to making a decision. We want to make it easier.', 'gt-bogo-engine' ),
			'at_risk'    => __( 'We have not seen you for a while. Here is a reason to come back.', 'gt-bogo-engine' ),
			'returning'  => __( 'Welcome back. We put together something just for your visit.', 'gt-bogo-engine' ),
		);
		$intro        = $stage_intros[ $stage ] ?? __( 'You were browsing and we noticed something — here is an offer.', 'gt-bogo-engine' );

		// Product context from Intent Engine (most-viewed product)
		$product_name = '';
		$product_url  = '';
		if ( $customer_id > 0 ) {
			$meta = get_user_meta( $customer_id, '_gtbogo_intent_top_product', true );
			if ( $meta && is_array( $meta ) ) {
				$p = wc_get_product( (int) ( $meta['id'] ?? 0 ) );
				if ( $p ) {
					$product_name = $p->get_name();
					$product_url  = $p->get_permalink();
				}
			}
		}

		// Behavioural explanation (non-invasive, honest)
		$behaviour_copy = __( 'Our system detected genuine interest based on your browsing pattern — time spent, pages visited, and interaction signals — and reserved this offer specifically for your account.', 'gt-bogo-engine' );

		$subject = $store . ': ' . __( 'An offer reserved from your last visit', 'gt-bogo-engine' );

				// Industry-aware copy
		$_ic   = class_exists( 'GT_BOGO_Email_Industry' )
			? GT_BOGO_Email_Industry::get(
				'intent_based_offer',
				array(
					'name'    => $first_name ?? '',
					'store'   => $store,
					'dynamic' => $expiry,
				)
			)
			: array();
		$body  = $this->heading( $_ic['heading'] ?? $intro );
		$body .= $this->para(
			sprintf(
			/* translators: 1: customer first name, 2: store name */                __( 'Hi %1$s, during your recent visit to %2$s your browsing pattern indicated you were considering a purchase.', 'gt-bogo-engine' ),
				$first,
				esc_html( $store )
			)
		);

		// Product context if available
		if ( $product_name ) {
			$body .= $this->highlight_box(
				__( 'You Were Looking At', 'gt-bogo-engine' ),
				esc_html( $product_name )
			);
		}

		$body .= $this->para( $behaviour_copy );

		$body .= $this->highlight_box( __( 'Your Reserved Offer', 'gt-bogo-engine' ), esc_html( $deal_name ) );
		$body .= $this->highlight_box( __( 'Discount', 'gt-bogo-engine' ), esc_html( $discount ) );
		$body .= $this->highlight_box( __( 'Offer Expires', 'gt-bogo-engine' ), esc_html( $expiry ) );

		$body .= $this->para( __( 'This offer is exclusive to your account and was not sent to other customers. It applies automatically at checkout — no code required.', 'gt-bogo-engine' ) );

		// Direct CTA — product page if available, otherwise store
		$cta_url = $product_url ?: $this->get_store_url();
		$body   .= $this->cta_button( __( 'Claim Offer Now', 'gt-bogo-engine' ), $cta_url );

		// What happens if they don't claim (honest framing)
		$body .= $this->para(
			sprintf(
			// translators: %s and %d are replaced with dynamic values.
				__( 'If you do not claim this offer within %s, it will expire and the discount will no longer be available on your account.', 'gt-bogo-engine' ),
				esc_html( $expiry )
			)
		);

		$body .= $this->para(
			sprintf(
			// translators: %s and %d are replaced with dynamic values.
				__( 'If you have questions or feel you received this in error, reply to this email and our team at %s will help.', 'gt-bogo-engine' ),
				esc_html( $store )
			)
		);

		$this->apply_custom_template( 'intent_based_offer', $subject, $body );
		$this->send(
			$user->user_email,
			$subject,
			$this->wrap(
				$body,
				__( 'An exclusive offer from your last visit — expiring soon.', 'gt-bogo-engine' )
			)
		);
	}

	/**
	 * Handle gtbogo_trigger_email action — decouples superpowers from email delivery.
	 *
	 * @param string $type  Email type matching a send_* method.
	 * @param array  $data  Data array passed to the method.
	 * @return void
	 */
	public function handle_trigger_email( string $type, array $data = array() ): void {
		$method = 'send_' . $type;
		if ( method_exists( $this, $method ) ) {
			call_user_func_array( array( $this, $method ), array_values( $data ) );
		}
	}
}
