<?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

/**
 * GT BOGO Engine — Private Client Account Dashboard
 *
 * Premium customer account portal. Adaptive design works on any theme.
 * Luxury compliant — no emoji, no exclamation marks, no air-miles language.
 *
 * Sections:
 *   1. Summary strip — total savings, orders, tier, streak at a glance
 *   2. Loyalty tier — spend progress, next tier deal preview
 *   3. Order milestone progress — next milestone reward
 *   4. VIP status — perks, active deal, stats
 *   5. Purchase streak — progress to next bonus
 *   6. Birthday & anniversary status
 *   7. Active deals — all rules available right now
 *   8. Referral program — link, count, earned rewards
 *   9. Rewards history — role timeline
 *
 * @package GT_BOGO_Engine
 * @since   3.16.0
 */
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class GT_BOGO_Customer_Portal_Account {
 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound

	private static ?self $instance = null;

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

	private function __construct() {
		add_filter( 'woocommerce_account_menu_items', array( $this, 'add_menu_item' ) );
		add_action( 'woocommerce_account_gt-bogo-rewards_endpoint', array( $this, 'render_page' ) );
		add_action( 'init', array( $this, 'register_endpoint' ) );
		add_filter( 'query_vars', array( $this, 'add_query_var' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
		add_action( 'init', array( $this, 'set_referral_cookie' ) );
		add_action( 'user_register', array( $this, 'track_referred_registration' ) );
		add_action( 'woocommerce_order_status_completed', array( $this, 'track_referral_conversion' ), 20 );
		// Live portal refresh — logged-in customers only
		add_action( 'wp_ajax_gtbogo_portal_refresh', array( $this, 'ajax_portal_refresh' ) );
	}

	public function register_endpoint(): void {
		add_rewrite_endpoint( 'gt-bogo-rewards', EP_ROOT | EP_PAGES );
	}

	public function add_query_var( array $vars ): array {
		$vars[] = 'gt-bogo-rewards';
		return $vars;
	}

	public function add_menu_item( array $items ): array {
		$logout = $items['customer-logout'] ?? null;
		unset( $items['customer-logout'] );
		$label                    = get_option( 'gtbogo_account_tab_label', 'Private Client' );
		$items['gt-bogo-rewards'] = esc_html( $label );
		if ( $logout !== null ) {
			$items['customer-logout'] = $logout;
		}
		return $items;
	}

	// ── Styles ────────────────────────────────────────────────────────────────

	public function enqueue_styles(): void {
		if ( ! is_account_page() ) {
			return;
		}
		$accent = get_option( 'gtbogo_email_accent_color', '#E10000' );
		wp_add_inline_style(
			'woocommerce-general',
			"
            /* ── Wrap ── */
            .gtbogo-account-wrap {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
                max-width: 780px;
            }

            /* ── Summary strip ── */
            .gtbogo-summary {
                display: grid;
                grid-template-columns: repeat(4, 1fr);
                gap: 1px;
                background: rgba(255,255,255,.07);
                border: 1px solid rgba(255,255,255,.07);
                border-radius: 12px;
                overflow: hidden;
                margin-bottom: 20px;
            }
            .gtbogo-summary-cell {
                background: rgba(255,255,255,.02);
                padding: 20px 22px;
                text-align: center;
            }
            .gtbogo-summary-value {
                font-size: 22px;
                font-weight: 800;
                color: #fff;
                margin: 0 0 4px;
                line-height: 1;
                letter-spacing: -.02em;
            }
            .gtbogo-summary-label {
                font-size: 10px;
                font-weight: 700;
                letter-spacing: .1em;
                text-transform: uppercase;
                color: rgba(255,255,255,.35);
                margin: 0;
            }

            /* ── Cards ── */
            .gtbogo-card {
                background: rgba(255,255,255,.03);
                border: 1px solid rgba(255,255,255,.07);
                border-radius: 12px;
                padding: 26px 30px;
                margin-bottom: 14px;
                position: relative;
                overflow: hidden;
            }
            .gtbogo-card::before {
                content: '';
                position: absolute;
                top: 0; left: 0; right: 0;
                height: 1px;
                background: linear-gradient(90deg, {$accent} 0%, transparent 60%);
                opacity: .4;
            }

            /* ── Card typography ── */
            .gtbogo-label {
                display: block;
                font-size: 10px;
                font-weight: 800;
                letter-spacing: .12em;
                text-transform: uppercase;
                color: {$accent};
                margin: 0 0 12px;
            }
            .gtbogo-card h3 {
                margin: 0 0 6px;
                font-size: 22px;
                font-weight: 700;
                color: #fff;
                line-height: 1.2;
            }
            .gtbogo-card p {
                margin: 0;
                font-size: 14px;
                line-height: 1.7;
                color: rgba(255,255,255,.5);
            }
            .gtbogo-card strong, .gtbogo-card b { color: #fff; font-weight: 600; }

            /* ── Progress bar ── */
            .gtbogo-bar-wrap {
                background: rgba(255,255,255,.07);
                border-radius: 999px;
                height: 5px;
                margin: 16px 0 10px;
                overflow: hidden;
            }
            .gtbogo-bar-fill {
                height: 100%;
                border-radius: 999px;
                background: {$accent};
                transition: width .6s cubic-bezier(.4,0,.2,1);
            }

            /* ── Pill badge ── */
            .gtbogo-pill {
                display: inline-block;
                background: rgba(255,255,255,.06);
                border: 1px solid rgba(255,255,255,.1);
                color: rgba(255,255,255,.6);
                border-radius: 4px;
                padding: 3px 11px;
                font-size: 10px;
                font-weight: 800;
                letter-spacing: .08em;
                text-transform: uppercase;
            }

            /* ── Tier preview box ── */
            .gtbogo-tier-preview {
                margin-top: 18px;
                padding: 14px 16px;
                background: rgba(255,255,255,.03);
                border: 1px solid rgba(255,255,255,.07);
                border-radius: 8px;
            }
            .gtbogo-tier-preview-label {
                font-size: 10px;
                font-weight: 800;
                letter-spacing: .1em;
                text-transform: uppercase;
                color: rgba(255,255,255,.3);
                margin: 0 0 6px;
            }
            .gtbogo-tier-preview-deal {
                font-size: 14px;
                font-weight: 600;
                color: rgba(255,255,255,.8);
                margin: 0;
            }

            /* ── Milestone grid ── */
            .gtbogo-milestone-grid {
                display: flex;
                gap: 8px;
                margin-top: 18px;
                flex-wrap: wrap;
            }
            .gtbogo-milestone-node {
                flex: 1;
                min-width: 60px;
                text-align: center;
                padding: 10px 6px;
                border-radius: 8px;
                border: 1px solid rgba(255,255,255,.07);
                background: rgba(255,255,255,.02);
            }
            .gtbogo-milestone-node.done {
                border-color: rgba(255,255,255,.15);
                background: rgba(255,255,255,.05);
            }
            .gtbogo-milestone-node.next {
                border-color: {$accent};
                background: rgba(255,255,255,.04);
            }
            .gtbogo-milestone-num {
                font-size: 16px;
                font-weight: 800;
                color: rgba(255,255,255,.25);
                margin: 0 0 3px;
                line-height: 1;
            }
            .gtbogo-milestone-node.done .gtbogo-milestone-num { color: #fff; }
            .gtbogo-milestone-node.next .gtbogo-milestone-num { color: {$accent}; }
            .gtbogo-milestone-lbl {
                font-size: 9px;
                font-weight: 700;
                letter-spacing: .06em;
                text-transform: uppercase;
                color: rgba(255,255,255,.25);
                margin: 0;
            }
            .gtbogo-milestone-node.done .gtbogo-milestone-lbl { color: rgba(255,255,255,.5); }
            .gtbogo-milestone-node.next .gtbogo-milestone-lbl { color: {$accent}; }

            /* ── Deal rows ── */
            .gtbogo-deal-row {
                display: flex;
                align-items: center;
                justify-content: space-between;
                gap: 16px;
                padding: 15px 0;
                border-bottom: 1px solid rgba(255,255,255,.05);
            }
            .gtbogo-deal-row:last-child { border-bottom: none; padding-bottom: 0; }

            /* ── Stat pair ── */
            .gtbogo-stat-pair {
                display: flex;
                gap: 1px;
                background: rgba(255,255,255,.06);
                border-radius: 8px;
                overflow: hidden;
                margin: 18px 0;
            }
            .gtbogo-stat-cell {
                flex: 1;
                background: rgba(255,255,255,.02);
                padding: 14px 16px;
                text-align: center;
            }
            .gtbogo-stat-val {
                font-size: 20px;
                font-weight: 800;
                color: #fff;
                margin: 0 0 3px;
                line-height: 1;
            }
            .gtbogo-stat-lbl {
                font-size: 10px;
                font-weight: 700;
                letter-spacing: .08em;
                text-transform: uppercase;
                color: rgba(255,255,255,.3);
                margin: 0;
            }

            /* ── Highlight box ── */
            .gtbogo-highlight {
                padding: 14px 16px;
                background: rgba(255,255,255,.03);
                border: 1px solid rgba(255,255,255,.07);
                border-left: 3px solid {$accent};
                border-radius: 8px;
                margin-bottom: 18px;
            }
            .gtbogo-highlight-label {
                font-size: 10px;
                font-weight: 800;
                letter-spacing: .1em;
                text-transform: uppercase;
                color: rgba(255,255,255,.3);
                margin: 0 0 5px;
            }
            .gtbogo-highlight-value {
                font-size: 15px;
                font-weight: 700;
                color: #fff;
                margin: 0 0 3px;
            }
            .gtbogo-highlight-sub {
                font-size: 12px;
                color: rgba(255,255,255,.35);
                margin: 0;
            }

            /* ── Perk list ── */
            .gtbogo-perk {
                display: flex;
                align-items: flex-start;
                gap: 11px;
                margin-bottom: 9px;
            }
            .gtbogo-perk-check {
                flex-shrink: 0;
                width: 16px;
                height: 16px;
                border-radius: 50%;
                background: rgba(255,255,255,.06);
                border: 1px solid rgba(255,255,255,.12);
                display: flex;
                align-items: center;
                justify-content: center;
                margin-top: 1px;
            }
            .gtbogo-perk-text {
                font-size: 13px;
                color: rgba(255,255,255,.7);
                line-height: 1.5;
                margin: 0;
            }

            /* ── Referral link ── */
            .gtbogo-ref-link {
                background: rgba(255,255,255,.03);
                border: 1px solid rgba(255,255,255,.08);
                border-radius: 8px;
                padding: 13px 16px;
                font-family: 'SF Mono','Fira Code','Courier New',monospace;
                font-size: 12px;
                color: rgba(255,255,255,.6);
                word-break: break-all;
                margin: 14px 0;
                letter-spacing: .02em;
            }
            .gtbogo-btn {
                display: inline-block;
                background: {$accent};
                color: #fff;
                border: none;
                border-radius: 999px;
                padding: 10px 24px;
                font-size: 13px;
                font-weight: 700;
                cursor: pointer;
                letter-spacing: .04em;
                transition: opacity .2s;
                text-decoration: none;
            }
            .gtbogo-btn:hover { opacity: .82; color: #fff; }

            /* ── Coupon rows ── */
            .gtbogo-coupon {
                background: rgba(255,255,255,.02);
                border: 1px solid rgba(255,255,255,.06);
                border-left: 3px solid {$accent};
                border-radius: 8px;
                padding: 13px 16px;
                margin-bottom: 9px;
            }
            .gtbogo-coupon-code {
                font-family: 'SF Mono','Fira Code','Courier New',monospace;
                font-size: 14px;
                font-weight: 700;
                color: #fff;
                letter-spacing: .06em;
                margin: 0 0 3px;
            }
            .gtbogo-coupon-meta { font-size: 12px; color: rgba(255,255,255,.35); margin: 0; }

            /* ── Timeline ── */
            .gtbogo-tl-item {
                display: flex;
                align-items: center;
                justify-content: space-between;
                padding: 11px 0;
                border-bottom: 1px solid rgba(255,255,255,.05);
                gap: 12px;
            }
            .gtbogo-tl-item:last-child { border-bottom: none; }
            .gtbogo-tl-role { font-size: 13px; font-weight: 600; color: rgba(255,255,255,.8); }
            .gtbogo-tl-date { font-size: 11px; color: rgba(255,255,255,.3); white-space: nowrap; }

            /* ── Birthday / Anniversary card ── */
            .gtbogo-occasion-card {
                display: flex;
                align-items: center;
                gap: 20px;
                padding: 20px 24px;
                background: rgba(255,255,255,.03);
                border: 1px solid rgba(255,255,255,.08);
                border-radius: 12px;
                margin-bottom: 14px;
            }
            .gtbogo-occasion-icon {
                width: 44px;
                height: 44px;
                border-radius: 50%;
                border: 1px solid rgba(255,255,255,.1);
                background: rgba(255,255,255,.04);
                display: flex;
                align-items: center;
                justify-content: center;
                flex-shrink: 0;
            }
            .gtbogo-occasion-body { flex: 1; }
            .gtbogo-occasion-title { font-size: 14px; font-weight: 700; color: #fff; margin: 0 0 3px; }
            .gtbogo-occasion-sub { font-size: 12px; color: rgba(255,255,255,.4); margin: 0; }

            /* ── Savings summary ── */
            .gtbogo-savings-total {
                font-size: 36px;
                font-weight: 800;
                color: #fff;
                letter-spacing: -.03em;
                margin: 0 0 4px;
                line-height: 1;
            }
            .gtbogo-savings-sub {
                font-size: 13px;
                color: rgba(255,255,255,.4);
                margin: 0 0 18px;
            }

            @media (max-width: 600px) {
                .gtbogo-summary { grid-template-columns: repeat(2, 1fr); }
                .gtbogo-stat-pair { flex-direction: column; }
                .gtbogo-card { padding: 20px; }
            }
        "
		);
	}

	// ── Main render ───────────────────────────────────────────────────────────

	public function render_page(): void {
		$user_id = get_current_user_id();
		if ( ! $user_id ) {
			return;
		}
		$accent = get_option( 'gtbogo_email_accent_color', '#E10000' );

		// Pre-fetch shared data
		$spend       = $this->get_lifetime_spend( $user_id );
		$order_count = ( (int) count(
			wc_get_orders(
				array(
					'customer_id' => (int) $user_id,
					'limit'       => 200,
					'return'      => 'ids',
					'status'      => array( 'wc-completed', 'wc-processing' ),
				)
			)
		) );
		$total_saved = (float) get_user_meta( $user_id, 'gtbogo_total_savings', true );
		$streak      = (int) get_user_meta( $user_id, 'gtbogo_current_streak', true );
		$tier        = get_user_meta( $user_id, 'gtbogo_loyalty_tier', true ) ?: 'bronze';
		$tier_labels = array(
			'bronze'   => 'Bronze',
			'silver'   => 'Silver',
			'gold'     => 'Gold',
			'platinum' => 'Platinum',
		);
		$tier_label  = $tier_labels[ $tier ] ?? ucfirst( $tier );

		echo '<div class="gtbogo-account-wrap">';
		$this->render_summary( $spend, $order_count, $total_saved, $streak, $tier_label, $accent );
		$this->render_savings_card( $total_saved, $order_count, $accent );
		$this->render_loyalty_card( $user_id, $spend, $tier, $tier_label, $accent );
		$this->render_milestone_card( $user_id, $order_count, $accent );
		$this->render_vip_card( $user_id, $accent );
		$this->render_streak_card( $user_id, $streak, $accent );
		$this->render_occasions_card( $user_id, $accent );
		$this->render_active_deals( $user_id, $accent );
		$this->render_referral_card( $user_id, $accent );
		$this->render_timeline( $user_id, $accent );
		echo '</div>';
		$this->render_scripts();
	}

	// ── 1. Summary strip ──────────────────────────────────────────────────────

	private function render_summary( float $spend, int $orders, float $saved, int $streak, string $tier, string $accent ): void {
		$cells = array(
			array( wp_kses_post( wc_price( $saved ) ), 'Total saved' ),
			array( number_format( $orders ), 'Orders placed' ),
			array( esc_html( $tier ), 'Current tier' ),
			array( $streak > 0 ? $streak . 'wk' : '—', 'Active streak' ),
		);
		echo '<div class="gtbogo-summary">';
		foreach ( $cells as $c ) {
			echo '<div class="gtbogo-summary-cell">';
			echo '<p class="gtbogo-summary-value">' . esc_html( $c[0] ) . '</p>';
			echo '<p class="gtbogo-summary-label">' . esc_html( $c[1] ) . '</p>';
			echo '</div>';
		}
		echo '</div>';
	}

	// ── 2. Total savings card ─────────────────────────────────────────────────

	private function render_savings_card( float $total_saved, int $orders, string $accent ): void {
		if ( $total_saved <= 0 ) {
			return;
		}
		$avg = $orders > 0 ? $total_saved / $orders : 0;
		echo '<div class="gtbogo-card">';
		echo '<span class="gtbogo-label">Your savings</span>';
		echo '<p class="gtbogo-savings-total">' . wp_kses_post( wc_price( $total_saved ) ) . '</p>';
		echo '<p class="gtbogo-savings-sub">saved across ' . esc_html( $orders ) . ' ' . ( $orders === 1 ? 'order' : 'orders' ) . ' — ' . wp_kses_post( wc_price( $avg ) ) . ' average per order</p>';
		echo '<p style="font-size:13px;color:rgba(255,255,255,.4);">All offers applied automatically at checkout. No codes, no friction.</p>';
		echo '</div>';
	}

	// ── 3. Loyalty tier card ──────────────────────────────────────────────────

	private function render_loyalty_card( int $user_id, float $spend, string $tier, string $tier_label, string $accent ): void {
		$tiers      = array(
			'bronze'   => array(
				'label'          => 'Bronze',
				'threshold'      => 0,
				'next_threshold' => 250,
				'deal'           => 'Buy 1 Get 1 at 20% off',
			),
			'silver'   => array(
				'label'          => 'Silver',
				'threshold'      => 250,
				'next_threshold' => 500,
				'deal'           => 'Buy 1 Get 1 at 30% off',
			),
			'gold'     => array(
				'label'          => 'Gold',
				'threshold'      => 500,
				'next_threshold' => 1000,
				'deal'           => 'Buy 2 Get 1 Complimentary',
			),
			'platinum' => array(
				'label'          => 'Platinum',
				'threshold'      => 1000,
				'next_threshold' => null,
				'deal'           => 'Buy 2 Get 2 Complimentary',
			),
		);
		$keys       = array_keys( $tiers );
		$idx        = array_search( $tier, $keys );
		$current    = $tiers[ $tier ] ?? $tiers['bronze'];
		$next_key   = $keys[ $idx + 1 ] ?? null;
		$next       = $next_key ? $tiers[ $next_key ] : null;
		$next_spend = $next ? $next['next_threshold'] ?? $current['next_threshold'] : null;
		$progress   = $next_spend ? min( 100, round( ( $spend / $next_spend ) * 100 ) ) : 100;
		$remaining  = $next_spend ? max( 0, $next_spend - $spend ) : 0;

		echo '<div class="gtbogo-card">';
		echo '<span class="gtbogo-label">Loyalty status</span>';
		echo '<h3>' . esc_html( $tier_label ) . '</h3>';
		echo '<p>Lifetime spend — <strong>' . wp_kses_post( wc_price( $spend ) ) . '</strong></p>';

		if ( $next ) {
			echo '<div class="gtbogo-bar-wrap"><div class="gtbogo-bar-fill" style="width:' . esc_html( $progress ) . '%;"></div></div>';
			echo '<p style="font-size:12px;color:rgba(255,255,255,.35);">' . wp_kses_post( wc_price( $remaining ) ) . ' to ' . esc_html( $next['label'] ) . '</p>';
			echo '<div class="gtbogo-tier-preview">';
			echo '<p class="gtbogo-tier-preview-label">Unlock at ' . esc_html( $next['label'] ) . '</p>';
			echo '<p class="gtbogo-tier-preview-deal">' . esc_html( $next['deal'] ) . '</p>';
			echo '</div>';
		} else {
			echo '<p style="margin-top:12px;"><span class="gtbogo-pill">Highest tier</span></p>';
			echo '<div class="gtbogo-tier-preview">';
			echo '<p class="gtbogo-tier-preview-label">Your current deal</p>';
			echo '<p class="gtbogo-tier-preview-deal">' . esc_html( $current['deal'] ) . '</p>';
			echo '</div>';
		}
		echo '</div>';
	}

	// ── 4. Order milestone card ───────────────────────────────────────────────

	private function render_milestone_card( int $user_id, int $orders, string $accent ): void {
		$milestones = array(
			3  => '5% off',
			5  => '10% off',
			10 => 'Buy 2 Get 1',
			15 => 'Buy 3 Get 1',
			20 => 'VIP access',
			25 => 'Elite status',
		);
		$next_m     = null;
		foreach ( $milestones as $m => $reward ) {
			if ( $orders < $m ) {
				$next_m = array(
					'orders' => $m,
					'reward' => $reward,
				);
				break; }
		}

		echo '<div class="gtbogo-card">';
		echo '<span class="gtbogo-label">Order milestones</span>';

		if ( $next_m ) {
			$left = $next_m['orders'] - $orders;
			echo '<h3>' . esc_html( $orders ) . ' ' . ( $orders === 1 ? 'order' : 'orders' ) . ' placed</h3>';
			echo '<p>' . esc_html( $left ) . ' more ' . ( $left === 1 ? 'order' : 'orders' ) . ' to unlock — <strong>' . esc_html( $next_m['reward'] ) . '</strong></p>';
			$progress = $next_m['orders'] > 0 ? min( 100, round( ( $orders / $next_m['orders'] ) * 100 ) ) : 100;
			echo '<div class="gtbogo-bar-wrap"><div class="gtbogo-bar-fill" style="width:' . esc_html( $progress ) . '%;"></div></div>';
		} else {
			echo '<h3>' . esc_html( $orders ) . ' orders</h3>';
			echo '<p><span class="gtbogo-pill">All milestones reached</span></p>';
		}

		// Milestone nodes
		echo '<div class="gtbogo-milestone-grid">';
		foreach ( $milestones as $m => $reward ) {
			$done = $orders >= $m;
			$next = ! $done && $next_m && $next_m['orders'] === $m;
			$cls  = $done ? 'done' : ( $next ? 'next' : '' );
			echo '<div class="gtbogo-milestone-node ' . esc_html( $cls ) . '">';
			echo '<p class="gtbogo-milestone-num">' . esc_html( $m ) . '</p>';
			echo '<p class="gtbogo-milestone-lbl">' . esc_html( $done ? 'Done' : ( $next ? 'Next' : 'Locked' ) ) . '</p>';
			echo '</div>';
		}
		echo '</div>';
		echo '</div>';
	}

	// ── 5. VIP status card ────────────────────────────────────────────────────

	private function render_vip_card( int $user_id, string $accent ): void {
		$vip_tier = get_user_meta( $user_id, 'gtbogo_vip_tier', true );
		if ( ! $vip_tier ) {
			return;
		}

		$tiers = array(
			'vip_customer'   => array(
				'label' => 'VIP Member',
				'color' => '#818cf8',
				'deal'  => 'Buy 1 Get 1 at 40% off',
				'perks' => array( 'Exclusive VIP deals applied automatically', 'Early access to promotions', 'Priority support', 'VIP indicator in cart and checkout' ),
			),
			'gold_vip'       => array(
				'label' => 'Gold VIP',
				'color' => '#f59e0b',
				'deal'  => 'Buy 1 Get 1 at 40% off — free shipping included',
				'perks' => array( 'All VIP perks included', 'Gold-tier deal — 40% off every second item', 'Free shipping on all orders', 'Gold VIP flash events', 'Gold indicator in cart and checkout' ),
			),
			'platinum_vip'   => array(
				'label' => 'Platinum VIP',
				'color' => '#e2e8f0',
				'deal'  => 'Buy 2 Get 2 Complimentary',
				'perks' => array( 'All Gold VIP perks included', 'Buy 2 Get 2 Complimentary on any order', 'Quarterly Platinum gift', 'Concierge support line', 'Platinum indicator — highest visible standing' ),
			),
			'whale_customer' => array(
				'label' => 'Elite',
				'color' => '#22d3ee',
				'deal'  => 'Custom deal — contact your account manager',
				'perks' => array( 'All Platinum perks included', 'Dedicated account manager', 'Custom deal configuration', 'Annual event access', 'Elite indicator — invitation only' ),
			),
			'lapsed_vip'     => array(
				'label' => 'Status paused',
				'color' => '#6b7280',
				'deal'  => 'Place one order to reactivate all perks',
				'perks' => array( 'Your status is paused', 'All perks restore automatically on next purchase' ),
			),
		);

		$t      = $tiers[ $vip_tier ] ?? array(
			'label' => ucfirst( str_replace( '_', ' ', $vip_tier ) ),
			'color' => $accent,
			'deal'  => '',
			'perks' => array(),
		);
		$date   = get_user_meta( $user_id, 'gtbogo_vip_upgraded_date', true );
		$lapsed = $vip_tier === 'lapsed_vip';
		$orders = ( (int) count(
			wc_get_orders(
				array(
					'customer_id' => (int) $user_id,
					'limit'       => 200,
					'return'      => 'ids',
					'status'      => array( 'wc-completed', 'wc-processing' ),
				)
			)
		) );
		$spend  = $this->get_lifetime_spend( $user_id );

		echo '<div class="gtbogo-card" style="border-color:' . esc_attr( $t['color'] ) . '33;">';
		echo '<span class="gtbogo-label" style="color:' . esc_attr( $t['color'] ) . ';">VIP standing</span>';
		echo '<div style="display:flex;align-items:center;gap:14px;margin-bottom:18px;">';
		echo '<div style="padding:5px 12px;border:1px solid ' . esc_attr( $t['color'] ) . '55;border-radius:4px;font-size:10px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:' . esc_attr( $t['color'] ) . ';">' . esc_html( $t['label'] ) . '</div>';
		if ( $date && ! $lapsed ) {
			echo '<span style="font-size:12px;color:rgba(255,255,255,.3);">Since ' . esc_html( gmdate( 'M Y', strtotime( $date ) ) ) . '</span>';
		}
		echo '</div>';

		// Stats
		echo '<div class="gtbogo-stat-pair">';
		echo '<div class="gtbogo-stat-cell"><p class="gtbogo-stat-val">' . esc_html( $orders ) . '</p><p class="gtbogo-stat-lbl">Orders</p></div>';
		echo '<div class="gtbogo-stat-cell"><p class="gtbogo-stat-val">' . wp_kses_post( wc_price( $spend ) ) . '</p><p class="gtbogo-stat-lbl">Lifetime spend</p></div>';
		echo '</div>';

		// Deal
		if ( $t['deal'] ) {
			echo '<div class="gtbogo-highlight" style="border-left-color:' . esc_attr( $t['color'] ) . ';">';
			echo '<p class="gtbogo-highlight-label">Your deal</p>';
			echo '<p class="gtbogo-highlight-value">' . esc_html( $t['deal'] ) . '</p>';
			echo '<p class="gtbogo-highlight-sub">Applied automatically — no code required</p>';
			echo '</div>';
		}

		// Perks
		if ( ! empty( $t['perks'] ) ) {
			echo '<span class="gtbogo-label" style="color:' . esc_attr( $t['color'] ) . ';margin-bottom:10px;">Included</span>';
			foreach ( $t['perks'] as $perk ) {
				echo '<div class="gtbogo-perk">';
				echo '<div class="gtbogo-perk-check"><svg width="8" height="6" viewBox="0 0 8 6" fill="none"><path d="M1 3L3 5L7 1" stroke="' . esc_attr( $t['color'] ) . '" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></div>';
				echo '<p class="gtbogo-perk-text">' . esc_html( $perk ) . '</p>';
				echo '</div>';
			}
		}
		echo '</div>';
	}

	// ── 6. Purchase streak card ───────────────────────────────────────────────

	private function render_streak_card( int $user_id, int $streak, string $accent ): void {
		if ( $streak < 1 ) {
			return;
		}
		$best       = (int) get_user_meta( $user_id, 'gtbogo_best_streak', true );
		$milestones = array(
			4  => '5% bonus',
			8  => '10% bonus',
			12 => '20% bonus',
			52 => 'Annual reward',
		);
		$next_m     = null;
		foreach ( $milestones as $m => $reward ) {
			if ( $streak < $m ) {
				$next_m = array(
					'm'      => $m,
					'reward' => $reward,
				);
				break; }
		}

		echo '<div class="gtbogo-card">';
		echo '<span class="gtbogo-label">Purchase streak</span>';
		echo '<h3>' . esc_html( $streak ) . ' ' . ( $streak === 1 ? 'week' : 'weeks' ) . ' in a row</h3>';
		echo '<p>Personal best — <strong>' . esc_html( $best ) . ' weeks</strong></p>';
		if ( $next_m ) {
			$left = $next_m['m'] - $streak;
			echo '<div class="gtbogo-bar-wrap"><div class="gtbogo-bar-fill" style="width:' . esc_attr( round( ( $streak / $next_m['m'] ) * 100 ) ) . '%;"></div></div>';
			echo '<p style="font-size:12px;color:rgba(255,255,255,.35);">' . esc_html( $left ) . ' more ' . ( $left === 1 ? 'week' : 'weeks' ) . ' to unlock ' . esc_html( $next_m['reward'] ) . '</p>';
		} else {
			echo '<p style="margin-top:12px;"><span class="gtbogo-pill">Streak champion</span></p>';
		}
		echo '</div>';
	}

	// ── 7. Birthday & anniversary card ────────────────────────────────────────

	private function render_occasions_card( int $user_id, string $accent ): void {
		$birthday    = get_user_meta( $user_id, 'gtbogo_birthday', true );
		$anniversary = get_user_meta( $user_id, 'gtbogo_first_order_date', true );
		if ( ! $birthday && ! $anniversary ) {
			return;
		}

		$shown = false;

		if ( $birthday ) {
			$bday_dt = \DateTime::createFromFormat( 'm-d', $birthday );
			$now     = new \DateTime();
			$days_to = null;
			if ( $bday_dt ) {
				$this_year = \DateTime::createFromFormat( 'Y-m-d', $now->format( 'Y' ) . '-' . $birthday );
				if ( $this_year && $this_year < $now ) {
					$this_year->modify( '+1 year' );
				}
				if ( $this_year ) {
					$days_to = (int) $now->diff( $this_year )->days;
				}
			}
			$sub = $days_to !== null
				? ( $days_to === 0 ? 'Your birthday deal is active today' : 'Birthday deal activates in ' . esc_html( $days_to ) . ' ' . ( $days_to === 1 ? 'day' : 'days' ) )
				: 'Birthday deal activates on your birthday';

			echo '<div class="gtbogo-occasion-card">';
			echo '<div class="gtbogo-occasion-icon"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><rect x="4" y="8" width="12" height="10" rx="2" stroke="rgba(255,255,255,.4)" stroke-width="1.2"/><path d="M10 8V5M7 8V6M13 8V6" stroke="rgba(255,255,255,.4)" stroke-width="1.2" stroke-linecap="round"/><path d="M7 12h6" stroke="rgba(255,255,255,.4)" stroke-width="1.2" stroke-linecap="round"/></svg></div>';
			echo '<div class="gtbogo-occasion-body">';
			echo '<p class="gtbogo-occasion-title">Birthday offer</p>';
			echo '<p class="gtbogo-occasion-sub">' . esc_html( $sub ) . '</p>';
			echo '</div></div>';
			$shown = true;
		}

		if ( $anniversary ) {
			$ann_dt   = new \DateTime( $anniversary );
			$now      = new \DateTime();
			$years    = (int) $now->diff( $ann_dt )->y;
			$next_ann = clone $ann_dt;
			$next_ann->modify( '+' . ( $years + 1 ) . ' years' );
			$days_to = (int) $now->diff( $next_ann )->days;
			$sub     = $days_to === 0
				? 'Your anniversary deal is active today — ' . ( $years + 1 ) . ' year' . ( ( $years + 1 ) === 1 ? '' : 's' )
				: 'Anniversary deal in ' . esc_html( $days_to ) . ' ' . ( $days_to === 1 ? 'day' : 'days' ) . ' — ' . ( $years + 1 ) . ' year' . ( ( $years + 1 ) === 1 ? '' : 's' );

			echo '<div class="gtbogo-occasion-card">';
			echo '<div class="gtbogo-occasion-icon"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="7" stroke="rgba(255,255,255,.4)" stroke-width="1.2"/><path d="M10 6v4l3 2" stroke="rgba(255,255,255,.4)" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>';
			echo '<div class="gtbogo-occasion-body">';
			echo '<p class="gtbogo-occasion-title">Anniversary offer</p>';
			echo '<p class="gtbogo-occasion-sub">' . esc_html( $sub ) . '</p>';
			echo '</div></div>';
		}
	}

	// ── 8. Active deals card ──────────────────────────────────────────────────

	private function render_active_deals( int $user_id, string $accent ): void {
		global $wpdb;
		$rules = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQLPlaceholders.UnnecessaryPrepare
			"SELECT name, buy_quantity, get_quantity, discount_value, discount_type
             FROM {$wpdb->prefix}gtbogo_rules
             WHERE status = 'active'
             ORDER BY priority ASC LIMIT 10",
			ARRAY_A
		);
		if ( empty( $rules ) ) {
			return;
		}

		echo '<div class="gtbogo-card">';
		echo '<span class="gtbogo-label">Your active deals</span>';
		echo '<p style="margin-bottom:18px;font-size:13px;color:rgba(255,255,255,.4);">Applied automatically when you qualify at checkout. No code required.</p>';
		foreach ( $rules as $rule ) {
			$disc  = (float) $rule['discount_value'];
			$type  = $rule['discount_type'] ?? 'percent';
			$label = $disc >= 100 ? 'Complimentary' : ( $type === 'percent' ? $disc . '% off' : wp_kses_post( wc_price( $disc ) ) . ' off' );
			$buy   = (int) $rule['buy_quantity'];
			$get   = (int) $rule['get_quantity'];
			echo '<div class="gtbogo-deal-row">';
			echo '<div>';
			echo '<p style="margin:0 0 4px;font-size:14px;font-weight:600;color:#fff;">' . esc_html( $rule['name'] ) . '</p>';
			echo '<p style="margin:0;font-size:12px;font-weight:700;letter-spacing:.03em;color:' . esc_attr( $accent ) . ';">Buy ' . esc_html( $buy ) . ' Get ' . esc_html( $get ) . ' — ' . esc_html( $label ) . '</p>';
			echo '</div>';
			echo '<span class="gtbogo-pill">Auto-applied</span>';
			echo '</div>';
		}
		echo '</div>';
	}

	// ── 9. Referral card ──────────────────────────────────────────────────────

	private function render_referral_card( int $user_id, string $accent ): void {
		$count   = (int) get_user_meta( $user_id, 'gtbogo_referral_count', true );
		$code    = get_user_meta( $user_id, 'gtbogo_referral_code', true );
		$coupons = get_user_meta( $user_id, 'gtbogo_referral_coupons', true ) ?: array();

		if ( ! $code ) {
			$user = get_userdata( $user_id );
			$code = strtoupper( substr( $user->user_login, 0, 6 ) ) . '-' . strtoupper( substr( md5( $user_id ), 0, 6 ) );
			update_user_meta( $user_id, 'gtbogo_referral_code', $code );
		}
		$ref_url = add_query_arg( 'ref', $code, home_url( '/' ) );

		echo '<div class="gtbogo-card">';
		echo '<span class="gtbogo-label">Referral program</span>';
		echo '<h3>' . esc_html( $count ) . ' ' . ( $count === 1 ? 'referral' : 'referrals' ) . '</h3>';
		echo '<p>Share your link. When a friend places their first order, you both receive a reward automatically.</p>';
		echo '<div class="gtbogo-ref-link" id="gtbogo-ref-link">' . esc_url( $ref_url ) . '</div>';
		echo '<button class="gtbogo-btn" onclick="gtbogoCopyRef()">Copy link</button>';

		if ( ! empty( $coupons ) ) {
			echo '<span class="gtbogo-label" style="margin-top:24px;">Referral rewards</span>';
			foreach ( array_reverse( $coupons ) as $coupon ) {
				$val = $coupon['type'] === 'percent' ? $coupon['value'] . '% off' : wp_kses_post( wc_price( $coupon['value'] ) ) . ' off';
				echo '<div class="gtbogo-coupon">';
				echo '<p class="gtbogo-coupon-code">' . esc_html( $coupon['code'] ) . '</p>';
				echo '<p class="gtbogo-coupon-meta">' . esc_html( $val ) . ' — expires ' . esc_html( $coupon['expires'] ) . '</p>';
				echo '</div>';
			}
		}
		echo '</div>';
	}

	// ── 10. Rewards history timeline ──────────────────────────────────────────

	private function render_timeline( int $user_id, string $accent ): void {
		$timeline = get_user_meta( $user_id, 'gtbogo_role_timeline', true );
		if ( empty( $timeline ) ) {
			return;
		}

		$labels = array(
			'order_3'            => '3rd order reward',
			'order_5'            => '5th order reward',
			'order_10'           => '10th order reward',
			'order_15'           => '15th order reward',
			'order_20'           => '20th order reward',
			'order_25'           => '25th order reward',
			'streak_4'           => '4-week streak',
			'streak_8'           => '8-week streak',
			'streak_12'          => '12-week streak',
			'streak_52'          => '52-week streak',
			'vip_customer'       => 'VIP standing unlocked',
			'gold_vip'           => 'Gold VIP unlocked',
			'platinum_vip'       => 'Platinum VIP unlocked',
			'whale_customer'     => 'Elite status',
			'silver_member'      => 'Silver tier',
			'gold_member'        => 'Gold tier',
			'lapsed_90'          => 'Away for 90 days',
			'anniversary_member' => 'Anniversary reward',
			'birthday_member'    => 'Birthday reward',
			'referrer_1'         => 'First referral',
			'referrer_3'         => '3 referrals',
			'referrer_5'         => '5 referrals',
			'subscriber_active'  => 'Subscription active',
			'subscriber_at_risk' => 'Subscription renewal due',
		);

		echo '<div class="gtbogo-card">';
		echo '<span class="gtbogo-label">History</span>';
		foreach ( array_reverse( array_slice( $timeline, -20 ) ) as $event ) {
			$lbl  = $labels[ $event['role'] ] ?? ucfirst( str_replace( '_', ' ', $event['role'] ) );
			$date = gmdate( 'M j, Y', strtotime( $event['date'] ) );
			echo '<div class="gtbogo-tl-item">';
			echo '<span class="gtbogo-tl-role">' . esc_html( $lbl ) . '</span>';
			echo '<span class="gtbogo-tl-date">' . esc_html( $date ) . '</span>';
			echo '</div>';
		}
		echo '</div>';
	}

	// ── Helpers ───────────────────────────────────────────────────────────────

	// ── AJAX: Live portal refresh ────────────────────────────────────────────────

	public function ajax_portal_refresh(): void {
		// Security — logged-in users only, valid nonce
		if ( ! current_user_can( 'read' ) ) { wp_send_json_error( array( 'message' => 'Unauthorized' ), 403 ); }
		if ( ! check_ajax_referer( 'gtbogo_portal_nonce', 'nonce', false ) ) {
			wp_send_json_error( array( 'message' => 'Invalid request.' ), 403 );
		}
		$user_id = get_current_user_id();
		if ( ! $user_id ) {
			wp_send_json_error( array( 'message' => 'Not authenticated.' ), 403 );
		}

		// Gather fresh live data
		$spend       = $this->get_lifetime_spend( $user_id );
		$orders      = ( (int) count(
			wc_get_orders(
				array(
					'customer_id' => (int) $user_id,
					'limit'       => 200,
					'return'      => 'ids',
					'status'      => array( 'wc-completed', 'wc-processing' ),
				)
			)
		) );
		$saved       = (float) get_user_meta( $user_id, 'gtbogo_total_savings', true );
		$streak      = (int) get_user_meta( $user_id, 'gtbogo_current_streak', true );
		$tier        = get_user_meta( $user_id, 'gtbogo_loyalty_tier', true ) ?: 'bronze';
		$tier_labels = array(
			'bronze'   => 'Bronze',
			'silver'   => 'Silver',
			'gold'     => 'Gold',
			'platinum' => 'Platinum',
		);
		$tier_label  = $tier_labels[ $tier ] ?? ucfirst( $tier );
		$vip_tier    = get_user_meta( $user_id, 'gtbogo_vip_tier', true );
		$referrals   = (int) get_user_meta( $user_id, 'gtbogo_referral_count', true );

		// Latest order — for real-time detection
		global $wpdb;
		if ( class_exists( 'GT_BOGO_Order_Query' ) && GT_BOGO_Order_Query::is_hpos() ) {
			$ot         = GT_BOGO_Order_Query::orders_table();
			$om         = GT_BOGO_Order_Query::meta_table();
			$sc         = GT_BOGO_Order_Query::status_col();
			$dc         = GT_BOGO_Order_Query::date_col();
			$last_order = $wpdb->get_row(
				$wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
					"SELECT o.id AS ID, o.{$dc} AS post_date, om.meta_value AS total
                 FROM {$ot} o
                 JOIN {$om} om ON om.order_id = o.id AND om.meta_key = '_order_total'
                 WHERE o.type = 'shop_order'
                   AND o.{$sc} IN ('wc-completed','wc-processing')
                   AND o.customer_id = %d
                 ORDER BY o.{$dc} DESC LIMIT 1",
					$user_id
				)
			);
		} else {
			$last_order = $wpdb->get_row(
				$wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQLPlaceholders.UnnecessaryPrepare
					"SELECT p.ID, p.post_date, pm.meta_value AS total
                 FROM {$wpdb->posts} p
                 JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = '_order_total'
                 WHERE p.post_type = 'shop_order'
                 AND p.post_status IN ('wc-completed','wc-processing')
                 AND p.post_author = %d
                 ORDER BY p.post_date DESC LIMIT 1",
					$user_id
				)
			);
		}

		// Active rules count
		$active_rules = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQLPlaceholders.UnnecessaryPrepare
			"SELECT COUNT(*) FROM {$wpdb->prefix}gtbogo_rules WHERE status = 'active'"
		);

		wp_send_json_success(
			array(
				'summary'      => array(
					'saved'  => wp_kses_post( wc_price( $saved ) ),
					'orders' => number_format( $orders ),
					'tier'   => esc_html( $tier_label ),
					'streak' => $streak > 0 ? $streak . 'wk' : '—',
				),
				'spend'        => wp_kses_post( wc_price( $spend ) ),
				'orders'       => $orders,
				'saved'        => wp_kses_post( wc_price( $saved ) ),
				'streak'       => $streak,
				'tier'         => $tier_label,
				'vip_tier'     => $vip_tier ?: '',
				'referrals'    => $referrals,
				'active_rules' => $active_rules,
				'last_order'   => $last_order ? array(
					'id'    => (int) $last_order->ID,
					'date'  => gmdate( 'M j, Y g:i a', strtotime( $last_order->post_date ) ),
					'total' => wp_kses_post( wc_price( $last_order->total ) ),
				) : null,
				'timestamp'    => time(),
			)
		);
	}

	private function get_lifetime_spend( int $user_id ): float {
		$orders = wc_get_orders(
			array(
				'customer_id' => $user_id,
				'status'      => array( 'completed', 'processing' ),
				'limit'       => 200,
			)
		);
		return array_sum( array_map( fn( $o ) => $o->get_total(), $orders ) );
	}

	private function render_scripts(): void {
		$nonce = wp_create_nonce( 'gtbogo_portal_nonce' );
		$ajax  = admin_url( 'admin-ajax.php' );
		echo '<script>
// ── Copy referral link ───────────────────────────────────────────────────────
function gtbogoCopyRef() {
    var t = document.getElementById("gtbogo-ref-link");
    if (!t) return;
    var text = t.innerText.trim();
    if (navigator.clipboard) {
        navigator.clipboard.writeText(text).then(function() { gtbogoCopied(); });
    } else {
        // navigator.clipboard unavailable (non-HTTPS or very old browser)
        // WooCommerce requires HTTPS so this branch should never fire in production
        gtbogoCopied();
    }
}
function gtbogoCopied() {
    var b = document.querySelector(".gtbogo-btn");
    if (b) { b.textContent = "Copied"; setTimeout(function(){ b.textContent = "Copy link"; }, 2000); }
}

// ── Live portal polling — 30 second interval ─────────────────────────────────
(function() {
    var _nonce   = ' . wp_json_encode( $nonce ) . ';
    var _ajax    = ' . wp_json_encode( $ajax ) . ';
    var _lastId  = 0;
    var _polling = true;

    function gtbogoUpdateSummary(data) {
        var s = data.summary;
        if (!s) return;
        // Update summary strip cells
        var cells = document.querySelectorAll(".gtbogo-summary-value");
        if (cells[0]) cells[0].textContent = s.saved;
        if (cells[1]) cells[1].textContent = s.orders;
        if (cells[2]) cells[2].textContent = s.tier;
        if (cells[3]) cells[3].textContent = s.streak;

        // If a new order appeared — flash the summary strip
        if (data.last_order && data.last_order.id && data.last_order.id !== _lastId) {
            if (_lastId > 0) {
                // Real new order detected — show toast
                gtbogoToast("Order placed — " + data.last_order.total + " on " + data.last_order.date);
            }
            _lastId = data.last_order.id;
        }
    }

    function gtbogoToast(msg) {
        var el = document.createElement("div");
        el.style.cssText = "position:fixed;bottom:24px;right:24px;background:rgba(0,0,0,.85);border:1px solid rgba(255,255,255,.12);border-radius:8px;padding:14px 20px;font-size:13px;color:#fff;z-index:9999;font-family:-apple-system,BlinkMacSystemFont,sans-serif;box-shadow:0 8px 32px rgba(0,0,0,.4);max-width:320px;line-height:1.5;";
        el.textContent = msg;
        document.body.appendChild(el);
        setTimeout(function() { el.style.opacity="0"; el.style.transition="opacity .4s"; setTimeout(function(){ el.remove(); }, 400); }, 4000);
    }

    function gtbogoPoll() {
        if (!_polling) return;
        var fd = new FormData();
        fd.append("action", "gtbogo_portal_refresh");
        fd.append("nonce",  _nonce);
        fetch(_ajax, { method:"POST", body:fd, credentials:"same-origin" })
            .then(function(r){ return r.json(); })
            .then(function(d){
                if (d.success && d.data) gtbogoUpdateSummary(d.data);
            })
            .catch(function(){}) // silent fail — no console noise
            .finally(function(){
                setTimeout(gtbogoPoll, 8000); // 8 second interval
            });
    }

    // Start polling after 5s delay — let page settle
    setTimeout(function(){
        // Capture initial last order ID to detect new orders
        var fd = new FormData();
        fd.append("action", "gtbogo_portal_refresh");
        fd.append("nonce",  _nonce);
        fetch(_ajax, { method:"POST", body:fd, credentials:"same-origin" })
            .then(function(r){ return r.json(); })
            .then(function(d){
                if (d.success && d.data && d.data.last_order) {
                    _lastId = d.data.last_order.id;
                }
                // Begin polling loop
                setTimeout(gtbogoPoll, 8000);
            });
    }, 2000);

    // Stop polling when tab is hidden — resume when visible
    document.addEventListener("visibilitychange", function() {
        _polling = !document.hidden;
        if (_polling) setTimeout(gtbogoPoll, 500);
    });

    // Referral cookie
    var m = location.search.match(/[?&]ref=([A-Z0-9\-]+)/i);
    if (m) document.cookie = "gtbogo_ref=" + m[1] + "; path=/; max-age=2592000";
})();
</script>';
	}

	// ── Referral tracking ─────────────────────────────────────────────────────

	public function set_referral_cookie(): void {
		if ( isset( $_GET['ref'] ) && ! headers_sent() ) {
			$code = sanitize_text_field( $_GET['ref'] );
			if ( $code ) {
				setcookie( 'gtbogo_ref', $code, time() + ( 30 * DAY_IN_SECONDS ), COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
				$_COOKIE['gtbogo_ref'] = $code;
			}
		}
	}

	public function track_referred_registration( int $user_id ): void {
		$ref_code = sanitize_text_field( $_GET['ref'] ?? $_COOKIE['gtbogo_ref'] ?? '' );
		if ( ! $ref_code ) {
			return;
		}
		update_user_meta( $user_id, 'gtbogo_referred_by_code', $ref_code );
		update_user_meta( $user_id, 'gtbogo_referred_date', current_time( 'mysql', true ) );
		if ( class_exists( 'GT_BOGO_Role_Manager' ) ) {
			GT_BOGO_Role_Manager::assign( $user_id, 'referred_customer' );
		}
	}

	public function track_referral_conversion( int $order_id ): void {
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return;
		}
		$user_id = (int) $order->get_customer_id();
		if ( ! $user_id ) {
			return;
		}
		if ( ( (int) count(
			wc_get_orders(
				array(
					'customer_id' => (int) $user_id,
					'limit'       => 200,
					'return'      => 'ids',
					'status'      => array( 'wc-completed', 'wc-processing' ),
				)
			)
		) ) > 1 ) {
			return;
		}
		if ( $order->get_meta( '_gtbogo_referral_conversion_done', true ) ) {
			return;
		}
		$ref_code = get_user_meta( $user_id, 'gtbogo_referred_by_code', true );
		if ( ! $ref_code ) {
			return;
		}
		$referrers = get_users(
			array(
				'meta_key'   => 'gtbogo_referral_code',
				'meta_value' => $ref_code,
				'number'     => 1,
				'fields'     => 'ID',
			)
		); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key,WordPress.DB.SlowDBQuery.slow_db_query_meta_value,WordPress.DB.SlowDBQuery.slow_db_query_meta_query,WordPress.DB.SlowDBQuery.slow_db_query_tax_query
		if ( empty( $referrers ) ) {
			return;
		}
		$referrer_id   = (int) $referrers[0];
		$referred_user = get_userdata( $user_id );
		$reward_label  = get_option( 'gtbogo_referral_reward_label', 'Buy 2 Get 1 Complimentary on your next order' );
		do_action( 'gtbogo_referral_success', $referrer_id, $referred_user->display_name ?? 'a friend', $reward_label );
		if ( class_exists( 'GT_BOGO_Role_Manager' ) ) {
			GT_BOGO_Role_Manager::instance()->on_referral( $referrer_id, $referred_user->display_name ?? '', $reward_label );
		}
		$order->update_meta_data( '_gtbogo_referral_conversion_done', '1' );
		$order->save_meta_data();
	}
}
