/**
 * Alpine.js components for drawer sections
 * Handles Earn Points, Spend Points, and Referrals drawer functionality
 *
 * @since 1.1.0
 */

/**
 * Resolve the store's currency symbol placement from the localized admin config.
 *
 * Symbol position ('before'/'after') and whether a non-breaking space separates the
 * symbol from the amount are decided server-side (see PHP get_currency_position()) and
 * passed through window.rewardswpConfig, so admin previews match the storefront exactly.
 *
 * @returns {{position: string, space: boolean}} Resolved placement.
 */
export function getCurrencyPosition() {
	const config = window.rewardswpConfig || {};

	return {
		position: config.currencyPosition === 'after' ? 'after' : 'before',
		space: Boolean(config.currencySpace),
	};
}

/**
 * Place a currency symbol before or after an amount per the store setting.
 *
 * Mirrors the PHP apply_currency_symbol() helper: a euro store renders "5 €" while a
 * dollar store renders "$5". The amount is used as-is, so the caller controls number
 * formatting.
 *
 * @param {string}        symbol - The currency symbol (e.g. "€", "$").
 * @param {string|number} amount - The already-formatted amount, without a symbol.
 * @returns {string} The assembled string (e.g. "$5", "5 €").
 */
export function applyCurrencySymbol(symbol, amount) {
	const { position, space } = getCurrencyPosition();
	const separator = space ? ' ' : ''; // Non-breaking space keeps "5 €" together.

	return position === 'after' ? `${amount}${separator}${symbol}` : `${symbol}${separator}${amount}`;
}

/**
 * Format a currency amount with symbol.
 * Hides decimals when they are .00 for cleaner display, then applies the store's
 * symbol placement (see applyCurrencySymbol()).
 *
 * @param {number} amount - The monetary amount.
 * @param {string} symbol - The currency symbol.
 * @returns {string} Formatted currency string (e.g., "$500", "50,99 €").
 */
function formatCurrency(amount, symbol = '$') {
	const numAmount = parseFloat(amount) || 0;

	// Hide decimals if they are .00
	const formatted =
		Number.isInteger(numAmount) || numAmount === Math.floor(numAmount)
			? `${Math.floor(numAmount)}`
			: numAmount.toFixed(2);

	return applyCurrencySymbol(symbol, formatted);
}

/**
 * Check if drawer has a selected action with data.
 * Since selectedAction is initialized as {} instead of null, we need to check if it has any keys.
 *
 * @param {Object} drawer - The drawer store object.
 * @returns {boolean} True if drawer has a selected action with data.
 */
function hasSelectedAction(drawer) {
	return drawer && drawer.selectedAction && Object.keys(drawer.selectedAction).length > 0;
}

/**
 * Register all drawer section components with Alpine
 *
 * @param {Object} Alpine - Alpine.js instance
 */
export function registerDrawerSections(Alpine) {

	/**
	 * Earn Actions Global Store
	 * Accessible from anywhere via $store.earnActions
	 */
	Alpine.store('earnActions', {
		all: {}, // All action definitions from backend
		enabled: {}, // Only configured actions (configured: true)
		sortIteration: 0, // Increment to force x-for re-render after sort
		currencySingular: 'point',
		currencyPlural: 'points',
		currencySymbol: '$',
		iconMap: {},

		// Referral mirror card data
		referralsEnabled: false,
		advocateReward: null,
		referralOrder: 0, // Order position for referral mirror card.

		// Script data (additional configuration from backend)
		scriptData: {},

		// Getter that returns enabled actions as array sorted by order (including referral mirror card).
		get enabledSorted() {
			const actions = Object.entries(this.enabled)
				.sort((a, b) => (a[1].order || 0) - (b[1].order || 0))
				.map(([key, action]) => ({ ...action, id: key }));

			// If referral mirror should show, include it in the sorted list.
			if (this.shouldShowReferralMirror()) {
				const referralAction = {
					id: 'referral_mirror',
					isMirror: true,
					order: this.referralOrder,
				};
				actions.push(referralAction);
				actions.sort((a, b) => (a.order || 0) - (b.order || 0));
			}

			return actions;
		},

		setup(data) {
			this.all = data.earnActions || {};
			this.currencySingular = data.currencySingular || 'point';
			this.currencyPlural = data.currencyPlural || 'points';
			this.currencySymbol = data.currencySymbol || '$';
			this.iconMap = data.iconMap || {};
			this.referralsEnabled = data.referralsEnabled || false;
			this.advocateReward = data.advocateReward || null;
			this.referralOrder = data.referralOrder ?? 0;
			this.scriptData = {
				isEdd: data.isEdd || false,
				eddReviewsStatus: data.eddReviewsStatus || null,
				defaultBaseAmount: data.defaultBaseAmount || 1,
			};

			// Listen for referrals program toggle events.
			window.addEventListener('referrals-program-toggled', (e) => {
				this.referralsEnabled = e.detail.enabled;
			});

			this.processEnabledActions();
		},

		processEnabledActions() {
			this.enabled = {};
			const actions = this.all;

			if (Array.isArray(actions)) {
				actions.forEach((action) => {
					if (
						action.configured === true ||
						action.configured === 1 ||
						action.configured === '1'
					) {
						this.enabled[action.id] = action;
					}
				});
			} else if (typeof actions === 'object') {
				Object.entries(actions).forEach(([actionId, action]) => {
					if (
						action.configured === true ||
						action.configured === 1 ||
						action.configured === '1'
					) {
						this.enabled[actionId] = action;
					}
				});
			}
		},

		add(actionId, actionData) {
			// If there's a pending reorder, apply it first before adding the new item.
			if (window._pendingEarnOrder && window._pendingEarnOrder.length > 0) {
				const updatedEnabled = { ...this.enabled };
				window._pendingEarnOrder.forEach((id, index) => {
					if (updatedEnabled[id]) {
						updatedEnabled[id] = { ...updatedEnabled[id], order: index };
					}
				});
				this.enabled = Alpine.reactive(updatedEnabled);

				// Update referralOrder if referral_mirror is in pending order.
				const referralIndex = window._pendingEarnOrder.indexOf('referral_mirror');
				if (referralIndex !== -1) {
					this.referralOrder = referralIndex;
				}

				// Clear pending order and increment sortIteration to sync DOM.
				window._pendingEarnOrder = null;
				this.sortIteration++;
			}

			// Get the max order value to put new action at the end
			const maxOrder = Object.values(this.enabled).reduce(
				(max, action) => Math.max(max, action.order || 0),
				-1
			);

			// Use Alpine.reactive to ensure reactivity is maintained
			this.enabled = Alpine.reactive({
				...this.enabled,
				[actionId]: { ...actionData, configured: true, order: maxOrder + 1 },
			});
		},

		update(actionId, updatedAction) {
			if (this.enabled[actionId]) {
				// Preserve the existing order value.
				const existingOrder = this.enabled[actionId].order;

				this.enabled = Alpine.reactive({
					...this.enabled,
					[actionId]: {
						...updatedAction,
						configured: true,
						order: existingOrder,
					},
				});

				// Notify main component of state change for dirty tracking.
				window.dispatchEvent(new CustomEvent('rewardswp:settings-changed'));
			}
		},

		remove(actionId) {
			// If there's a pending reorder, apply it first before removing.
			if (window._pendingEarnOrder && window._pendingEarnOrder.length > 0) {
				const updatedEnabled = { ...this.enabled };
				window._pendingEarnOrder.forEach((id, index) => {
					if (updatedEnabled[id]) {
						updatedEnabled[id] = { ...updatedEnabled[id], order: index };
					}
				});
				this.enabled = Alpine.reactive(updatedEnabled);

				// Update referralOrder if referral_mirror is in pending order.
				const referralIndex = window._pendingEarnOrder.indexOf('referral_mirror');
				if (referralIndex !== -1) {
					this.referralOrder = referralIndex;
				}

				// Clear pending order and increment sortIteration to sync DOM.
				window._pendingEarnOrder = null;
				this.sortIteration++;
			}

			// Use Alpine.reactive to ensure reactivity is maintained
			const { [actionId]: removed, ...rest } = this.enabled;
			this.enabled = Alpine.reactive(rest);

			// Notify main component of state change for dirty tracking
			window.dispatchEvent(new CustomEvent('rewardswp:settings-changed'));
		},

		toggle(actionId) {
			if (this.enabled[actionId]) {
				const oldValue = this.enabled[actionId].enabled;

				// Use Alpine.reactive to ensure reactivity is maintained
				this.enabled = Alpine.reactive({
					...this.enabled,
					[actionId]: {
						...this.enabled[actionId],
						enabled: !this.enabled[actionId].enabled,
					},
				});

				// Notify main component of state change for dirty tracking
				window.dispatchEvent(new CustomEvent('rewardswp:settings-changed'));
			}
		},

		isAdded(actionId) {
			return !!this.enabled[actionId];
		},

		getArray() {
			return Object.values(this.enabled);
		},

		getActionIcon(iconName) {
			return this.iconMap?.[iconName] || '';
		},

		getActionDetails(action) {
			// Return formatted details based on action type
			if (action.id === 'purchase' && action.points_per_dollar) {
				const pointsWord =
					action.points_per_dollar === 1 ? this.currencySingular : this.currencyPlural;
				// Use base_amount if set, otherwise use currency-appropriate default
				const baseAmount = action.base_amount || this.scriptData?.defaultBaseAmount || 1;
				const formattedBaseAmount = baseAmount.toLocaleString();
				return `${action.points_per_dollar} ${pointsWord} per ${applyCurrencySymbol(this.currencySymbol, formattedBaseAmount)} spent`;
			}
			if (action.points) {
				const pointsWord =
					action.points === 1 ? this.currencySingular : this.currencyPlural;
				return `${action.points} ${pointsWord}`;
			}
			return '';
		},

		/**
		 * Check if referral mirror card should be shown
		 * Show if: referrals enabled AND advocate reward type is points
		 */
		shouldShowReferralMirror() {
			return (
				this.referralsEnabled &&
				this.advocateReward &&
				this.advocateReward.type === 'points'
			);
		},

		/**
		 * Get the description for the referral points mirror card
		 */
		getReferralPointsDescription() {
			if (!this.advocateReward || !this.advocateReward.reward_amount) {
				return 'Earn points when your referral is completed';
			}
			const points = parseInt(this.advocateReward.reward_amount) || 0;
			return `Earn ${points} ${this.currencyPlural} when your referral is completed`;
		},
	});

	/**
	 * Spend Actions Global Store
	 * Accessible from anywhere via $store.spendActions
	 * Uses ARRAY (not object) since can have multiple instances of same reward type
	 */
	Alpine.store('spendActions', {
		rewards: [], // Array of all rewards (can have multiple instances of same type)
		sortIteration: 0, // Increment to force x-for re-render after sort
		currencyPlural: 'points',
		currencySymbol: '$',
		iconMap: {},
		addTitle: 'Add ways to redeem',
		addSubtitle: 'Choose a reward type for members to redeem',
		createButtonText: 'Create',
		labelTemplates: {},

		setup(data) {
			let rewardsData = data.spendActions || [];

			// Convert object to array if needed (PHP returns object, we need array).
			if (!Array.isArray(rewardsData) && typeof rewardsData === 'object') {
				// Get all values from the object.
				const allRewards = Object.values(rewardsData);

				// Filter for only configured rewards (rewards that have been saved).
				// If a reward has configured: true OR enabled: true, it's been saved before.
				rewardsData = allRewards.filter((r) => {
					return r.configured === true || r.enabled === true;
				});
			}

			this.currencyPlural = data.currencyPlural || 'points';
			this.currencySymbol = data.currencySymbol || '$';
			this.iconMap = data.iconMap || {};
			this.addTitle = data.addTitle || 'Add ways to redeem';
			this.addSubtitle = data.addSubtitle || 'Choose a reward type for members to redeem';
			this.createButtonText = data.createButtonText || 'Create';
			this.labelTemplates = data.labelTemplates || {};

			// Regenerate labels for all existing rewards.
			if (Array.isArray(rewardsData)) {
				this.rewards = rewardsData.map((reward) => this.updateRewardLabels(reward));
			} else {
				this.rewards = rewardsData;
			}
		},

		generateRewardLabel(reward) {
			if (!this.labelTemplates) return '';

			const symbol = this.currencySymbol || '$';
			const amount = reward.coupon_amount || 0;

			switch (reward.type) {
				case 'fixed_discount':
					return this.labelTemplates.fixed_coupon.replace('%s', applyCurrencySymbol(symbol, amount));
				case 'percentage_discount':
					return this.labelTemplates.percentage_coupon.replace('%s', amount);
				case 'free_shipping':
					return this.labelTemplates.free_shipping;
				case 'free_product':
					// Check if product_value is set (limits the discount amount).
					const productValue = parseFloat(reward.product_value || 0);
					const hasValueLimit = productValue > 0;
					let productName = '';

					// Priority: reward_description > product name > fallback
					if (reward.reward_description && reward.reward_description.trim()) {
						productName = reward.reward_description;
					} else if (reward.products_included && reward.products_included.length > 0 && reward.products_included[0].name) {
						productName = reward.products_included[0].name;
					}

					if (productName) {
						if (hasValueLimit) {
							return `${formatCurrency(productValue, symbol)} off ${productName}`;
						}
						return `Free ${productName}`;
					}

					// Fallback when no product name.
					if (hasValueLimit) {
						return `${formatCurrency(productValue, symbol)} off product`;
					}
					return this.labelTemplates.free_product;
				default:
					return '';
			}
		},

		generatePointsCostLabel(reward) {
			if (!this.labelTemplates) return '';

			const points = reward.points_required || reward.points || 0;
			const currency =
				points === 1
					? this.currencyPlural.replace(/s$/, '') // Crude singular
					: this.currencyPlural;

			// For free_product, just return the points text.
			// The product name is already shown in the reward title (e.g., "Free Macbook"),
			// so we don't repeat it here to avoid grammatical issues with articles (a/an).
			return this.labelTemplates.points_cost
				.replace('%1$s', points)
				.replace('%2$s', currency)
				.replace('%s', points) // Fallback for non-numbered placeholders
				.replace('%s', currency);
		},

		updateRewardLabels(reward) {
			reward.reward_label = this.generateRewardLabel(reward);
			reward.points_cost_label = this.generatePointsCostLabel(reward);
			return reward;
		},

		add(reward) {
			// Generate labels before adding
			const rewardWithLabels = this.updateRewardLabels({ ...reward, configured: true });

			// If there's a pending reorder, apply it first before adding the new item
			if (window._pendingSpendOrder && window._pendingSpendOrder.length > 0) {
				const reordered = [];
				for (const id of window._pendingSpendOrder) {
					const item = this.rewards.find((r) => r.id === id);
					if (item) {
						reordered.push(item);
					}
				}
				// Only apply if we found all items
				if (reordered.length === this.rewards.length) {
					this.rewards = Alpine.reactive(reordered);
				}
				// Clear pending order and increment sortIteration to sync DOM
				window._pendingSpendOrder = null;
				this.sortIteration++;
			}

			// Add reward to array using Alpine.reactive
			this.rewards = Alpine.reactive([...this.rewards, rewardWithLabels]);
		},

		update(rewardId, updatedReward) {
			// Find and update reward by ID
			const index = this.rewards.findIndex((r) => r.id === rewardId);
			if (index !== -1) {
				// Regenerate labels with new values
				const rewardWithLabels = this.updateRewardLabels({
					...updatedReward,
					configured: true,
				});

				const newRewards = [...this.rewards];
				newRewards[index] = rewardWithLabels;
				this.rewards = Alpine.reactive(newRewards);
			}
		},

		remove(rewardId) {
			// If there's a pending reorder, apply it first before removing
			if (window._pendingSpendOrder && window._pendingSpendOrder.length > 0) {
				const reordered = [];
				for (const id of window._pendingSpendOrder) {
					const item = this.rewards.find((r) => r.id === id);
					if (item) {
						reordered.push(item);
					}
				}
				// Only apply if we found all items
				if (reordered.length === this.rewards.length) {
					this.rewards = Alpine.reactive(reordered);
				}
				// Clear pending order and increment sortIteration to sync DOM
				window._pendingSpendOrder = null;
				this.sortIteration++;
			}

			// Remove reward from array
			this.rewards = Alpine.reactive(this.rewards.filter((r) => r.id !== rewardId));

			// Notify main component of state change for dirty tracking
			window.dispatchEvent(new CustomEvent('rewardswp:settings-changed'));
		},

		toggle(rewardId) {
			// Toggle reward enabled status
			const index = this.rewards.findIndex((r) => r.id === rewardId);
			if (index !== -1) {
				const newRewards = [...this.rewards];
				newRewards[index] = { ...newRewards[index], enabled: !newRewards[index].enabled };
				this.rewards = Alpine.reactive(newRewards);

				// Notify main component of state change for dirty tracking
				window.dispatchEvent(new CustomEvent('rewardswp:settings-changed'));
			}
		},

		getArray() {
			return this.rewards;
		},
	});

	/**
	 * Referrals Rewards Global Store
	 * Accessible from anywhere via $store.referrals
	 * Manages TWO SLOTS: advocate and friend (not an array, just two nullable properties)
	 */
	Alpine.store('referrals', {
		rewards: {
			advocate: null, // Single advocate reward or null
		friend: null, // Single friend reward or null
	},
	currencySymbol: '$',
	currencySingular: 'Point',
	currencyPlural: 'Points',
	rewardTitles: {},
		rewardDescriptions: {},
		rewardDefaults: {},
		iconMap: {},
		pointsProgramEnabled: false,
		referralsProgramEnabled: false,
		advocateConfigSubtitle: '',
		friendConfigSubtitle: '',
		friendRequiresPro: false,

		setup(data) {
			// Process rewards to use products_cache for products_included
			// This ensures the ChoicesComponent has objects with {id, name} instead of just IDs
			const processReward = (reward) => {
				if (!reward) return null;
				const processed = { ...reward };

				// If products_cache exists, use it for products_included
				if (reward.products_cache && Array.isArray(reward.products_cache) && reward.products_cache.length > 0) {
					processed.products_included = reward.products_cache;
				}

				// Normalize minimum_order_amount: convert 0 to empty string so placeholder shows instead of "0"
				// Empty string works better with input type="number" and Alpine.js x-model for showing placeholders
				if (processed.minimum_order_amount === 0 || processed.minimum_order_amount === '0' || processed.minimum_order_amount === null) {
					processed.minimum_order_amount = '';
				}

				return processed;
			};

			this.rewards = Alpine.reactive({
				advocate: processReward(data.rewards?.advocate),
				friend: processReward(data.rewards?.friend),
			});

		this.currencySymbol = data.currencySymbol || '$';
		this.currencySingular = data.currencySingular || 'Point';
		this.currencyPlural = data.currencyPlural || 'Points';
		this.rewardTitles = data.rewardTitles || {};
			this.rewardDescriptions = data.rewardDescriptions || {};
			this.rewardDefaults = data.rewardDefaults || {};
			this.iconMap = data.iconMap || {};
			this.pointsProgramEnabled = data.pointsProgramEnabled || false;
			this.referralsProgramEnabled = data.referralsProgramEnabled || false;
			this.advocateConfigSubtitle =
				data.advocateConfigSubtitle || 'Configure advocate reward';
			this.friendConfigSubtitle = data.friendConfigSubtitle || 'Configure friend reward';
			this.friendRequiresPro = data.friendRequiresPro || false;

			// Listen for program toggle events to update store state.
			window.addEventListener('points-program-toggled', (e) => {
				this.pointsProgramEnabled = e.detail.enabled;
			});
			window.addEventListener('referrals-program-toggled', (e) => {
				this.referralsProgramEnabled = e.detail.enabled;
			});
		},

		// Check if reward type is already added as advocate or friend
		isAdded(rewardType) {
			const advocateMatch = this.rewards.advocate?.type === rewardType;
			const friendMatch = this.rewards.friend?.type === rewardType;
			return {
				advocate: advocateMatch,
				friend: friendMatch,
				any: advocateMatch || friendMatch,
			};
		},

		// Add or update reward for context (advocate or friend)
		setReward(context, reward) {
			if (context !== 'advocate' && context !== 'friend') {
				console.error('[REFERRALS STORE] ERROR: Invalid context:', context);
				return;
			}

			// Add formatted_amount for widget preview to use (matches PHP formatting).
			if (reward && reward.type) {
				reward.formatted_amount = this.getFormattedAmount(reward);
			}

			// Mutate in place to preserve reactivity - don't replace the object!
			this.rewards[context] = reward;

			// Dispatch event for widget preview to update
			window.dispatchEvent(new CustomEvent('referral-reward-updated', {
				detail: { context, reward }
			}));
		},

		// Get formatted amount for widget preview display (matches PHP RewardType::get_reward_formatted_amount).
		getFormattedAmount(reward) {
			const type = reward.type;
			const amount = parseFloat(reward.reward_amount || 0);

			switch (type) {
				case 'fixed_discount':
					return `${applyCurrencySymbol(this.currencySymbol, amount)} off coupon`;
				case 'percentage_discount':
					return `${amount}% off coupon`;
				case 'free_shipping':
					return 'Free shipping';
				case 'free_product':
					return 'Free product';
				case 'points':
					// Get currency labels from earnActions store.
					const earnStore = Alpine.store('earnActions');
					const label = amount === 1 ? earnStore.currencySingular : earnStore.currencyPlural;
					return `${amount.toLocaleString()} ${label}`;
				default:
					return this.rewardDescriptions[type] || 'Reward';
			}
		},

		// Remove reward for context
		removeReward(context) {
			if (context !== 'advocate' && context !== 'friend') {
				console.error('[REFERRALS STORE] Invalid context:', context);
				return;
			}

			// Mutate in place to preserve reactivity - don't replace the object!
			this.rewards[context] = null;

			// Dispatch event for widget preview to update
			window.dispatchEvent(new CustomEvent('referral-reward-updated', {
				detail: { context, reward: null }
			}));
		},

		// Get icon for reward type
		getIcon(rewardType) {
			return this.iconMap[rewardType] || '';
		},

		// Get title for reward
		getTitle(reward, context) {
			// If custom name is set, use it
			if (reward.name && reward.name.trim()) {
				return reward.name;
			}
			// For all types (including free_product), return the type title
			// The specific product name/details will be shown in getDescription()
			return this.rewardTitles[reward.type] || '';
		},

		// Get description for reward (formatted with amounts)
		// context: 'advocate' or 'friend'
		getDescription(reward, context) {
			const type = reward.type;
			const amount = parseFloat(reward.reward_amount || 0);
			const suffix = context === 'friend' ? ' first order' : '';

			switch (type) {
				case 'points': {
					const pointsWord = amount === 1 ? this.currencySingular : this.currencyPlural;
					return amount > 0 ? `${amount} ${pointsWord} per referral` : (this.rewardDescriptions[type] || '');
				}
				case 'fixed_discount':
					return `${applyCurrencySymbol(this.currencySymbol, amount)} off${suffix}`;
				case 'percentage_discount':
					return `${amount}% off${suffix}`;
				case 'free_shipping':
					return 'Free shipping on next order';
				case 'free_product':
					// Consider product_value (Points) or reward_amount (Referrals) for transparency.
					// Referrals drawer uses reward_amount for "Product value ($)" field.
					const descProductValue = parseFloat(reward.product_value || reward.reward_amount || 0);
					const descHasValueLimit = descProductValue > 0;
					let descProductName = '';

					// Priority: reward_description > product name
					if (reward.reward_description && reward.reward_description.trim()) {
						descProductName = reward.reward_description;
					} else if (reward.products_included && reward.products_included.length > 0 && reward.products_included[0].name) {
						descProductName = reward.products_included[0].name;
					}

					if (descProductName) {
						if (descHasValueLimit) {
							return `${formatCurrency(descProductValue, this.currencySymbol)} off ${descProductName}`;
						}
						return `Free ${descProductName}`;
					}

					// Fallback.
					if (descHasValueLimit) {
						return `${formatCurrency(descProductValue, this.currencySymbol)} off product`;
					}
					return 'Free product';
				default:
					return this.rewardDescriptions[type] || '';
			}
		},
	});

	/**
	 * Export Drawer Global Store
	 * Accessible from anywhere via $store.exportDrawer
	 * Handles export configuration and history management
	 */
	Alpine.store('exportDrawer', {
		isOpen: false,
		isLoading: false,
		selectedType: null,
		emailCopy: true,
		dateRangeStart: null,
		dateRangeEnd: null,
		dateRangeKey: null,
		history: [],
		isLoadingHistory: false,
		hasLoadedOnce: false, // Track if initial load has completed
		pollingInterval: null,
		_initialized: false, // Track if setup has been called

		// Properties required by DrawerComponentView
		title: '',
		subtitle: '',
		type: 'export',
		view: 'config',
		isNew: true,
		isUpgrade: false,
		errors: {},
		addTitle: '',
		actionId: null,

		// Script data (set via setup() from PHP)
		scriptData: {
			ajaxUrl: '',
			nonce: '',
			canExport: true,
			dateRange: 'last_30_days',
			labels: {
				subtitle: 'Export to CSV',
				addTitle: 'Start Export',
				exportStarted: 'Export started! Check the Export History for progress.',
				failedToStart: 'Failed to start export',
				errorOccurred: 'An error occurred',
				exportRestarted: 'Export restarted',
				failedToRetry: 'Failed to retry export',
				rangeLabels: {
					last_7_days: 'Last 7 days',
					last_14_days: 'Last 14 days',
					last_30_days: 'Last 30 days',
					last_60_days: 'Last 60 days',
					last_90_days: 'Last 90 days',
					last_12_months: 'Last 12 months',
					all_time: 'All time',
				},
				typeLabels: {
					members: 'Members',
					points: 'Points Activity',
					referrals: 'Referrals',
					rewards: 'Rewards',
				},
				statusLabels: {
					completed: 'Ready',
					processing: 'Processing',
					pending: 'Pending',
					failed: 'Failed',
				},
			},
		},

		setup(data) {
			this.scriptData = { ...this.scriptData, ...data };
			this.isUpgrade = !data.canExport;
			this.subtitle = data.labels?.subtitle || 'Export to CSV';
			this.addTitle = data.labels?.addTitle || 'Start Export';
			this._initialized = true;
		},

		init() {
			// Auto-initialize from localized config if available (set via wp_localize_script).
			if (!this._initialized && typeof window.rewardswpExportDrawerConfig !== 'undefined') {
				const config = window.rewardswpExportDrawerConfig;
				this.setup({
					ajaxUrl: config.ajaxUrl || '',
					nonce: config.nonce || '',
					canExport: !!config.canExport,
					labels: config.labels || {},
				});
			}

			// Only load history if setup() has been called with valid ajaxUrl.
			if (this._initialized && this.scriptData.ajaxUrl) {
				this.loadHistory();
			}
		},

		open(type) {
			this.selectedType = type;
			this.title = this.getTypeLabel();
			this.isOpen = true;
			// Get date range from the current selected range
			const dateRange = this.getDateRangeFromSelection();
			this.dateRangeStart = dateRange.start;
			this.dateRangeEnd = dateRange.end;
			this.dateRangeKey = dateRange.key;
		},

		close() {
			this.isOpen = false;
			this.selectedType = null;
			this.isLoading = false;
			this.title = '';
		},

		// Stub for hasChanges required by DrawerComponentView
		hasChanges() {
			return false;
		},

		// Stub for save required by DrawerComponentView
		save() {
			this.startExport();
		},

		getDateRangeFromSelection() {
			// Get date range based on the current reports page selection
			const urlParams = new URLSearchParams(window.location.search);
			const range = urlParams.get('range') || 'last_30_days';
			const now = new Date();
			let start = new Date();

			switch (range) {
				case 'last_7_days':
					start.setDate(now.getDate() - 7);
					break;
				case 'last_14_days':
					start.setDate(now.getDate() - 14);
					break;
				case 'last_30_days':
					start.setDate(now.getDate() - 30);
					break;
				case 'last_60_days':
					start.setDate(now.getDate() - 60);
					break;
				case 'last_90_days':
					start.setDate(now.getDate() - 90);
					break;
				case 'last_12_months':
					start.setMonth(now.getMonth() - 12);
					break;
				case 'all_time':
					start = new Date('2020-01-01');
					break;
				default:
					start.setDate(now.getDate() - 30);
			}

			return {
				key: range,
				start: start.toISOString().split('T')[0],
				end: now.toISOString().split('T')[0],
			};
		},

		getRangeLabel() {
			const labels = this.scriptData.labels?.rangeLabels || {};
			return labels[this.dateRangeKey] || '';
		},

		getTypeLabel() {
			const labels = this.scriptData.labels?.typeLabels || {};
			return labels[this.selectedType] || '';
		},

		formatDateRange() {
			if (!this.dateRangeStart || !this.dateRangeEnd) return '';
			const rangeLabel = this.getRangeLabel();
			const options = { month: 'short', day: 'numeric', year: 'numeric' };
			const start = new Date(this.dateRangeStart + 'T00:00:00').toLocaleDateString(
				'en-US',
				options
			);
			const end = new Date(this.dateRangeEnd + 'T00:00:00').toLocaleDateString('en-US', options);
			return rangeLabel ? `${rangeLabel} · ${start} - ${end}` : `${start} - ${end}`;
		},

		async startExport() {
			if (!this.selectedType || this.isLoading) return;

			this.isLoading = true;

			const formData = new FormData();
			formData.append('action', 'rewardswp_enqueue_export');
			formData.append('_ajax_nonce', this.scriptData.nonce);
			formData.append('type', this.selectedType);
			formData.append('range_start', this.dateRangeStart);
			formData.append('range_end', this.dateRangeEnd);
			formData.append('email_copy', this.emailCopy ? '1' : '0');

			try {
				const response = await fetch(this.scriptData.ajaxUrl, {
					method: 'POST',
					body: formData,
				});

				const result = await response.json();

				if (result.success) {
					// Show success toast
					window.dispatchEvent(
						new CustomEvent('rewardswp-toast', {
							detail: {
								message:
									this.scriptData.labels?.exportStarted ||
									'Export started! Check the Export History for progress.',
								type: 'success',
							},
						})
					);

					// Close drawer and refresh history
					this.close();
					this.loadHistory();
					this.startPolling();
				} else {
					window.dispatchEvent(
						new CustomEvent('rewardswp-toast', {
							detail: {
								message:
									result.data?.message ||
									this.scriptData.labels?.failedToStart ||
									'Failed to start export',
								type: 'error',
							},
						})
					);
				}
			} catch (error) {
				window.dispatchEvent(
					new CustomEvent('rewardswp-toast', {
						detail: {
							message: this.scriptData.labels?.errorOccurred || 'An error occurred',
							type: 'error',
						},
					})
				);
			}

			this.isLoading = false;
		},

		async loadHistory() {
			this.isLoadingHistory = true;

			const formData = new FormData();
			formData.append('action', 'rewardswp_list_exports');
			formData.append('_ajax_nonce', this.scriptData.nonce);

			try {
				const response = await fetch(this.scriptData.ajaxUrl, {
					method: 'POST',
					body: formData,
				});

				const result = await response.json();

				if (result.success) {
					this.history = result.data.jobs || [];

					// Check if any exports are still processing
					const hasProcessing = this.history.some(
						(e) => e.status === 'pending' || e.status === 'processing'
					);
					if (hasProcessing) {
						this.startPolling();
					} else {
						this.stopPolling();
					}
				}
			} catch (error) {
				console.error('Failed to load export history:', error);
			}

			this.isLoadingHistory = false;
			this.hasLoadedOnce = true;
		},

		startPolling() {
			if (this.pollingInterval) return;

			this.pollingInterval = setInterval(() => {
				this.loadHistory();
			}, 5000); // Poll every 5 seconds
		},

		stopPolling() {
			if (this.pollingInterval) {
				clearInterval(this.pollingInterval);
				this.pollingInterval = null;
			}
		},

		async downloadExport(jobId) {
			const url =
				this.scriptData.ajaxUrl +
				'?' +
				new URLSearchParams({
					action: 'rewardswp_download_export',
					_ajax_nonce: this.scriptData.nonce,
					job_id: jobId,
				});
			window.location.href = url;
		},

		async retryExport(jobId) {
			const formData = new FormData();
			formData.append('action', 'rewardswp_retry_export');
			formData.append('_ajax_nonce', this.scriptData.nonce);
			formData.append('job_id', jobId);

			try {
				const response = await fetch(this.scriptData.ajaxUrl, {
					method: 'POST',
					body: formData,
				});

				const result = await response.json();

				if (result.success) {
					window.dispatchEvent(
						new CustomEvent('rewardswp-toast', {
							detail: {
								message: this.scriptData.labels?.exportRestarted || 'Export restarted',
								type: 'success',
							},
						})
					);
					this.loadHistory();
					this.startPolling();
				} else {
					window.dispatchEvent(
						new CustomEvent('rewardswp-toast', {
							detail: {
								message:
									result.data?.message ||
									this.scriptData.labels?.failedToRetry ||
									'Failed to retry export',
								type: 'error',
							},
						})
					);
				}
			} catch (error) {
				window.dispatchEvent(
					new CustomEvent('rewardswp-toast', {
						detail: {
							message: this.scriptData.labels?.errorOccurred || 'An error occurred',
							type: 'error',
						},
					})
				);
			}
		},

		formatFileSize(bytes) {
			if (!bytes) return '-';
			const units = ['B', 'KB', 'MB', 'GB'];
			let i = 0;
			while (bytes >= 1024 && i < units.length - 1) {
				bytes /= 1024;
				i++;
			}
			return bytes.toFixed(1) + ' ' + units[i];
		},

		formatDate(dateString) {
			if (!dateString) return '-';
			const date = new Date(dateString);
			return date.toLocaleDateString('en-US', {
				month: 'short',
				day: 'numeric',
				hour: '2-digit',
				minute: '2-digit',
			});
		},

		getStatusClass(status) {
			// Match Members screen badge styling with ring-1 ring-inset
			switch (status) {
				case 'completed':
					return 'bg-green-50 text-green-700 ring-1 ring-inset ring-green-600/20';
				case 'processing':
					return 'bg-blue-50 text-blue-700 ring-1 ring-inset ring-blue-600/20';
				case 'pending':
					return 'bg-yellow-50 text-yellow-700 ring-1 ring-inset ring-yellow-600/20';
				case 'failed':
					return 'bg-red-50 text-red-700 ring-1 ring-inset ring-red-600/20';
				default:
					return 'bg-gray-50 text-gray-600 ring-1 ring-inset ring-gray-500/20';
			}
		},

		getStatusLabel(status) {
			const labels = this.scriptData.labels?.statusLabels || {};
			return labels[status] || status;
		},
	});

	/**
	 * Earn Points Drawer Section Component
	 * Delegates data management to $store.earnActions
	 */
	Alpine.data('earnDrawerSection', () => ({
		init() {
			// Listen for select action events from the drawer
			window.addEventListener('select-earn-action', (event) => {
				this.selectEarnAction(event.detail.actionType, event.detail.actionDefaults);
			});

			// Listen for add action events from the drawer save button
			window.addEventListener('add-earn-action', () => {
				this.addEarnAction();
			});

			// Listen for update earn action events from the drawer save button.
			window.addEventListener('update-earn-action', () => {
				this.updateEarnAction();
			});

			// Listen for select spend/redeem action events from the drawer
			window.addEventListener('select-spend-action', (event) => {
				const { actionType, actionDefaults } = event.detail;
				const drawer = Alpine.store('drawer');

				// Check feature access using feature_slug from action definition and accessibleFeatures from config.
				const featureSlug = actionDefaults.feature_slug;
				const accessibleFeatures = Alpine.store('config').accessibleFeatures || [];
				const hasAccess = !featureSlug || accessibleFeatures.includes(featureSlug);
				const isUpgrade = !hasAccess;

				// Create new action with unique ID (timestamp-based for multiple instances)
				const newAction = {
					id: `${actionType}_${Date.now()}`,
					type: actionType,
					enabled: true,
					configured: true,
					...actionDefaults,
					requires_pro: !hasAccess,
				};

				// Update drawer state - data FIRST, view LAST
				drawer.type = 'spend';
				drawer.selectedAction = newAction;
				drawer.originalSnapshot = null;
				drawer.actionId = newAction.id;
				drawer.isNew = true;
				drawer.isUpgrade = isUpgrade;
				drawer.title = actionDefaults.title;
				drawer.subtitle = 'Configure reward settings';
				drawer.errors = {};
				drawer.isOpen = true;
				drawer.view = 'config'; // LAST
			});

			// Listen for add spend/redeem action events from the drawer save button
			window.addEventListener('add-spend-action', () => {
				this.addSpendAction();
			});

			// Listen for update spend/redeem action events from the drawer save button
			window.addEventListener('update-spend-action', () => {
				this.updateSpendAction();
			});
		},

		// Handler for Alpine Sort plugin - called when an earn action is reordered
		handleEarnSort(itemId, newPosition) {
			// Track the order changes - don't update store directly to avoid conflicts with SortableJS.
			const store = Alpine.store('earnActions');

			// Get current order (either pending or from store's sorted getter).
			let currentOrder = window._pendingEarnOrder
				? [...window._pendingEarnOrder]
				: store.enabledSorted.map((a) => a.id);

			// Find current index of the item in the pending order.
			const currentIndex = currentOrder.indexOf(itemId);

			if (currentIndex === -1 || currentIndex === newPosition) {
				return;
			}

			// Remove from current position and insert at new position.
			currentOrder.splice(currentIndex, 1);
			currentOrder.splice(newPosition, 0, itemId);

			// Store the pending order.
			window._pendingEarnOrder = currentOrder;

			// Dispatch event to mark earn order as changed.
			window.dispatchEvent(new CustomEvent('rewardswp:earn-order-changed'));
		},

		openEarnDrawer() {
			const earnData = window.rewardswpDrawerData?.earn;
			const drawer = Alpine.store('drawer');

			const config = {
				type: 'earn',
				view: 'list',
				title: earnData?.addTitle || 'Add ways to earn',
				subtitle: earnData?.addSubtitle || 'Choose an action for members to earn points',
				addTitle: earnData?.createButtonText || 'Create',
				actionId: null,
				selectedAction: {},
				originalSnapshot: null,
				isNew: true,
			};

			drawer.open(config);
		},

		selectEarnAction(actionType, actionDefaults) {
			const earnActionsStore = Alpine.store('earnActions');
			const drawer = Alpine.store('drawer');

			// Check if action already exists (earn actions can only be added once)
			const isAlreadyAdded = earnActionsStore.isAdded(actionType);
			if (isAlreadyAdded) {
				return; // Silently prevent - visual indicators in drawer already clear
			}

			// Create new action
			const newAction = {
				id: actionType,
				type: actionType,
				enabled: true,
				configured: true,
				...actionDefaults,
			};

			const earnData = window.rewardswpDrawerData?.earn;

			// Check feature access using feature_slug + accessibleFeatures pattern.
			const featureSlug = actionDefaults.feature_slug;
			const accessibleFeatures = Alpine.store('config').accessibleFeatures || [];
			const hasAccess = !featureSlug || accessibleFeatures.includes(featureSlug);
			const isUpgrade = !hasAccess;

			// Update drawer state - set data properties FIRST, then view LAST
			// This ensures Alpine.js x-if templates have data ready when view changes
			drawer.type = 'earn';

			drawer.selectedAction = newAction;

			drawer.originalSnapshot = null;
			drawer.actionId = actionType;
			drawer.isNew = true;
			drawer.isUpgrade = isUpgrade;

			drawer.title = actionDefaults.title;
			drawer.subtitle = earnData?.configSubtitle || 'Configure action settings';
			drawer.errors = {};
			drawer.isOpen = true;
			drawer.view = 'config';
		},

		editEarnAction(action) {
			const earnData = window.rewardswpDrawerData?.earn;
			const drawer = Alpine.store('drawer');

			// Check feature access for upgrade gating.
			const featureSlug = action.feature_slug;
			const accessibleFeatures = Alpine.store('config').accessibleFeatures || [];
			const hasAccess = !featureSlug || accessibleFeatures.includes(featureSlug);

			const config = {
				type: 'earn',
				view: 'config',
				selectedAction: { ...action },
				originalSnapshot: { ...action },
				actionId: action.id,
				isNew: false,
				isUpgrade: !hasAccess,
				title: action.title,
				subtitle: earnData?.editSubtitle || 'Edit action settings',
			};

			drawer.open(config);
		},

		addEarnAction() {
			const drawer = Alpine.store('drawer');
			if (!hasSelectedAction(drawer)) return;

			drawer.isLoading = true;

			// Add to global store
			Alpine.store('earnActions').add(drawer.selectedAction.id, drawer.selectedAction);

			// Close drawer
			setTimeout(() => {
				drawer.close();
			}, 500);
		},

		updateEarnAction() {
			const drawer = Alpine.store('drawer');
			if (!hasSelectedAction(drawer) || !drawer.actionId) return;

			drawer.isLoading = true;

			// Update in global store (preserves position).
			Alpine.store('earnActions').update(drawer.actionId, drawer.selectedAction);

			// Close drawer.
			setTimeout(() => {
				drawer.close();
			}, 500);
		},

		addSpendAction() {
			const drawer = Alpine.store('drawer');
			if (!hasSelectedAction(drawer)) return;

			// Validate free_product has a product selected
			if (drawer.selectedAction.type === 'free_product') {
				const productsIncluded = drawer.selectedAction.products_included || [];
				if (productsIncluded.length === 0) {
					drawer._showProductError = true;
					drawer.isLoading = false;
					return;
				}
			}

			drawer.isLoading = true;

			// Add to global store
			Alpine.store('spendActions').add(drawer.selectedAction);

			// Close drawer
			setTimeout(() => {
				drawer.close();
			}, 500);
		},

		updateSpendAction() {
			const drawer = Alpine.store('drawer');
			if (!hasSelectedAction(drawer) || !drawer.actionId) return;

			// Validate free_product has a product selected
			if (drawer.selectedAction.type === 'free_product') {
				const productsIncluded = drawer.selectedAction.products_included || [];
				if (productsIncluded.length === 0) {
					drawer._showProductError = true;
					drawer.isLoading = false;
					return;
				}
			}

			drawer.isLoading = true;

			// Update in global store
			Alpine.store('spendActions').update(drawer.actionId, drawer.selectedAction);

			// Close drawer
			setTimeout(() => {
				drawer.close();
			}, 500);
		},

		toggleActionStatus(actionId) {
			Alpine.store('earnActions').toggle(actionId);
		},

		removeEarnAction(actionId) {
			Alpine.store('earnActions').remove(actionId);
		},

		getActionIcon(iconName) {
			return Alpine.store('earnActions').getActionIcon(iconName);
		},

		getActionDetails(action) {
			return Alpine.store('earnActions').getActionDetails(action);
		},

		/**
		 * Get the priority value for a plan tier.
		 * Higher numbers mean higher tier plans.
		 *
		 * @param {string} plan - The plan tier ('lite', 'basic', 'plus', 'pro')
		 * @returns {number} The priority value
		 */
		getPlanPriority(plan) {
			const priorities = {
				lite: 1,
				basic: 2,
				plus: 3,
				pro: 4,
			};
			return priorities[plan] || 0;
		},
	}));

	/**
	 * Spend Points Drawer Section Component
	 * Delegates data management to $store.spendActions
	 */
	Alpine.data('spendDrawerSection', () => ({
		init() {
			// Listen for select reward events from the drawer
			window.addEventListener('select-spend-action', (event) => {
				this.selectSpendAction(event.detail.actionType, event.detail.actionDefaults);
			});

			// Listen for add reward events from the drawer save button
			window.addEventListener('add-spend-action', () => {
				this.addSpendAction();
			});

			// Listen for update reward events from the drawer save button
			window.addEventListener('update-spend-action', () => {
				this.updateSpendAction();
			});
		},

		// Handler for Alpine Sort plugin - called when an item is reordered
		handleSort(itemId, newPosition) {
			// Don't update the store here - let SortableJS handle DOM completely
			// Instead, track the order changes using the $item and $position from Alpine Sort
			const store = Alpine.store('spendActions');

			// Get current order (either pending or from store)
			let currentOrder = window._pendingSpendOrder
				? [...window._pendingSpendOrder]
				: store.rewards.map((r) => r.id);

			// Find current index of the item in the pending order
			const currentIndex = currentOrder.indexOf(itemId);

			if (currentIndex === -1 || currentIndex === newPosition) {
				return;
			}

			// Remove from current position and insert at new position
			currentOrder.splice(currentIndex, 1);
			currentOrder.splice(newPosition, 0, itemId);

			// Store the pending order
			window._pendingSpendOrder = currentOrder;

			// Dispatch event to mark spend order as changed
			window.dispatchEvent(new CustomEvent('rewardswp:spend-order-changed'));
		},

		selectSpendAction(actionType, actionDefaults) {
			const drawer = Alpine.store('drawer');

			// Check feature access using feature_slug from action definition and accessibleFeatures from config.
			const featureSlug = actionDefaults.feature_slug;
			const accessibleFeatures = Alpine.store('config').accessibleFeatures || [];
			const hasAccess = !featureSlug || accessibleFeatures.includes(featureSlug);
			const isUpgrade = !hasAccess;

			// Create new reward with unique ID (timestamp + random for true uniqueness)
			// This ensures even rapid double-clicks won't create duplicate IDs
			const uniqueId = `${actionType}_${Date.now()}_${Math.random()
				.toString(36)
				.slice(2, 11)}`;
			const newReward = {
				id: uniqueId,
				type: actionType,
				enabled: true,
				configured: true,
				...actionDefaults,
				requires_pro: !hasAccess,
			};

			// Update drawer state - data FIRST, view LAST
			drawer.type = 'spend';
			drawer.selectedAction = newReward;

			drawer.originalSnapshot = null;
			drawer.actionId = newReward.id;
			drawer.isNew = true;
			drawer.isUpgrade = isUpgrade;

			drawer.title = actionDefaults.title;
			drawer.subtitle = actionDefaults.description || '';
			drawer.errors = {};
			drawer.isOpen = true;
			drawer.view = 'config'; // LAST
		},

		openSpendDrawer() {
			const drawer = Alpine.store('drawer');
			const spendData = Alpine.store('spendActions');

			// Check limit for lite users
			if (spendData.rewards.length >= window.liteRedemptionLimit && !Alpine.store('config').hasProFeatures) {
				window.dispatchEvent(
					new CustomEvent('show-upgrade-modal', {
						detail: 'redeem_options_limit',
					})
				);
				return;
			}

			// Use drawer.open() to properly handle timeout cancellation
			const config = {
				type: 'spend',
				view: 'list',
				title: spendData?.addTitle || 'Add ways to redeem',
				subtitle: spendData?.addSubtitle || 'Choose a reward type for members to redeem',
				addTitle: spendData?.createButtonText || 'Create',
				actionId: null,
				selectedAction: {},
				originalSnapshot: null,
				isNew: true,
			};

			drawer.open(config);
		},

		editSpendAction(reward) {
			const drawer = Alpine.store('drawer');

			// Use drawer.open() to properly handle timeout cancellation
			const config = {
				type: 'spend',
				view: 'config',
				selectedAction: JSON.parse(JSON.stringify(reward)),
				originalSnapshot: JSON.parse(JSON.stringify(reward)),
				actionId: reward.id,
				isNew: false,
				title: reward.reward_label || reward.custom_title || 'Edit Reward',
				subtitle: '',
			};

			drawer.open(config);
		},

		addSpendAction() {
			const drawer = Alpine.store('drawer');
			if (!hasSelectedAction(drawer)) {
				return;
			}

			// Prevent double-add if already processing
			if (drawer.isLoading) {
				return;
			}

			// Validate free_product has a product selected
			if (drawer.selectedAction.type === 'free_product') {
				const productsIncluded = drawer.selectedAction.products_included || [];
				if (productsIncluded.length === 0) {
					drawer._showProductError = true;
					drawer.isLoading = false;
					return;
				}
			}

			drawer.isLoading = true;

			// Add to global store
			Alpine.store('spendActions').add(drawer.selectedAction);

			// Close drawer
			setTimeout(() => {
				drawer.close();
			}, 500);
		},

		updateSpendAction() {
			const drawer = Alpine.store('drawer');
			if (!hasSelectedAction(drawer) || !drawer.actionId) return;

			// Validate free_product has a product selected
			if (drawer.selectedAction.type === 'free_product') {
				const productsIncluded = drawer.selectedAction.products_included || [];
				if (productsIncluded.length === 0) {
					drawer._showProductError = true;
					drawer.isLoading = false;
					return;
				}
			}

			drawer.isLoading = true;

			// Update in global store
			Alpine.store('spendActions').update(drawer.actionId, drawer.selectedAction);

			// Close drawer
			setTimeout(() => {
				drawer.close();
			}, 500);
		},

		removeSpendAction(rewardId) {
			Alpine.store('spendActions').remove(rewardId);
		},

		toggleSpendStatus(rewardId) {
			Alpine.store('spendActions').toggle(rewardId);
		},

		getSpendIcon(type) {
			return Alpine.store('spendActions').iconMap?.[type] || '';
		},

		getSpendTitle(reward) {
			// Return custom title if set, otherwise default title from definitions
			if (reward.custom_title && reward.custom_title.trim() !== '') {
				return reward.custom_title;
			}

			// Fall back to action type definitions from drawer data
			const drawerData = window.rewardswpDrawerData?.spend;
			if (drawerData && drawerData.actionTitles && drawerData.actionTitles[reward.type]) {
				return drawerData.actionTitles[reward.type];
			}

			// Final fallback to reward.title or empty string
			return reward.title || '';
		},

		// Note: getSpendDescription() and getSpendDetails() methods removed.
		// Labels are now generated in PHP and passed via reward.reward_label and reward.points_cost_label.
	}));

	/**
	 * Referrals Section Component
	 * Manages referral rewards using $store.referrals
	 */
	Alpine.data('referralsSection', () => ({
		init() {
			// Listen for remove-referral-reward event from drawer
			window.addEventListener('remove-referral-reward', (e) => {
				this.removeReward(e.detail.context);
			});

			// Listen for open-advocate-reward-drawer event (from Manage in Referrals button)
			window.addEventListener('open-advocate-reward-drawer', () => {
				const advocateReward = Alpine.store('referrals').rewards.advocate;
				if (advocateReward) {
					this.editReward('advocate', advocateReward);
				}
			});

			// Listen for points program toggle events
			window.addEventListener('points-program-toggled', (e) => {
				Alpine.store('referrals').pointsProgramEnabled = e.detail.enabled;
			});

			// Listen for referrals program toggle events
			window.addEventListener('referrals-program-toggled', (e) => {
				Alpine.store('referrals').referralsProgramEnabled = e.detail.enabled;
			});

			// Listen for select-referral-reward event from drawer list view
			window.addEventListener('select-referral-reward', (e) => {
				const { rewardType, rewardDefaults, featureSlug } = e.detail;

				const drawer = Alpine.store('drawer');
				const store = Alpine.store('referrals');
				const currentContext = drawer.actionId; // 'advocate' or 'friend'

				// Get the reward title and context-specific subtitle.
				const rewardTitle = store.rewardTitles[rewardType] || rewardType;
				const subtitle =
					currentContext === 'advocate'
						? store.advocateConfigSubtitle
						: store.friendConfigSubtitle;

				// Check if upgrade is required using feature_slug + accessibleFeatures pattern.
				// For both advocate and friend, we need to check:
				// 1. The base context feature (friend_reward for friends)
				// 2. The specific reward type feature (e.g., advocate_reward_free_shipping)
			const isFriendReward = currentContext === 'friend';
				const accessibleFeatures = Alpine.store('config').accessibleFeatures || [];

				// Map 'redeem_X' to 'advocate_reward_X' for reward type check
				// e.g., 'redeem_free_product' -> 'advocate_reward_free_product'
				// e.g., 'redeem_fixed_discount' -> 'advocate_reward_fixed'
				let rewardTypeFeatureSlug = '';
				if (featureSlug) {
					rewardTypeFeatureSlug = featureSlug
						.replace('redeem_', 'advocate_reward_')
						.replace('_discount', '');
				}

				let requiresUpgrade = false;
				if (isFriendReward) {
					// Friend rewards: check both friend_reward AND the specific reward type
					const hasFriendAccess = accessibleFeatures.includes('friend_reward');
					const hasRewardTypeAccess = !rewardTypeFeatureSlug || accessibleFeatures.includes(rewardTypeFeatureSlug);
					requiresUpgrade = !hasFriendAccess || !hasRewardTypeAccess;
				} else if (rewardTypeFeatureSlug) {
					// Advocate rewards: check the reward type feature
					requiresUpgrade = !accessibleFeatures.includes(rewardTypeFeatureSlug);
				}

				if (requiresUpgrade) {
					// Create preview reward and open with upgrade overlay
					const previewReward = {
						type: rewardType,
						name: rewardDefaults.title || '',
						reward_amount: rewardDefaults.reward_amount || 0,
						minimum_order_amount: '',
						reward_expiration: '+3 months',
						products_included: [],
					};

					drawer.selectedAction = previewReward;
					drawer.isUpgrade = true;
					drawer.title = rewardTitle;
					drawer.subtitle = subtitle;
					drawer.view = 'config';
					const upgradeReason = isFriendReward
						? 'Friend rewards (Pro feature)'
						: 'Pro reward type';
				} else {
					// Create new reward with defaults
					const newReward = {
						type: rewardType,
						name: '',
						reward_amount: rewardDefaults.reward_amount || 0,
						minimum_order_amount: '',
						reward_expiration: '+3 months',
						products_included: [],
					};

					drawer.selectedAction = newReward;
					drawer.isUpgrade = false;
					drawer.title = rewardTitle;
					drawer.subtitle = subtitle;
					drawer.view = 'config';
				}
			});

			// Listen for save-referral-reward event from drawer footer button
			window.addEventListener('save-referral-reward', (e) => {
				this.addReward(e);
			});

			// Listen for update-referral-reward event from drawer footer button
			window.addEventListener('update-referral-reward', (e) => {
				this.updateReward(e);
			});
		},

		openDrawer(context) {
			const drawer = Alpine.store('drawer');
			const title = context === 'advocate' ? 'Add Advocate Reward' : 'Add Friend Reward';

		// Check if Friend reward requires Pro (check via friendRequiresPro from PHP)
		const store = Alpine.store('referrals');
		const isFriendReward = context === 'friend';
		const requiresUpgrade = isFriendReward && store.friendRequiresPro;

			// Use drawer.open() to properly handle timeout cancellation
			const config = {
				type: 'referral',
				view: 'list',
				title: title,
				subtitle: 'Choose a reward type',
				actionId: context, // Store context as actionId
				selectedAction: {},
				originalSnapshot: null,
				isNew: true,
				isUpgrade: requiresUpgrade, // Set to true if Friend reward on Lite plan
			};

			drawer.open(config);
		},

		editReward(context, reward) {
			const drawer = Alpine.store('drawer');
			const title = context === 'advocate' ? 'Edit Advocate Reward' : 'Edit Friend Reward';

			// Normalize minimum_order_amount: convert 0 to empty string so placeholder shows instead of "0"
			// Empty string works better with input type="number" and Alpine.js x-model for showing placeholders
			const normalizedReward = { ...reward };
			if (normalizedReward.minimum_order_amount === 0 || normalizedReward.minimum_order_amount === '0' || normalizedReward.minimum_order_amount === null) {
				normalizedReward.minimum_order_amount = '';
			}

			// Use drawer.open() to properly handle timeout cancellation
			const config = {
				type: 'referral',
				view: 'config',
				title: title,
				subtitle: 'Edit reward settings',
				actionId: context,
				selectedAction: normalizedReward,
				originalSnapshot: { ...reward },
				isNew: false,
				isUpgrade: false,
			};

			drawer.open(config);
		},

		addReward(event) {
			// Prevent any default behavior and stop propagation
			if (event) {
				event.preventDefault();
				event.stopPropagation();
			}

			const drawer = Alpine.store('drawer');
			const context = drawer.actionId; // 'advocate' or 'friend'

			if (!hasSelectedAction(drawer) || !context) {
				return false;
			}

			// Validate free_product has a product selected
			if (drawer.selectedAction.type === 'free_product') {
				const productsIncluded = drawer.selectedAction.products_included || [];
				if (productsIncluded.length === 0) {
					drawer._showProductError = true;
					drawer.isLoading = false;
					return false;
				}
			}

			// Normalize minimum_order_amount: convert empty string/null to 0 before saving
			const rewardToSave = { ...drawer.selectedAction };
			if (rewardToSave.minimum_order_amount === '' || rewardToSave.minimum_order_amount === null || rewardToSave.minimum_order_amount === undefined) {
				rewardToSave.minimum_order_amount = 0;
			}

			// Note: drawer.isLoading is already set to true by drawer.save() before this is called
			// Add to referrals store
			try {
				Alpine.store('referrals').setReward(context, rewardToSave);
			} catch (error) {}

			// Close drawer
			setTimeout(() => {
				drawer.close();
			}, 500);
			return false; // Extra safety to prevent any form submission
		},

		updateReward(event) {
			// Prevent any default behavior and stop propagation
			if (event) {
				event.preventDefault();
				event.stopPropagation();
			}
			const drawer = Alpine.store('drawer');
			const context = drawer.actionId; // 'advocate' or 'friend'

			if (!hasSelectedAction(drawer) || !context) {
				return false;
			}

			// Validate free_product has a product selected
			if (drawer.selectedAction.type === 'free_product') {
				const productsIncluded = drawer.selectedAction.products_included || [];
				if (productsIncluded.length === 0) {
					drawer._showProductError = true;
					drawer.isLoading = false;
					return false;
				}
			}

			// Normalize minimum_order_amount: convert empty string/null to 0 before saving
			const rewardToSave = { ...drawer.selectedAction };
			if (rewardToSave.minimum_order_amount === '' || rewardToSave.minimum_order_amount === null || rewardToSave.minimum_order_amount === undefined) {
				rewardToSave.minimum_order_amount = 0;
			}

			drawer.isLoading = true;

			// Update in referrals store
			Alpine.store('referrals').setReward(context, rewardToSave);

			// Close drawer
			setTimeout(() => {
				drawer.close();
			}, 500);

			return false; // Extra safety to prevent any form submission
		},

		removeReward(context) {
			// No need for confirm() - drawer already has "Are you sure? Yes/No" confirmation
			Alpine.store('referrals').removeReward(context);
		},

		getIcon(rewardType) {
			return Alpine.store('referrals').getIcon(rewardType);
		},

		getTitle(reward, context) {
			return Alpine.store('referrals').getTitle(reward, context);
		},

		getDescription(reward, context) {
			return Alpine.store('referrals').getDescription(reward, context);
		},

		getDisplayRewardTitle(reward, rewardType) {
			if (!reward) return '';

			const referralsData = window.rewardswpDrawerData?.referrals;
			const defaultTitles = referralsData?.rewardTitles || {
				fixed_discount: 'Fixed Amount Discount',
				percentage_discount: 'Percentage Discount',
				free_shipping: 'Free Shipping',
				free_product: 'Free Product',
				points: 'Points Reward',
			};

			return reward.name || defaultTitles[reward.type] || reward.type;
		},

		get currencySymbol() {
			const referralsData = window.rewardswpDrawerData?.referrals;
			return referralsData?.currencySymbol || '$';
		},

		getRewardProductName(productId) {
			// Get product name from the products list if available
			const referralsData = window.rewardswpDrawerData?.referrals;
			if (referralsData?.products && referralsData.products[productId]) {
				return referralsData.products[productId].name;
			}
			return productId;
		},
	}));
}
