<script lang="ts">
	import { authStore } from '$lib/stores/auth.svelte';
	import { apiFetch } from '$lib/utils/api';
	import { toast } from 'svelte-sonner';
	import { Button } from '$lib/components/ui/button';
	import * as Card from '$lib/components/ui/card';
	import { Input } from '$lib/components/ui/input';
	import { EmailInput } from '$lib/components/ui/email-input';
	import * as Modal from '$lib/components/ui/dialog';
	import { Skeleton } from '$lib/components/ui/skeleton';
	import { formatUserName } from '$lib/utils/nameDisplay';
	import { SvelteDate, SvelteURLSearchParams } from 'svelte/reactivity';

	interface GiftCard {
		id: string;
		total_funds_added: number;
		amount_remaining: number;
		created_by?: string;
		created_at: string;
		redeemed_at?: string;
		redeemed_by?: string;
		is_inventory?: boolean;
		last_used_at?: string;
	}

	interface UserBalance {
		user_id: string;
		name: string;
		email: string;
		balance: number;
		updated_at: string;
		/* TODO: add previousFirstName/previousLastName when backend sends them */
	}

	interface GiftCardSummary {
		total_unclaimed: number;
		total_user_balances: number;
		gift_cards: GiftCard[];
		user_balances: UserBalance[];
	}

	const summary = $state<GiftCardSummary>({
		total_unclaimed: 0,
		total_user_balances: 0,
		gift_cards: [],
		user_balances: []
	});

	let cardQuery = $state('');
	let cards = $state<GiftCard[]>([]);
	let balances = $state<UserBalance[]>([]);
	let _totalCards = $state(0);
	let totalBalanceRecords = $state(0);
	let currentPage = $state(1);
	let totalPages = $state(1);
	let loadingSearch = $state(false);

	let activeSection = $state<'cards' | 'balances' | 'expired' | 'expired_cards'>('cards');

	let expiredBalances = $state<
		Array<{
			id: string;
			account_id?: string;
			original_balance: number;
			expired_at: string;
			claimed_at?: string;
			claimed_by_admin?: string;
			notes?: string;
		}>
	>([]);
	let loadingExpired = $state(false);
	let claimingId = $state<string | null>(null);

	let loading = $state(true);
	let showGenerateModal = $state(false);
	let showTopUpModal = $state(false);
	let showTransferModal = $state(false);

	let creating = $state(false);
	let transferring = $state(false);

	let selectedCardId = $state<string | null>(null);

	// Form inputs
	let generateAmount = $state('');
	let generateUserQuery = $state('');
	let generateUsers = $state<
		Array<{
			id: string;
			fullName: string;
			email?: string;
			phone?: string;
			previousFirstName?: string | null;
			previousLastName?: string | null;
		}>
	>([]);
	let generateLoadingUsers = $state(false);
	let topUpAmount = $state('');
	let transferAmount = $state('');
	let transferToCode = $state('');

	// Redesigned generate modal states
	let generateStep = $state<
		| 'type'
		| 'customer'
		| 'amount_email'
		| 'payment'
		| 'cash_entry'
		| 'card_details'
		| 'processing'
		| 'success'
		| 'error'
	>('type');
	let generateType = $state<'code' | 'account' | 'stock'>('code');

	// Page 2: Customer selection state
	let generateCustomerTab = $state<'current' | 'member' | 'guest'>('current');
	let currentCustomerInfo = $state<{
		id: string;
		name: string;
		email?: string;
		phone?: string;
		previousFirstName?: string | null;
		previousLastName?: string | null;
	} | null>(null);
	let loadingCurrentCustomer = $state(false);

	// Selection from Page 2
	let selectedCustomer = $state<{
		id: string;
		name: string;
		email?: string;
		previousFirstName?: string | null;
		previousLastName?: string | null;
	} | null>(null);
	let isGuestSelected = $state(false);

	// Page 3: Recipient email input
	let generateEmail = $state('');

	let _tillGuestEmail = $state<string | undefined>(undefined);

	// Payment Processing States
	let cashAmount = $state('');
	let _cashTendered = $state(0);
	let _extraAsTip = $state(false);

	let ephemeralCardNumber = $state('');
	let ephemeralCardExpiry = $state('');
	let ephemeralCardCVC = $state('');
	let _ephemeralCardError = $state('');

	let paymentError = $state('');
	let paymentResult = $state<{
		id: string;
		giftcard_code?: string;
		item_id?: string;
		status?: string;
	} | null>(null);
	let cardMachineItemID = $state<string | null>(null);
	let _checkoutId = $state<string | null>(null);
	let processingMessage = $state('Processing payment...');

	// Idempotency Key
	let idempotencyKey = $state('');

	function getIdempotencyKey(): string {
		if (!idempotencyKey) {
			const array = new Uint8Array(16);
			if (typeof window !== 'undefined' && window.crypto) {
				window.crypto.getRandomValues(array);
			} else {
				for (let i = 0; i < 16; i++) array[i] = Math.floor(Math.random() * 256);
			}
			array[6] = (array[6] & 0x0f) | 0x40;
			array[8] = (array[8] & 0x3f) | 0x80;
			idempotencyKey = [...array]
				.map((b, i) => {
					const hex = b.toString(16).padStart(2, '0');
					if (i === 4 || i === 6 || i === 8 || i === 10) return '-' + hex;
					return hex;
				})
				.join('');
		}
		return idempotencyKey;
	}

	let topUpStep = $state<
		| 'choice'
		| 'amount'
		| 'payment'
		| 'cash_entry'
		| 'card_details'
		| 'processing'
		| 'success'
		| 'error'
	>('choice');
	let topUpMode = $state<'giveaway' | 'purchase'>('giveaway');

	// Validation
	const generateError = $derived(
		generateAmount && (isNaN(Number(generateAmount)) || Number(generateAmount) <= 0)
			? 'Must be a valid positive number'
			: ''
	);
	const topUpError = $derived(
		topUpAmount && (isNaN(Number(topUpAmount)) || Number(topUpAmount) <= 0)
			? 'Must be a valid positive number'
			: ''
	);
	const transferAmountError = $derived(
		transferAmount && (isNaN(Number(transferAmount)) || Number(transferAmount) <= 0)
			? 'Must be a valid positive number'
			: ''
	);
	const transferCodeError = $derived(
		transferToCode && transferToCode.replace(/[^a-zA-Z0-9]/g, '').length !== 12
			? 'Code must be exactly 12 characters'
			: ''
	);

	const isAmountValid = $derived(
		generateType === 'stock' ? true : !!(generateAmount && !generateError)
	);

	const isEmailValid = $derived.by(() => {
		if (generateType !== 'code') return true;
		const trimmed = generateEmail.trim();
		if (isGuestSelected) {
			// Mandatory for guest: non-empty & valid email format
			return trimmed !== '' && trimmed.includes('@') && trimmed.includes('.');
		}
		// Optional for member: if non-empty, must be valid email format
		if (trimmed === '') return true;
		return trimmed.includes('@') && trimmed.includes('.');
	});

	const isGenerateValid = $derived(generateType === 'stock' ? true : isAmountValid && isEmailValid);

	const isTopUpValid = $derived(topUpAmount && !topUpError);
	const isTransferValid = $derived(
		transferAmount && !transferAmountError && transferToCode && !transferCodeError
	);

	// TillPayment state
	const _showTillPayment = $state(false);
	const _tillAmount = $state(0);
	const _tillAction = $state<'create' | 'topup'>('create');
	const _tillGiftCardId = $state<string | undefined>(undefined);
	const _tillUserId = $state<string | undefined>(undefined);
	const _tillDelivery = $state<'account' | 'code'>('code');

	async function fetchGiftCards(page: number = 1, search: string = '') {
		loading = true;
		loadingSearch = true;
		try {
			const params = new SvelteURLSearchParams({
				page: page.toString(),
				per_page: '10'
			});
			if (search.trim()) params.append('q', search.trim());

			const res = await apiFetch(`/api/admin/gift-cards?${params}`);
			if (res.ok) {
				const data = await res.json();
				summary.total_unclaimed = data.total_unclaimed;
				summary.total_user_balances = data.total_user_balances;
				cards = data.gift_cards;
				balances = data.user_balances;
				_totalCards = data.total;
				totalBalanceRecords = data.ub_total ?? data.user_balances?.length ?? 0;
				currentPage = data.page;
				totalPages = data.totalPages;
			} else {
				toast.error('Failed to fetch gift cards');
			}
		} catch (_err) {
			toast.error('Network error fetching gift cards');
		} finally {
			loading = false;
			loadingSearch = false;
		}
	}

	function searchCards() {
		currentPage = 1;
		fetchGiftCards(1, cardQuery);
	}

	async function fetchExpiredBalances() {
		loadingExpired = true;
		try {
			const res = await apiFetch('/api/admin/gift-cards/expired-balances');
			if (res.ok) {
				const data = await res.json();
				expiredBalances = data.expired_balances || [];
			} else {
				toast.error('Failed to fetch expired balances');
			}
		} catch (_err) {
			toast.error('Network error fetching expired balances');
		} finally {
			loadingExpired = false;
		}
	}

	async function claimExpiredBalance(balanceId: string) {
		claimingId = balanceId;
		try {
			const res = await apiFetch('/api/admin/gift-cards/expired-balances/claim', {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json'
				},
				body: JSON.stringify({ balance_id: balanceId })
			});
			if (res.ok) {
				toast.success('Balance claimed successfully');
				await fetchExpiredBalances();
			} else {
				const err = await res.json();
				toast.error(err.error || 'Failed to claim balance');
			}
		} catch (_err) {
			toast.error('Network error claiming balance');
		} finally {
			claimingId = null;
		}
	}

	function nextPage() {
		if (currentPage < totalPages) {
			fetchGiftCards(currentPage + 1, cardQuery);
		}
	}

	function previousPage() {
		if (currentPage > 1) {
			fetchGiftCards(currentPage - 1, cardQuery);
		}
	}

	async function fetchCurrentCustomerForGenerate() {
		loadingCurrentCustomer = true;
		currentCustomerInfo = null;
		try {
			const res = await apiFetch('/api/admin/today/current-next');
			if (res.ok) {
				const data = await res.json();
				const appointment = data.current || data.next;
				if (appointment?.user?.id) {
					currentCustomerInfo = {
						id: appointment.user.id,
						name: appointment.user.full_name || appointment.user.name || 'Current Customer',
						email: appointment.user.email,
						phone: appointment.user.phone,
						previousFirstName: appointment.user.previous_first_name,
						previousLastName: appointment.user.previous_last_name
					};
				}
			}
		} catch {
			// Ignore silently
		} finally {
			loadingCurrentCustomer = false;
		}
	}

	function goToGeneratePayment() {
		if (!isGenerateValid) return;
		generateStep = 'payment';
	}

	async function searchGenerateCustomers() {
		if (!generateUserQuery.trim()) return;
		generateLoadingUsers = true;
		try {
			const res = await apiFetch(
				`/api/admin/users?page=1&per_page=5&q=${encodeURIComponent(generateUserQuery)}`
			);
			if (res.ok) {
				const data = await res.json();
				const excludedRoles = ['admin', 'guest', 'affiliate'];
				generateUsers = (data.users || []).filter(
					(u: { account_role: string }) => !excludedRoles.includes(u.account_role)
				);
			}
		} catch {
			toast.error('Failed to search users');
		} finally {
			generateLoadingUsers = false;
		}
	}

	async function generateInventoryCard() {
		creating = true;
		try {
			const res = await apiFetch('/api/admin/gift-cards', {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json'
				},
				body: JSON.stringify({
					amount: 0,
					is_inventory: true
				})
			});
			if (res.ok) {
				const data = await res.json();
				toast.success(`Inventory card ${formatCardCode(data.id)} created`);
				showGenerateModal = false;
				resetGenerateModal();
				await fetchGiftCards();
			} else {
				const errText = await res.text();
				toast.error(errText || 'Failed to create inventory card');
			}
		} catch (_err) {
			toast.error('Network error');
		} finally {
			creating = false;
		}
	}

	function goToTopUpPayment() {
		if (!isTopUpValid) return;
		if (topUpMode === 'giveaway') {
			if (selectedCardId) {
				handleEmbeddedGiveawayTopUp(selectedCardId);
			}
		} else {
			topUpStep = 'payment';
		}
	}

	async function transferCard() {
		if (!isTransferValid || !selectedCardId) return;
		transferring = true;
		try {
			const res = await apiFetch(`/api/admin/gift-cards/${selectedCardId}/transfer`, {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json'
				},
				body: JSON.stringify({
					to_card_id: transferToCode,
					amount: Number(transferAmount)
				})
			});
			if (res.ok) {
				toast.success('Balance transferred successfully');
				showTransferModal = false;
				transferAmount = '';
				transferToCode = '';
				await fetchGiftCards();
			} else {
				const errText = await res.text();
				toast.error(errText || 'Failed to transfer balance');
			}
		} catch (_err) {
			toast.error('Network error transferring balance');
		} finally {
			transferring = false;
		}
	}

	function resetGenerateModal() {
		generateStep = 'type';
		generateType = 'code';
		generateCustomerTab = 'current';
		currentCustomerInfo = null;
		selectedCustomer = null;
		isGuestSelected = false;
		generateAmount = '';
		generateEmail = '';
		generateUserQuery = '';
		generateUsers = [];
		_tillGuestEmail = undefined;
	}

	function resetTopUpModal() {
		topUpStep = 'choice';
		topUpMode = 'giveaway';
		topUpAmount = '';

		// Reset payment
		cashAmount = '';
		_cashTendered = 0;
		_extraAsTip = false;
		ephemeralCardNumber = '';
		ephemeralCardExpiry = '';
		ephemeralCardCVC = '';
		_ephemeralCardError = '';
		paymentError = '';
		paymentResult = null;
		cardMachineItemID = null;
		_checkoutId = null;
		idempotencyKey = '';
	}

	// =============== Embedded Payment Handlers ===============

	const isEphemeralCardValid = $derived(
		ephemeralCardNumber.replace(/\s/g, '').length >= 13 &&
			ephemeralCardExpiry.includes('/') &&
			ephemeralCardExpiry.length === 5 &&
			ephemeralCardCVC.length >= 3
	);

	function handleEphemeralCardNumberInput(e: Event) {
		const target = e.currentTarget as HTMLInputElement;
		const clean = target.value.replace(/\D/g, '');
		const formatted = clean.match(/.{1,4}/g)?.join(' ') || clean;
		ephemeralCardNumber = formatted.slice(0, 19);
	}

	function handleEphemeralExpiryInput(e: Event) {
		const target = e.currentTarget as HTMLInputElement;
		const clean = target.value.replace(/\D/g, '');
		if (clean.length > 2) {
			ephemeralCardExpiry = clean.slice(0, 2) + '/' + clean.slice(2, 4);
		} else {
			ephemeralCardExpiry = clean;
		}
	}

	function handleEphemeralCvcInput(e: Event) {
		const target = e.currentTarget as HTMLInputElement;
		ephemeralCardCVC = target.value.replace(/\D/g, '').slice(0, 4);
	}

	function setModalStep(
		actionType: 'create' | 'topup',
		step: 'processing' | 'success' | 'error' | 'payment' | 'cash_entry' | 'card_details'
	) {
		if (actionType === 'create') {
			generateStep = step;
		} else {
			topUpStep = step;
		}
	}

	async function handleEmbeddedCashPayment(actionType: 'create' | 'topup', gcId?: string) {
		setModalStep(actionType, 'processing');
		processingMessage = 'Processing cash payment...';
		try {
			const amt = actionType === 'create' ? Number(generateAmount) : Number(topUpAmount);
			const body: Record<string, unknown> = {
				item_type: 'gift_card',
				action: actionType,
				amount: amt,
				payment_method: 'cash',
				idempotency_key: getIdempotencyKey()
			};
			if (gcId) body.gift_card_id = gcId;
			if (selectedCustomer) body.user_id = selectedCustomer.id;
			if (actionType === 'create' && generateType === 'account' && selectedCustomer)
				body.redeem_to_user_id = selectedCustomer.id;

			const res = await apiFetch('/api/admin/till/sale', {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json'
				},
				body: JSON.stringify(body)
			});
			if (res.ok) {
				const data = await res.json();
				paymentResult = { ...data };
				setModalStep(actionType, 'success');
				await fetchGiftCards();
			} else {
				paymentError = await res.text();
				setModalStep(actionType, 'error');
			}
		} catch {
			paymentError = 'Network error processing cash payment';
			setModalStep(actionType, 'error');
		}
	}

	async function handleEmbeddedCardMachinePayment(actionType: 'create' | 'topup', gcId?: string) {
		setModalStep(actionType, 'processing');
		processingMessage = 'Initiating card machine...';
		try {
			const amt = actionType === 'create' ? Number(generateAmount) : Number(topUpAmount);
			const body: Record<string, unknown> = {
				item_type: 'gift_card',
				action: actionType,
				amount: amt,
				payment_method: 'card_machine',
				idempotency_key: getIdempotencyKey()
			};
			if (gcId) body.gift_card_id = gcId;
			if (selectedCustomer) body.user_id = selectedCustomer.id;
			if (actionType === 'create' && generateType === 'account' && selectedCustomer)
				body.redeem_to_user_id = selectedCustomer.id;

			const res = await apiFetch('/api/admin/till/sale', {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json'
				},
				body: JSON.stringify(body)
			});
			if (res.ok) {
				const data = await res.json();
				cardMachineItemID = data.item_id || null;
				if (data.status === 'pending' && data.checkout_id) {
					_checkoutId = data.checkout_id;
					setModalStep(actionType, 'processing');
					pollEmbeddedCheckout(data.checkout_id, amt, actionType);
				} else {
					paymentResult = { ...data };
					setModalStep(actionType, 'success');
					await fetchGiftCards();
				}
			} else {
				paymentError = await res.text();
				setModalStep(actionType, 'error');
			}
		} catch {
			paymentError = 'Network error initiating card machine payment';
			setModalStep(actionType, 'error');
		}
	}

	async function pollEmbeddedCheckout(ckId: string, amt: number, actionType: 'create' | 'topup') {
		const maxAttempts = 60;
		let attempts = 0;
		while (attempts < maxAttempts) {
			if (!showGenerateModal && !showTopUpModal) {
				return;
			}
			await new Promise((r) => setTimeout(r, 2000));
			attempts++;
			try {
				const res = await apiFetch(`/api/admin/till/sale/checkout/${ckId}/status`);
				if (res.ok) {
					const data = await res.json();
					if (data.status === 'COMPLETED') {
						paymentResult = {
							id: data.payment_id || ckId,
							item_id: cardMachineItemID ?? undefined,
							giftcard_code: data.giftcard_code || undefined
						};
						setModalStep(actionType, 'success');
						await fetchGiftCards();
						return;
					}
				}
			} catch {
				// Continue polling
			}
		}
		paymentError = 'Card machine payment timed out. Please check Square dashboard.';
		setModalStep(actionType, 'error');
	}

	async function handleEmbeddedEphemeralCardPayment(actionType: 'create' | 'topup', gcId?: string) {
		const cardNum = ephemeralCardNumber.replace(/\s/g, '');
		const [monthStr, yearStr] = ephemeralCardExpiry.split('/');
		const expMonth = parseInt(monthStr, 10);
		const expYear = 2000 + parseInt(yearStr, 10);

		setModalStep(actionType, 'processing');
		processingMessage = 'Processing card payment...';
		try {
			const amt = actionType === 'create' ? Number(generateAmount) : Number(topUpAmount);
			const body: Record<string, unknown> = {
				item_type: 'gift_card',
				action: actionType,
				amount: amt,
				payment_method: 'online_square',
				idempotency_key: getIdempotencyKey(),
				card_number: cardNum,
				card_exp_month: expMonth,
				card_exp_year: expYear,
				card_cvc: ephemeralCardCVC
			};
			if (gcId) body.gift_card_id = gcId;
			if (selectedCustomer) body.user_id = selectedCustomer.id;
			if (actionType === 'create' && generateType === 'account' && selectedCustomer)
				body.redeem_to_user_id = selectedCustomer.id;

			const res = await apiFetch('/api/admin/till/sale', {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json'
				},
				body: JSON.stringify(body)
			});
			if (res.ok) {
				const data = await res.json();
				paymentResult = { ...data };
				setModalStep(actionType, 'success');
				await fetchGiftCards();
			} else {
				paymentError = await res.text();
				setModalStep(actionType, 'error');
			}
		} catch {
			paymentError = 'Network error processing card payment';
			setModalStep(actionType, 'error');
		}
	}

	async function handleEmbeddedGiveawayTopUp(gcId: string) {
		topUpStep = 'processing';
		processingMessage = 'Processing on-the-house top-up...';
		try {
			const body: Record<string, unknown> = {
				item_type: 'gift_card',
				action: 'topup',
				amount: Number(topUpAmount),
				payment_method: 'on_the_house',
				gift_card_id: gcId,
				idempotency_key: 'till-on-the-house-' + gcId + '-' + Date.now()
			};

			const res = await apiFetch('/api/admin/till/sale', {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json'
				},
				body: JSON.stringify(body)
			});
			if (res.ok) {
				const data = await res.json();
				paymentResult = { ...data };
				topUpStep = 'success';
				await fetchGiftCards();
			} else {
				paymentError = await res.text();
				topUpStep = 'error';
			}
		} catch {
			paymentError = 'Network error processing giveaway top-up';
			topUpStep = 'error';
		}
	}

	function handleCodeInput(e: Event) {
		const target = e.target as HTMLInputElement;
		let raw = target.value.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
		if (raw.length > 12) raw = raw.slice(0, 12);
		let formatted = '';
		if (raw.length > 0) formatted += raw.slice(0, 4);
		if (raw.length > 4) formatted += '-' + raw.slice(4, 8);
		if (raw.length > 8) formatted += '-' + raw.slice(8, 12);
		transferToCode = formatted;
	}

	function formatCardCode(id: string): string {
		if (id.length !== 12) return id;
		return `${id.slice(0, 4)}-${id.slice(4, 8)}-${id.slice(8, 12)}`.toUpperCase();
	}

	function formatCurrency(amount: number): string {
		return new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).format(amount);
	}

	function formatDate(dateStr: string): string {
		return new SvelteDate(dateStr).toLocaleDateString('en-GB', {
			day: 'numeric',
			month: 'short',
			year: 'numeric',
			timeZone: 'Europe/London'
		});
	}

	function getExpiryDate(lastUsedAt?: string): Date | null {
		if (!lastUsedAt) return null;
		const date = new SvelteDate(lastUsedAt);
		date.setMonth(date.getMonth() + 24);
		return date;
	}

	function isExpired(lastUsedAt?: string): boolean {
		const expiry = getExpiryDate(lastUsedAt);
		return expiry !== null && expiry < new SvelteDate();
	}

	// =============== Sorting ===============
	type SortKey =
		| 'code'
		| 'added'
		| 'remaining'
		| 'created'
		| 'status'
		| 'name'
		| 'email'
		| 'balance'
		| 'updated';

	const cardSort = $state<{ key: SortKey; dir: 'asc' | 'desc' }>({ key: 'created', dir: 'desc' });
	const balanceSort = $state<{ key: SortKey; dir: 'asc' | 'desc' }>({
		key: 'updated',
		dir: 'desc'
	});

	function toggleCardSort(key: SortKey) {
		if (cardSort.key === key) {
			cardSort.dir = cardSort.dir === 'asc' ? 'desc' : 'asc';
		} else {
			cardSort.key = key;
			cardSort.dir = 'asc';
		}
	}

	function toggleBalanceSort(key: SortKey) {
		if (balanceSort.key === key) {
			balanceSort.dir = balanceSort.dir === 'asc' ? 'desc' : 'asc';
		} else {
			balanceSort.key = key;
			balanceSort.dir = 'asc';
		}
	}

	const sortedCards = $derived.by(() => {
		let filtered = [...cards];
		if (activeSection === 'cards') {
			filtered = cards.filter((gc) => !isExpired(gc.last_used_at));
		} else if (activeSection === 'expired_cards') {
			filtered = cards.filter((gc) => isExpired(gc.last_used_at));
		}
		const sorted = [...filtered];
		const { key, dir } = cardSort;
		const mul = dir === 'asc' ? 1 : -1;
		sorted.sort((a, b) => {
			switch (key) {
				case 'code':
					return a.id.localeCompare(b.id) * mul;
				case 'added':
					return (a.total_funds_added - b.total_funds_added) * mul;
				case 'remaining':
					return (a.amount_remaining - b.amount_remaining) * mul;
				case 'created':
					return (
						(new SvelteDate(a.created_at).getTime() - new SvelteDate(b.created_at).getTime()) * mul
					);
				case 'status': {
					const aVal = a.redeemed_by ? 2 : a.amount_remaining === 0 ? 1 : 0;
					const bVal = b.redeemed_by ? 2 : b.amount_remaining === 0 ? 1 : 0;
					return (aVal - bVal) * mul;
				}
				default:
					return 0;
			}
		});
		return sorted;
	});

	const activeCardsCount = $derived(cards.filter((gc) => !isExpired(gc.last_used_at)).length);
	const expiredCardsCount = $derived(cards.filter((gc) => isExpired(gc.last_used_at)).length);

	const sortedBalances = $derived.by(() => {
		const bals = [...balances];
		const { key, dir } = balanceSort;
		const mul = dir === 'asc' ? 1 : -1;
		bals.sort((a, b) => {
			switch (key) {
				case 'name':
					return a.name.localeCompare(b.name) * mul;
				case 'email':
					return a.email.localeCompare(b.email) * mul;
				case 'balance':
					return (a.balance - b.balance) * mul;
				case 'updated':
					return (
						(new SvelteDate(a.updated_at).getTime() - new SvelteDate(b.updated_at).getTime()) * mul
					);
				default:
					return 0;
			}
		});
		return bals;
	});

	function sortArrow(key: SortKey, state: typeof cardSort): string {
		if (state.key !== key) return '';
		return state.dir === 'asc' ? ' \u25B2' : ' \u25BC';
	}

	$effect(() => {
		if (authStore.currentToken) {
			fetchGiftCards();
		}
	});
</script>

<Card.Root>
	<Card.Header>
		<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
			<div>
				<Card.Title>Gift Card Management</Card.Title>
				<Card.Description>
					Create, top-up, and track gift cards. Unclaimed cards can be topped up or transferred.
				</Card.Description>
			</div>
			<Button onclick={() => (showGenerateModal = true)}>Generate Gift Card</Button>
		</div>
	</Card.Header>

	<Card.Content class="space-y-6">
		<div class="grid gap-4 sm:grid-cols-2 md:grid-cols-3">
			<div class="rounded-xl border bg-card p-4">
				<div class="text-xs text-gray-400">Total Unclaimed</div>
				<div class="mt-1 text-2xl font-bold text-card-foreground">
					{loading ? '...' : formatCurrency(summary.total_unclaimed)}
				</div>
			</div>
			<div class="rounded-xl border bg-card p-4">
				<div class="text-xs text-gray-400">User Account Balances</div>
				<div class="mt-1 text-2xl font-bold text-card-foreground">
					{loading ? '...' : formatCurrency(summary.total_user_balances)}
				</div>
			</div>
			<div class="rounded-xl border bg-card p-4 sm:col-span-2 md:col-span-1">
				<div class="text-xs text-gray-400">Combined Liability</div>
				<div class="mt-1 text-2xl font-bold text-card-foreground">
					{loading ? '...' : formatCurrency(summary.total_unclaimed + summary.total_user_balances)}
				</div>
			</div>
		</div>

		<!-- Tabs -->
		<div class="flex border-b border-gray-200">
			<button
				type="button"
				class="border-b-2 px-4 py-2 text-sm font-medium transition-colors {activeSection === 'cards'
					? 'border-primary font-semibold text-primary'
					: 'border-transparent text-muted-foreground hover:text-foreground'}"
				onclick={() => (activeSection = 'cards')}
			>
				Active Gift Cards ({activeCardsCount})
			</button>
			<button
				type="button"
				class="border-b-2 px-4 py-2 text-sm font-medium transition-colors {activeSection ===
				'expired_cards'
					? 'border-primary font-semibold text-primary'
					: 'border-transparent text-muted-foreground hover:text-foreground'}"
				onclick={() => (activeSection = 'expired_cards')}
			>
				Expired Gift Cards ({expiredCardsCount})
			</button>
			<button
				type="button"
				class="border-b-2 px-4 py-2 text-sm font-medium transition-colors {activeSection ===
				'balances'
					? 'border-primary font-semibold text-primary'
					: 'border-transparent text-muted-foreground hover:text-foreground'}"
				onclick={() => (activeSection = 'balances')}
			>
				Customer Account Balances ({totalBalanceRecords})
			</button>
			<button
				type="button"
				class="border-b-2 px-4 py-2 text-sm font-medium transition-colors {activeSection ===
				'expired'
					? 'border-primary font-semibold text-primary'
					: 'border-transparent text-muted-foreground hover:text-foreground'}"
				onclick={() => {
					activeSection = 'expired';
					if (expiredBalances.length === 0) fetchExpiredBalances();
				}}
			>
				Expired Balances ({expiredBalances.length})
			</button>
		</div>

		{#if activeSection === 'cards' || activeSection === 'expired_cards'}
			<div class="mb-4 flex gap-2">
				<Input
					placeholder="Search by gift card code..."
					bind:value={cardQuery}
					onkeyup={(e) => {
						if (e.key === 'Enter') searchCards();
					}}
				/>
				<Button onclick={searchCards} disabled={loadingSearch}>
					{loadingSearch ? 'Searching...' : 'Search'}
				</Button>
			</div>

			<div class="hidden w-full overflow-x-auto md:block">
				<table class="w-full table-auto border-collapse text-sm">
					<thead>
						<tr class="border-b text-left text-xs tracking-wider text-gray-500 uppercase">
							<th
								class="cursor-pointer py-3 font-medium select-none hover:text-foreground"
								onclick={() => toggleCardSort('code')}
							>
								Card Code{sortArrow('code', cardSort)}
							</th>
							<th
								class="cursor-pointer py-3 font-medium select-none hover:text-foreground"
								onclick={() => toggleCardSort('added')}
							>
								Total Added{sortArrow('added', cardSort)}
							</th>
							<th
								class="cursor-pointer py-3 font-medium select-none hover:text-foreground"
								onclick={() => toggleCardSort('remaining')}
							>
								Remaining{sortArrow('remaining', cardSort)}
							</th>
							<th
								class="cursor-pointer py-3 font-medium select-none hover:text-foreground"
								onclick={() => toggleCardSort('created')}
							>
								Created On{sortArrow('created', cardSort)}
							</th>
							<th class="py-3 font-medium text-gray-500">Expiry</th>
							<th
								class="cursor-pointer py-3 font-medium select-none hover:text-foreground"
								onclick={() => toggleCardSort('status')}
							>
								Status{sortArrow('status', cardSort)}
							</th>
							<th class="py-3 text-center font-medium">Actions</th>
						</tr>
					</thead>
					<tbody class={sortedCards.length > 0 && (loading || loadingSearch) ? 'opacity-60' : ''}>
						{#if sortedCards.length === 0 && !loading && !loadingSearch}
							<tr>
								<td colspan="8" class="py-8 text-center text-gray-500">
									{activeSection === 'expired_cards'
										? 'No expired gift cards found.'
										: 'No gift cards generated yet. Click "Generate Gift Card" to create one.'}
								</td>
							</tr>
						{:else if sortedCards.length > 0}
							{#each sortedCards as gc (gc.id)}
								<tr class="border-b hover:bg-gray-50">
									<td class="py-3 font-mono font-bold text-gray-900">{formatCardCode(gc.id)}</td>
									<td class="py-3 font-medium text-gray-600"
										>{formatCurrency(gc.total_funds_added)}</td
									>
									<td class="py-3 font-semibold text-card-foreground"
										>{formatCurrency(gc.amount_remaining)}</td
									>
									<td class="py-3 text-gray-600">{formatDate(gc.created_at)}</td>
									<td class="py-3 text-gray-600">
										{#if gc.last_used_at}
											{formatDate(getExpiryDate(gc.last_used_at)!.toISOString())}
										{:else}
											<span class="text-gray-400 italic">—</span>
										{/if}
									</td>
									<td class="py-3">
										{#if gc.redeemed_by}
											<span
												class="inline-flex items-center rounded-full border border-green-200 bg-green-50 px-2.5 py-0.5 text-xs font-medium text-green-700"
											>
												Claimed
											</span>
										{:else if gc.is_inventory}
											<span
												class="inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-700"
											>
												Inventory
											</span>
										{:else if isExpired(gc.last_used_at)}
											<span
												class="inline-flex items-center rounded-full border border-red-200 bg-red-50 px-2.5 py-0.5 text-xs font-medium text-red-700"
											>
												Expired
											</span>
										{:else if gc.amount_remaining === 0}
											<span
												class="inline-flex items-center rounded-full border border-gray-200 bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600"
											>
												Spent
											</span>
										{:else}
											<span
												class="inline-flex items-center rounded-full border border-gray-200 bg-gray-50 px-2.5 py-0.5 text-xs font-medium text-gray-600"
											>
												Active
											</span>
										{/if}
									</td>
									<td class="py-3 text-center">
										<div class="flex items-center justify-center gap-2">
											{#if !gc.redeemed_by}
												<Button
													variant="outline"
													size="sm"
													onclick={() => {
														selectedCardId = gc.id;
														showTopUpModal = true;
													}}
												>
													Top Up
												</Button>
												<Button
													variant="outline"
													size="sm"
													onclick={() => {
														selectedCardId = gc.id;
														showTransferModal = true;
													}}
												>
													Transfer
												</Button>
											{:else}
												<span class="text-xs text-gray-400 italic">No actions available</span>
											{/if}
										</div>
									</td>
								</tr>
							{/each}
						{/if}
					</tbody>
				</table>
			</div>

			<!-- Mobile view - Cards -->
			<div class="grid gap-4 md:hidden">
				{#if sortedCards.length === 0 && !loading && !loadingSearch}
					<div class="rounded-lg border border-dashed py-8 text-center text-gray-500">
						{activeSection === 'expired_cards'
							? 'No expired gift cards found.'
							: 'No gift cards generated yet.'}
					</div>
				{:else if sortedCards.length > 0}
					<div class={loading || loadingSearch ? 'opacity-60' : ''}>
						{#each sortedCards as gc (gc.id)}
							<div class="space-y-3 rounded-lg border p-4 hover:bg-gray-50">
								<div class="flex items-center justify-between">
									<span class="font-mono font-bold text-gray-900">{formatCardCode(gc.id)}</span>
									{#if gc.redeemed_by}
										<span
											class="inline-flex items-center rounded-full border border-green-200 bg-green-50 px-2 py-0.5 text-xs font-medium text-green-700"
										>
											Claimed
										</span>
									{:else if gc.is_inventory}
										<span
											class="inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-700"
										>
											Inventory
										</span>
									{:else if isExpired(gc.last_used_at)}
										<span
											class="inline-flex items-center rounded-full border border-red-200 bg-red-50 px-2.5 py-0.5 text-xs font-medium text-red-700"
										>
											Expired
										</span>
									{:else if gc.amount_remaining === 0}
										<span
											class="inline-flex items-center rounded-full border border-gray-200 bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600"
										>
											Spent
										</span>
									{:else}
										<span
											class="inline-flex items-center rounded-full border border-blue-200 bg-blue-50 px-2.5 py-0.5 text-xs font-medium text-blue-700"
										>
											Active
										</span>
									{/if}
								</div>
								<div class="grid grid-cols-2 gap-2 border-t border-b py-2 text-xs text-gray-600">
									<div>
										<span class="text-gray-400">Total Added:</span>
										<span class="ml-1 font-semibold text-gray-700"
											>{formatCurrency(gc.total_funds_added)}</span
										>
									</div>
									<div>
										<span class="text-gray-400">Remaining:</span>
										<span class="ml-1 font-semibold text-gray-700"
											>{formatCurrency(gc.amount_remaining)}</span
										>
									</div>
									<div class="col-span-2">
										<span class="text-gray-400">Created:</span>
										<span class="ml-1 font-medium text-gray-700">{formatDate(gc.created_at)}</span>
									</div>
									{#if gc.last_used_at}
										<div class="col-span-2">
											<span class="text-gray-400">Expires:</span>
											<span class="ml-1 font-medium text-gray-700"
												>{formatDate(getExpiryDate(gc.last_used_at)!.toISOString())}</span
											>
										</div>
									{/if}
								</div>
								<div class="flex justify-end gap-2 pt-1">
									{#if !gc.redeemed_by}
										<Button
											variant="outline"
											size="sm"
											onclick={() => {
												selectedCardId = gc.id;
												showTopUpModal = true;
											}}
											class="flex-1"
										>
											Top Up
										</Button>
										<Button
											variant="outline"
											size="sm"
											onclick={() => {
												selectedCardId = gc.id;
												showTransferModal = true;
											}}
											class="flex-1"
										>
											Transfer
										</Button>
									{/if}
								</div>
							</div>
						{/each}
					</div>
				{/if}
			</div>

			{#if totalPages > 1}
				<div class="mt-3 flex items-center justify-between border-t pt-3 text-sm">
					<Button
						variant="outline"
						size="sm"
						onclick={previousPage}
						disabled={currentPage === 1 || loadingSearch}
					>
						Previous
					</Button>
					<span class="text-xs text-gray-600">Page {currentPage} of {totalPages}</span>
					<Button
						variant="outline"
						size="sm"
						onclick={nextPage}
						disabled={currentPage === totalPages || loadingSearch}
					>
						Next
					</Button>
				</div>
			{/if}
		{:else if activeSection === 'balances'}
			<!-- Desktop - Account Balances -->
			<div class="hidden w-full overflow-x-auto md:block">
				<table class="w-full table-auto border-collapse text-sm">
					<thead>
						<tr class="border-b text-left text-xs tracking-wider text-gray-500 uppercase">
							<th
								class="cursor-pointer py-3 font-medium select-none hover:text-foreground"
								onclick={() => toggleBalanceSort('name')}
							>
								Customer{sortArrow('name', balanceSort)}
							</th>
							<th
								class="cursor-pointer py-3 font-medium select-none hover:text-foreground"
								onclick={() => toggleBalanceSort('email')}
							>
								Email{sortArrow('email', balanceSort)}
							</th>
							<th
								class="cursor-pointer py-3 font-medium select-none hover:text-foreground"
								onclick={() => toggleBalanceSort('balance')}
							>
								Account Balance{sortArrow('balance', balanceSort)}
							</th>
							<th
								class="cursor-pointer py-3 font-medium select-none hover:text-foreground"
								onclick={() => toggleBalanceSort('updated')}
							>
								Last Updated{sortArrow('updated', balanceSort)}
							</th>
						</tr>
					</thead>
					<tbody>
						{#if loading}
							{#each Array(3) as _, i (i)}
								<tr class="border-b">
									<td class="py-3"><Skeleton class="h-4 w-36" /></td>
									<td class="py-3"><Skeleton class="h-4 w-44" /></td>
									<td class="py-3"><Skeleton class="h-4 w-16" /></td>
									<td class="py-3"><Skeleton class="h-4 w-24" /></td>
								</tr>
							{/each}
						{:else if balances.length === 0}
							<tr>
								<td colspan="4" class="py-8 text-center text-gray-500">
									No customers have redeemed gift cards yet.
								</td>
							</tr>
						{:else}
							{#each sortedBalances as ub (ub.user_id)}
								<tr class="border-b hover:bg-gray-50">
									<td class="py-3 font-medium text-gray-900"
										>{ub.name}<!-- TODO: add formerly name when previous name data is available --></td
									>
									<td class="py-3 text-gray-600">{ub.email}</td>
									<td class="py-3 font-semibold text-primary">{formatCurrency(ub.balance)}</td>
									<td class="py-3 text-gray-600">{formatDate(ub.updated_at)}</td>
								</tr>
							{/each}
						{/if}
					</tbody>
				</table>
			</div>

			<!-- Mobile view - Balances -->
			<div class="grid gap-4 md:hidden">
				{#if loading}
					{#each Array(2) as _, i (i)}
						<div class="space-y-3 rounded-lg border p-4">
							<Skeleton class="h-4 w-36" />
							<Skeleton class="h-4 w-full" />
							<Skeleton class="h-4 w-24" />
						</div>
					{/each}
				{:else if balances.length === 0}
					<div class="rounded-lg border border-dashed py-8 text-center text-gray-500">
						No customers have redeemed gift cards yet.
					</div>
				{:else}
					{#each sortedBalances as ub (ub.user_id)}
						<div class="space-y-3 rounded-lg border p-4 hover:bg-gray-50">
							<div class="flex items-center justify-between">
								<span class="font-medium text-gray-900"
									>{ub.name}<!-- TODO: add formerly name when previous name data is available --></span
								>
							</div>
							<div class="grid grid-cols-2 gap-2 border-t border-b py-2 text-xs text-gray-600">
								<div class="col-span-2">
									<span class="text-gray-400">Email:</span>
									<span class="ml-1 font-medium text-gray-700">{ub.email}</span>
								</div>
								<div>
									<span class="text-gray-400">Balance:</span>
									<span class="ml-1 font-bold text-primary">{formatCurrency(ub.balance)}</span>
								</div>
								<div>
									<span class="text-gray-400">Updated:</span>
									<span class="ml-1 font-medium text-gray-700">{formatDate(ub.updated_at)}</span>
								</div>
							</div>
						</div>
					{/each}
				{/if}
			</div>
		{:else if activeSection === 'expired'}
			<!-- Desktop view - Expired Balances -->
			<div class="hidden w-full overflow-x-auto md:block">
				<table class="w-full table-auto border-collapse text-sm">
					<thead>
						<tr class="border-b text-left text-xs tracking-wider text-gray-500 uppercase">
							<th class="py-3 font-medium">Account ID</th>
							<th class="py-3 font-medium">Original Balance</th>
							<th class="py-3 font-medium">Expired At</th>
							<th class="py-3 font-medium">Status</th>
							<th class="py-3 text-center font-medium">Actions</th>
						</tr>
					</thead>
					<tbody>
						{#if loadingExpired}
							{#each Array(3) as _, i (i)}
								<tr class="border-b">
									<td class="py-3"><Skeleton class="h-4 w-32" /></td>
									<td class="py-3"><Skeleton class="h-4 w-16" /></td>
									<td class="py-3"><Skeleton class="h-4 w-24" /></td>
									<td class="py-3"><Skeleton class="h-4 w-20" /></td>
									<td class="py-3 text-center"><Skeleton class="mx-auto h-4 w-24" /></td>
								</tr>
							{/each}
						{:else if expiredBalances.length === 0}
							<tr>
								<td colspan="5" class="py-8 text-center text-gray-500">
									No expired balances found.
								</td>
							</tr>
						{:else}
							{#each expiredBalances as eb (eb.id)}
								<tr class="border-b hover:bg-gray-50">
									<td class="py-3 font-mono text-xs text-gray-600">{eb.account_id || 'N/A'}</td>
									<td class="py-3 font-semibold text-primary"
										>{formatCurrency(eb.original_balance)}</td
									>
									<td class="py-3 text-gray-600">{formatDate(eb.expired_at)}</td>
									<td class="py-3">
										{#if eb.claimed_at}
											<span
												class="inline-flex items-center rounded-full border border-green-200 bg-green-50 px-2.5 py-0.5 text-xs font-medium text-green-700"
											>
												Claimed
											</span>
										{:else}
											<span
												class="inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-700"
											>
												Unclaimed
											</span>
										{/if}
									</td>
									<td class="py-3 text-center">
										{#if !eb.claimed_at}
											<Button
												size="sm"
												onclick={() => claimExpiredBalance(eb.id)}
												disabled={claimingId === eb.id}
											>
												{claimingId === eb.id ? 'Claiming...' : 'Mark Claimed'}
											</Button>
										{:else}
											<span class="text-xs text-gray-500">
												{formatDate(eb.claimed_at)}
											</span>
										{/if}
									</td>
								</tr>
							{/each}
						{/if}
					</tbody>
				</table>
			</div>

			<!-- Mobile view - Expired Balances -->
			<div class="grid gap-4 md:hidden">
				{#if loadingExpired}
					{#each Array(2) as _, i (i)}
						<div class="space-y-3 rounded-lg border p-4">
							<Skeleton class="h-4 w-36" />
							<Skeleton class="h-4 w-full" />
							<Skeleton class="h-4 w-24" />
						</div>
					{/each}
				{:else if expiredBalances.length === 0}
					<div class="rounded-lg border border-dashed py-8 text-center text-gray-500">
						No expired balances found.
					</div>
				{:else}
					{#each expiredBalances as eb (eb.id)}
						<div class="space-y-3 rounded-lg border p-4 hover:bg-gray-50">
							<div class="flex items-center justify-between">
								<span class="font-mono text-xs text-gray-600">{eb.account_id || 'N/A'}</span>
								{#if eb.claimed_at}
									<span
										class="inline-flex items-center rounded-full border border-green-200 bg-green-50 px-2.5 py-0.5 text-xs font-medium text-green-700"
									>
										Claimed
									</span>
								{:else}
									<span
										class="inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-700"
									>
										Unclaimed
									</span>
								{/if}
							</div>
							<div class="grid grid-cols-2 gap-2 border-t border-b py-2 text-xs text-gray-600">
								<div>
									<span class="text-gray-400">Balance:</span>
									<span class="ml-1 font-bold text-primary"
										>{formatCurrency(eb.original_balance)}</span
									>
								</div>
								<div>
									<span class="text-gray-400">Expired:</span>
									<span class="ml-1 font-medium text-gray-700">{formatDate(eb.expired_at)}</span>
								</div>
								{#if eb.claimed_at}
									<div class="col-span-2">
										<span class="text-gray-400">Claimed:</span>
										<span class="ml-1 font-medium text-gray-700">{formatDate(eb.claimed_at)}</span>
									</div>
								{/if}
							</div>
							{#if !eb.claimed_at}
								<Button
									size="sm"
									class="w-full"
									onclick={() => claimExpiredBalance(eb.id)}
									disabled={claimingId === eb.id}
								>
									{claimingId === eb.id ? 'Claiming...' : 'Mark as Claimed'}
								</Button>
							{/if}
						</div>
					{/each}
				{/if}
			</div>
		{/if}
	</Card.Content>
</Card.Root>

<!-- Generate Gift Card Modal -->
<Modal.Root bind:open={showGenerateModal}>
	<Modal.Content class="max-w-md">
		<Modal.Header>
			<Modal.Title>Generate Gift Card</Modal.Title>
			<Modal.Description>
				{#if generateStep === 'type'}
					Select how the gift card will be issued and redeemed.
				{:else if generateStep === 'customer'}
					Select the customer for this gift card.
				{:else if generateStep === 'amount_email'}
					Enter the starting amount and delivery details.
				{:else if generateStep === 'payment'}
					Select the payment method.
				{:else if generateStep === 'cash_entry'}
					Enter cash amount received.
				{:else if generateStep === 'card_details'}
					Enter card payment details.
				{/if}
			</Modal.Description>
		</Modal.Header>

		{#if generateStep === 'type'}
			<div class="space-y-3 py-4">
				<p class="text-sm font-medium text-gray-700">How would you like to issue this gift card?</p>

				<button
					type="button"
					class="w-full rounded-lg border border-gray-200 bg-white p-4 text-left transition-colors hover:bg-fuchsia-50/50"
					onclick={() => {
						generateType = 'code';
						selectedCustomer = null;
						isGuestSelected = false;
						generateStep = 'customer';
						generateCustomerTab = 'current';
						fetchCurrentCustomerForGenerate();
					}}
				>
					<div class="font-semibold text-card-foreground">Generate Gift Code</div>
					<div class="mt-1 text-xs text-muted-foreground">
						Creates a 12-character code (not linked to an account at start). Treated as walk-in
						guest payment-wise.
					</div>
				</button>

				<button
					type="button"
					class="w-full rounded-lg border border-gray-200 bg-white p-4 text-left transition-colors hover:bg-fuchsia-50/50"
					onclick={() => {
						generateType = 'account';
						selectedCustomer = null;
						isGuestSelected = false;
						generateStep = 'customer';
						generateCustomerTab = 'current';
						fetchCurrentCustomerForGenerate();
					}}
				>
					<div class="font-semibold text-card-foreground">Add to Customer Account</div>
					<div class="mt-1 text-xs text-muted-foreground">
						Issue directly to a registered customer account balance (members only).
					</div>
				</button>

				<button
					type="button"
					class="w-full rounded-lg border border-amber-200 bg-amber-50/50 p-4 text-left transition-colors hover:bg-amber-50"
					onclick={() => {
						generateType = 'stock';
						selectedCustomer = null;
						isGuestSelected = false;
						generateStep = 'amount_email';
					}}
				>
					<div class="font-semibold text-amber-900">Blank Card for Stock</div>
					<div class="mt-1 text-xs text-amber-700">
						Create a blank card with £0 balance for shop stock — top up when sold.
					</div>
				</button>
			</div>

			<Modal.Footer>
				<Button variant="ghost" onclick={() => (showGenerateModal = false)}>Cancel</Button>
			</Modal.Footer>
		{:else if generateStep === 'customer'}
			<div class="space-y-4 py-4">
				<div class="flex gap-6 border-b border-gray-200 text-sm">
					<button
						type="button"
						class="pb-2 font-medium transition-colors {generateCustomerTab === 'current'
							? 'border-b-2 border-primary font-semibold text-primary'
							: 'text-gray-500 hover:text-gray-700'}"
						onclick={() => (generateCustomerTab = 'current')}
					>
						Current Customer
					</button>
					<button
						type="button"
						class="pb-2 font-medium transition-colors {generateCustomerTab === 'member'
							? 'border-b-2 border-primary font-semibold text-primary'
							: 'text-gray-500 hover:text-gray-700'}"
						onclick={() => (generateCustomerTab = 'member')}
					>
						Member Search
					</button>
					{#if generateType === 'code'}
						<button
							type="button"
							class="pb-2 font-medium transition-colors {generateCustomerTab === 'guest'
								? 'border-b-2 border-primary font-semibold text-primary'
								: 'text-gray-500 hover:text-gray-700'}"
							onclick={() => (generateCustomerTab = 'guest')}
						>
							Guest / Non-Member
						</button>
					{/if}
				</div>

				{#if generateCustomerTab === 'current'}
					<div class="space-y-3">
						{#if loadingCurrentCustomer}
							<div class="space-y-2 p-2">
								<Skeleton class="h-12 w-full" />
							</div>
						{:else if currentCustomerInfo}
							<button
								type="button"
								class="flex w-full cursor-pointer items-center justify-between rounded-lg border p-4 text-left transition-colors {selectedCustomer?.id ===
								currentCustomerInfo.id
									? 'border-input bg-fuchsia-100 font-medium'
									: 'border-gray-200 hover:bg-fuchsia-50/40'}"
								onclick={() => {
									if (currentCustomerInfo) {
										selectedCustomer = {
											id: currentCustomerInfo.id,
											name: currentCustomerInfo.name,
											email: currentCustomerInfo.email,
											previousFirstName: currentCustomerInfo.previousFirstName,
											previousLastName: currentCustomerInfo.previousLastName
										};
										isGuestSelected = false;
									}
								}}
							>
								<div>
									<div class="text-sm font-semibold">
										{formatUserName(
											currentCustomerInfo.name,
											currentCustomerInfo.previousFirstName,
											currentCustomerInfo.previousLastName
										)}
									</div>
									{#if currentCustomerInfo.email}
										<div class="text-xs text-gray-500">{currentCustomerInfo.email}</div>
									{/if}
								</div>
								{#if selectedCustomer?.id === currentCustomerInfo.id}
									<svg class="h-5 w-5 text-primary" fill="currentColor" viewBox="0 0 20 20">
										<path
											fill-rule="evenodd"
											d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
											clip-rule="evenodd"
										/>
									</svg>
								{/if}
							</button>
						{:else}
							<div class="rounded-lg border border-dashed p-6 text-center text-sm text-gray-500">
								No current or next appointment found today.
							</div>
						{/if}
					</div>
				{:else if generateCustomerTab === 'member'}
					<div class="space-y-3">
						<div class="flex gap-2">
							<div class="relative flex-1">
								<input
									type="text"
									class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none"
									placeholder="Search by name, email or phone..."
									bind:value={generateUserQuery}
									onkeydown={(e) => {
										if (e.key === 'Enter') {
											e.preventDefault();
											searchGenerateCustomers();
										}
									}}
								/>
							</div>
							<Button onclick={searchGenerateCustomers} disabled={generateLoadingUsers}>
								{generateLoadingUsers ? '...' : 'Search'}
							</Button>
						</div>

						<div class="max-h-[220px] overflow-y-auto rounded-md border border-gray-200">
							{#if generateUsers.length === 0 && !generateLoadingUsers}
								<div class="flex items-center justify-center p-8 text-xs text-gray-500">
									{generateUserQuery ? 'No members found.' : 'Search for a member above.'}
								</div>
							{:else if generateUsers.length > 0}
								<ul class="divide-y divide-gray-200 {generateLoadingUsers ? 'opacity-60' : ''}">
									{#each generateUsers.slice(0, 5) as user (user.id)}
										<li>
											<button
												type="button"
												class="flex w-full cursor-pointer items-center justify-between px-4 py-3 text-left transition-colors {selectedCustomer?.id ===
												user.id
													? 'bg-fuchsia-100 font-medium'
													: 'hover:bg-fuchsia-50/40'}"
												onclick={() => {
													selectedCustomer = {
														id: user.id,
														name: user.fullName,
														email: user.email,
														previousFirstName: user.previousFirstName,
														previousLastName: user.previousLastName
													};
													isGuestSelected = false;
												}}
											>
												<div>
													<div class="text-sm font-semibold">
														{formatUserName(
															user.fullName,
															user.previousFirstName,
															user.previousLastName
														)}
													</div>
													<div class="text-xs text-gray-500">
														{#if user.email && user.phone}
															{user.email} • {user.phone}
														{:else if user.email}
															{user.email}
														{:else}
															No email info
														{/if}
													</div>
												</div>
												{#if selectedCustomer?.id === user.id}
													<svg class="h-5 w-5 text-primary" fill="currentColor" viewBox="0 0 20 20">
														<path
															fill-rule="evenodd"
															d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
															clip-rule="evenodd"
														/>
													</svg>
												{/if}
											</button>
										</li>
									{/each}
								</ul>
							{/if}
						</div>
					</div>
				{:else if generateCustomerTab === 'guest'}
					<div class="space-y-3">
						<button
							type="button"
							class="flex w-full cursor-pointer items-center justify-between rounded-lg border p-4 text-left transition-colors {isGuestSelected
								? 'border-input bg-fuchsia-100 font-medium'
								: 'border-gray-200 hover:bg-fuchsia-50/40'}"
							onclick={() => {
								selectedCustomer = null;
								isGuestSelected = true;
							}}
						>
							<div>
								<div class="text-sm font-semibold">Walk-in / Call-in Guest</div>
								<div class="text-xs text-gray-500">
									A temporary guest customer. A mandatory email address will be required.
								</div>
							</div>
							{#if isGuestSelected}
								<svg class="h-5 w-5 text-primary" fill="currentColor" viewBox="0 0 20 20">
									<path
										fill-rule="evenodd"
										d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
										clip-rule="evenodd"
									/>
								</svg>
							{/if}
						</button>
					</div>
				{/if}
			</div>

			<Modal.Footer>
				<Button variant="ghost" onclick={() => (generateStep = 'type')}>Back</Button>
				<Button
					disabled={!selectedCustomer && !isGuestSelected}
					onclick={() => (generateStep = 'amount_email')}
				>
					Next
				</Button>
			</Modal.Footer>
		{:else if generateStep === 'amount_email'}
			<div class="space-y-4 py-4">
				{#if generateType === 'stock'}
					<div class="rounded-lg border border-amber-200 bg-amber-50 p-4">
						<p class="text-sm font-medium text-amber-900">£0 — Blank Card for Stock</p>
						<p class="mt-1 text-xs text-amber-700">
							Creates an empty gift card. No delivery details needed.
						</p>
					</div>
				{:else}
					<div class="rounded-lg border bg-gray-50 p-3 text-sm">
						<span class="block text-xs font-semibold tracking-wider text-gray-400 uppercase"
							>Customer</span
						>
						<p class="mt-0.5 font-medium text-gray-900">
							{#if selectedCustomer}
								{formatUserName(
									selectedCustomer.name,
									selectedCustomer.previousFirstName,
									selectedCustomer.previousLastName
								)} (Member)
							{:else if isGuestSelected}
								Walk-in Guest
							{/if}
						</p>
					</div>

					<div class="space-y-2">
						<label for="generate-amount" class="text-sm font-medium">Starting Amount (£)</label>
						<Input
							id="generate-amount"
							type="text"
							inputmode="decimal"
							placeholder="e.g. 50.00"
							bind:value={generateAmount}
						/>
						{#if generateError}
							<span class="text-xs font-medium text-red-500">{generateError}</span>
						{/if}
					</div>

					{#if generateType === 'code'}
						<div class="space-y-2">
							<label for="generate-email" class="text-sm font-medium">
								{#if isGuestSelected}
									Email Address (Required)*
								{:else}
									Recipient Email Address (Optional)
								{/if}
							</label>
							<EmailInput
								id="generate-email"
								bind:value={generateEmail}
								placeholder="e.g. customer@example.com"
							/>
							{#if selectedCustomer?.email && !generateEmail.trim()}
								<p class="text-xs text-gray-500 italic">Defaults to: {selectedCustomer.email}</p>
							{/if}
						</div>
					{/if}
				{/if}
			</div>

			<Modal.Footer>
				<Button
					variant="ghost"
					onclick={() => {
						if (generateType === 'stock') {
							generateStep = 'type';
						} else {
							generateStep = 'customer';
						}
					}}>Back</Button
				>

				{#if generateType === 'stock'}
					<Button onclick={generateInventoryCard} disabled={creating}>
						{creating ? 'Creating...' : 'Create Inventory Card'}
					</Button>
				{:else}
					<Button onclick={goToGeneratePayment} disabled={!isGenerateValid}>
						Continue to Payment
					</Button>
				{/if}
			</Modal.Footer>
		{:else if generateStep === 'payment'}
			<div class="space-y-4 py-4">
				<div class="flex justify-between rounded-lg border bg-gray-50 p-4 text-sm">
					<span class="text-base font-semibold text-gray-700">Total Amount</span>
					<span class="text-xl font-bold text-gray-900"
						>{formatCurrency(Number(generateAmount))}</span
					>
				</div>

				<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
					<button
						type="button"
						class="rounded-lg border border-input py-6 text-center text-sm font-semibold transition-colors hover:bg-fuchsia-50"
						onclick={() => handleEmbeddedCardMachinePayment('create')}
					>
						<svg
							class="mx-auto mb-2 h-8 w-8 text-gray-500"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<rect x="1" y="4" width="22" height="16" rx="2" ry="2" />
							<line x1="1" y1="10" x2="23" y2="10" />
						</svg>
						Card Machine
					</button>
					<button
						type="button"
						class="rounded-lg border border-input py-6 text-center text-sm font-semibold transition-colors hover:bg-fuchsia-50"
						onclick={() => (generateStep = 'cash_entry')}
					>
						<svg
							class="mx-auto mb-2 h-8 w-8 text-gray-500"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<line x1="12" y1="1" x2="12" y2="23" />
							<path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
						</svg>
						Cash
					</button>
					<button
						type="button"
						class="rounded-lg border border-input py-6 text-center text-sm font-semibold transition-colors hover:bg-fuchsia-50 sm:col-span-2"
						onclick={() => (generateStep = 'card_details')}
					>
						<svg
							class="mx-auto mb-2 h-8 w-8 text-gray-500"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<rect x="1" y="4" width="22" height="16" rx="2" ry="2" />
							<line x1="1" y1="10" x2="23" y2="10" />
							<path d="M1 14h22" />
							<circle cx="7" cy="18" r="1.5" />
						</svg>
						Card Details
					</button>
				</div>
			</div>
			<Modal.Footer>
				<Button variant="ghost" onclick={() => (generateStep = 'amount_email')}>Back</Button>
			</Modal.Footer>
		{:else if generateStep === 'cash_entry'}
			<div class="space-y-4 py-4">
				<div class="flex justify-between rounded-lg border bg-gray-50 p-4 text-sm">
					<span class="text-base font-semibold text-gray-700">Total Amount</span>
					<span class="text-xl font-bold text-gray-900"
						>{formatCurrency(Number(generateAmount))}</span
					>
				</div>

				<div class="space-y-2">
					<label for="generate-cash-tendered" class="text-sm font-medium">Cash Received (£)</label>
					<Input
						id="generate-cash-tendered"
						type="text"
						inputmode="numeric"
						placeholder="e.g. 50.00"
						value={cashAmount}
						oninput={(e) => (cashAmount = e.currentTarget.value)}
					/>
				</div>

				{#if Number(cashAmount) > Number(generateAmount)}
					<div class="rounded-md bg-green-50 p-3 text-xs text-green-800">
						Change due: <span class="font-bold"
							>{formatCurrency(Number(cashAmount) - Number(generateAmount))}</span
						>
					</div>
				{/if}
			</div>
			<Modal.Footer>
				<Button variant="ghost" onclick={() => (generateStep = 'payment')}>Back</Button>
				<Button
					disabled={isNaN(Number(cashAmount)) || Number(cashAmount) < Number(generateAmount)}
					onclick={() => handleEmbeddedCashPayment('create')}
				>
					Confirm Cash
				</Button>
			</Modal.Footer>
		{:else if generateStep === 'card_details'}
			<div class="space-y-4 py-4">
				<div class="space-y-3 rounded-lg border border-gray-100 bg-gray-50 p-4">
					<h4 class="text-sm font-medium text-gray-700">Online Card Processing</h4>
					<div class="space-y-3">
						<div>
							<label for="generate-card-number" class="text-sm font-medium text-gray-700"
								>Card Number</label
							>
							<Input
								id="generate-card-number"
								type="text"
								inputmode="numeric"
								value={ephemeralCardNumber}
								oninput={handleEphemeralCardNumberInput}
								placeholder="1234 5678 9012 3456"
								maxlength={19}
								class="mt-1"
							/>
						</div>
						<div class="grid grid-cols-2 gap-3">
							<div>
								<label for="generate-card-expiry" class="text-sm font-medium text-gray-700"
									>Expiry (MM/YY)</label
								>
								<Input
									id="generate-card-expiry"
									type="text"
									inputmode="numeric"
									value={ephemeralCardExpiry}
									oninput={handleEphemeralExpiryInput}
									placeholder="MM/YY"
									maxlength={5}
									class="mt-1"
								/>
							</div>
							<div>
								<label for="generate-card-cvc" class="text-sm font-medium text-gray-700">CVC</label>
								<Input
									id="generate-card-cvc"
									type="text"
									inputmode="numeric"
									value={ephemeralCardCVC}
									oninput={handleEphemeralCvcInput}
									placeholder="123"
									maxlength={4}
									class="mt-1"
								/>
							</div>
						</div>
					</div>
				</div>
			</div>
			<Modal.Footer>
				<Button variant="ghost" onclick={() => (generateStep = 'payment')}>Back</Button>
				<Button
					disabled={!isEphemeralCardValid}
					onclick={() => handleEmbeddedEphemeralCardPayment('create')}
				>
					Pay {formatCurrency(Number(generateAmount))}
				</Button>
			</Modal.Footer>
		{:else if generateStep === 'processing'}
			<div class="flex flex-col items-center justify-center py-8">
				<div
					class="mb-4 h-12 w-12 animate-spin rounded-full border-4 border-gray-200 border-t-primary"
				></div>
				<p class="text-sm text-gray-600">{processingMessage}</p>
			</div>
		{:else if generateStep === 'success'}
			<div class="flex flex-col items-center justify-center space-y-4 py-8 text-center">
				<div
					class="flex h-16 w-16 items-center justify-center rounded-full border border-green-200 bg-green-50"
				>
					<svg
						class="h-8 w-8 text-green-600"
						fill="none"
						viewBox="0 0 24 24"
						stroke="currentColor"
						stroke-width="2"
					>
						<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
					</svg>
				</div>
				<div>
					<h3 class="text-lg font-bold text-gray-900">Payment Successful</h3>
					{#if generateType === 'code'}
						<p class="mt-1 text-xs text-gray-500">A copy of the code has been emailed.</p>
						<div
							class="mt-4 rounded-md border border-fuchsia-100 bg-fuchsia-50 p-4 font-mono text-xl font-bold tracking-widest text-fuchsia-900 shadow-sm"
						>
							{formatCardCode(paymentResult?.giftcard_code || paymentResult?.item_id || '')}
						</div>
						<p class="mt-2 text-xs text-gray-400">
							Write down or print this code for the customer.
						</p>
					{:else if generateType === 'account'}
						<p class="mt-2 text-sm text-gray-600">
							✓ Gift card added to {selectedCustomer?.name || 'account'}
						</p>
						<p class="mt-1 text-xs text-gray-400">Balance is available immediately.</p>
					{/if}
				</div>
				<Button
					onclick={() => {
						showGenerateModal = false;
						resetGenerateModal();
					}}
					class="mt-4 w-full">Done</Button
				>
			</div>
		{:else if generateStep === 'error'}
			<div class="space-y-4 py-4">
				<div class="rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-800">
					<p class="font-semibold">Payment Failed</p>
					<p class="mt-1 text-xs">{paymentError || 'An error occurred during transaction.'}</p>
				</div>
				<div class="flex gap-2">
					<Button variant="ghost" onclick={() => (generateStep = 'payment')} class="flex-1"
						>Try Again</Button
					>
					<Button
						variant="outline"
						onclick={() => {
							showGenerateModal = false;
							resetGenerateModal();
						}}
						class="flex-1">Close</Button
					>
				</div>
			</div>
		{/if}
	</Modal.Content>
</Modal.Root>

<!-- Top Up Modal -->
<Modal.Root bind:open={showTopUpModal}>
	<Modal.Content class="max-w-md">
		<Modal.Header>
			<Modal.Title>Top Up Gift Card</Modal.Title>
			<Modal.Description>
				{#if topUpStep === 'choice'}
					Select how you would like to add funds to this card.
				{:else if topUpStep === 'amount'}
					Enter the amount to add to this card.
				{:else if topUpStep === 'payment'}
					Select the payment method.
				{:else if topUpStep === 'cash_entry'}
					Enter cash amount received.
				{:else if topUpStep === 'card_details'}
					Enter card payment details.
				{/if}
			</Modal.Description>
		</Modal.Header>

		{#if topUpStep === 'choice'}
			<div class="space-y-3 py-4">
				<p class="text-sm text-gray-600">How would you like to add funds?</p>
				<button
					type="button"
					class="w-full rounded-lg border border-gray-200 bg-white p-4 text-left transition-colors hover:bg-fuchsia-50/50"
					onclick={() => {
						topUpMode = 'giveaway';
						topUpStep = 'amount';
					}}
				>
					<div class="font-semibold text-card-foreground">Giveaway (On the House)</div>
					<div class="mt-1 text-sm text-muted-foreground">Free top-up, no payment needed</div>
				</button>
				<button
					type="button"
					class="w-full rounded-lg border border-gray-200 bg-white p-4 text-left transition-colors hover:bg-fuchsia-50/50"
					onclick={() => {
						topUpMode = 'purchase';
						topUpStep = 'amount';
					}}
				>
					<div class="font-semibold text-card-foreground">Customer Purchase</div>
					<div class="mt-1 text-sm text-muted-foreground">
						Collect payment via cash or card machine
					</div>
				</button>
			</div>
			<Modal.Footer>
				<Button variant="ghost" onclick={() => (showTopUpModal = false)}>Cancel</Button>
			</Modal.Footer>
		{:else if topUpStep === 'amount'}
			<div class="space-y-4 py-4">
				<div class="space-y-2">
					<label for="topup-amount" class="text-sm font-medium">Amount to Add (£)</label>
					<Input
						id="topup-amount"
						type="text"
						inputmode="decimal"
						placeholder="e.g. 20.00"
						bind:value={topUpAmount}
					/>
					{#if topUpError}
						<span class="text-xs font-medium text-red-500">{topUpError}</span>
					{/if}
				</div>
			</div>

			<Modal.Footer>
				<Button variant="ghost" onclick={() => (topUpStep = 'choice')}>Back</Button>
				<Button onclick={goToTopUpPayment} disabled={!isTopUpValid}>
					{#if topUpMode === 'giveaway'}
						Add Funds
					{:else}
						Continue to Payment
					{/if}
				</Button>
			</Modal.Footer>
		{:else if topUpStep === 'payment'}
			<div class="space-y-4 py-4">
				<div class="flex justify-between rounded-lg border bg-gray-50 p-4 text-sm">
					<span class="text-base font-semibold text-gray-700">Total Top Up</span>
					<span class="text-xl font-bold text-gray-900">{formatCurrency(Number(topUpAmount))}</span>
				</div>

				<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
					<button
						type="button"
						class="rounded-lg border border-input py-6 text-center text-sm font-semibold transition-colors hover:bg-fuchsia-50"
						onclick={() => handleEmbeddedCardMachinePayment('topup', selectedCardId ?? undefined)}
					>
						<svg
							class="mx-auto mb-2 h-8 w-8 text-gray-500"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<rect x="1" y="4" width="22" height="16" rx="2" ry="2" />
							<line x1="1" y1="10" x2="23" y2="10" />
						</svg>
						Card Machine
					</button>
					<button
						type="button"
						class="rounded-lg border border-input py-6 text-center text-sm font-semibold transition-colors hover:bg-fuchsia-50"
						onclick={() => (topUpStep = 'cash_entry')}
					>
						<svg
							class="mx-auto mb-2 h-8 w-8 text-gray-500"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<line x1="12" y1="1" x2="12" y2="23" />
							<path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
						</svg>
						Cash
					</button>
					<button
						type="button"
						class="rounded-lg border border-input py-6 text-center text-sm font-semibold transition-colors hover:bg-fuchsia-50 sm:col-span-2"
						onclick={() => (topUpStep = 'card_details')}
					>
						<svg
							class="mx-auto mb-2 h-8 w-8 text-gray-500"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<rect x="1" y="4" width="22" height="16" rx="2" ry="2" />
							<line x1="1" y1="10" x2="23" y2="10" />
							<path d="M1 14h22" />
							<circle cx="7" cy="18" r="1.5" />
						</svg>
						Card Details
					</button>
				</div>
			</div>
			<Modal.Footer>
				<Button variant="ghost" onclick={() => (topUpStep = 'amount')}>Back</Button>
			</Modal.Footer>
		{:else if topUpStep === 'cash_entry'}
			<div class="space-y-4 py-4">
				<div class="flex justify-between rounded-lg border bg-gray-50 p-4 text-sm">
					<span class="text-base font-semibold text-gray-700">Total Top Up</span>
					<span class="text-xl font-bold text-gray-900">{formatCurrency(Number(topUpAmount))}</span>
				</div>

				<div class="space-y-2">
					<label for="topup-cash-tendered" class="text-sm font-medium">Cash Received (£)</label>
					<Input
						id="topup-cash-tendered"
						type="text"
						inputmode="numeric"
						placeholder="e.g. 50.00"
						value={cashAmount}
						oninput={(e) => (cashAmount = e.currentTarget.value)}
					/>
				</div>

				{#if Number(cashAmount) > Number(topUpAmount)}
					<div class="rounded-md bg-green-50 p-3 text-xs text-green-800">
						Change due: <span class="font-bold"
							>{formatCurrency(Number(cashAmount) - Number(topUpAmount))}</span
						>
					</div>
				{/if}
			</div>
			<Modal.Footer>
				<Button variant="ghost" onclick={() => (topUpStep = 'payment')}>Back</Button>
				<Button
					disabled={isNaN(Number(cashAmount)) || Number(cashAmount) < Number(topUpAmount)}
					onclick={() => handleEmbeddedCashPayment('topup', selectedCardId ?? undefined)}
				>
					Confirm Cash
				</Button>
			</Modal.Footer>
		{:else if topUpStep === 'card_details'}
			<div class="space-y-4 py-4">
				<div class="space-y-3 rounded-lg border border-gray-100 bg-gray-50 p-4">
					<h4 class="text-sm font-medium text-gray-700">Online Card Processing</h4>
					<div class="space-y-3">
						<div>
							<label for="topup-card-number" class="text-sm font-medium text-gray-700"
								>Card Number</label
							>
							<Input
								id="topup-card-number"
								type="text"
								inputmode="numeric"
								value={ephemeralCardNumber}
								oninput={handleEphemeralCardNumberInput}
								placeholder="1234 5678 9012 3456"
								maxlength={19}
								class="mt-1"
							/>
						</div>
						<div class="grid grid-cols-2 gap-3">
							<div>
								<label for="topup-card-expiry" class="text-sm font-medium text-gray-700"
									>Expiry (MM/YY)</label
								>
								<Input
									id="topup-card-expiry"
									type="text"
									inputmode="numeric"
									value={ephemeralCardExpiry}
									oninput={handleEphemeralExpiryInput}
									placeholder="MM/YY"
									maxlength={5}
									class="mt-1"
								/>
							</div>
							<div>
								<label for="topup-card-cvc" class="text-sm font-medium text-gray-700">CVC</label>
								<Input
									id="topup-card-cvc"
									type="text"
									inputmode="numeric"
									value={ephemeralCardCVC}
									oninput={handleEphemeralCvcInput}
									placeholder="123"
									maxlength={4}
									class="mt-1"
								/>
							</div>
						</div>
					</div>
				</div>
			</div>
			<Modal.Footer>
				<Button variant="ghost" onclick={() => (topUpStep = 'payment')}>Back</Button>
				<Button
					disabled={!isEphemeralCardValid}
					onclick={() => handleEmbeddedEphemeralCardPayment('topup', selectedCardId ?? undefined)}
				>
					Pay {formatCurrency(Number(topUpAmount))}
				</Button>
			</Modal.Footer>
		{:else if topUpStep === 'processing'}
			<div class="flex flex-col items-center justify-center py-8">
				<div
					class="mb-4 h-12 w-12 animate-spin rounded-full border-4 border-gray-200 border-t-primary"
				></div>
				<p class="text-sm text-gray-600">{processingMessage}</p>
			</div>
		{:else if topUpStep === 'success'}
			<div class="flex flex-col items-center justify-center space-y-4 py-8 text-center">
				<div
					class="flex h-16 w-16 items-center justify-center rounded-full border border-green-200 bg-green-50"
				>
					<svg
						class="h-8 w-8 text-green-600"
						fill="none"
						viewBox="0 0 24 24"
						stroke="currentColor"
						stroke-width="2"
					>
						<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
					</svg>
				</div>
				<div>
					<h3 class="text-lg font-bold text-gray-900">Top Up Successful</h3>
					<p class="mt-2 text-sm text-gray-600">✓ Gift card topped up successfully!</p>
					<p class="mt-1 text-xs text-gray-400">New balance is available immediately.</p>
				</div>
				<Button
					onclick={() => {
						showTopUpModal = false;
						resetTopUpModal();
					}}
					class="mt-4 w-full">Done</Button
				>
			</div>
		{:else if topUpStep === 'error'}
			<div class="space-y-4 py-4">
				<div class="rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-800">
					<p class="font-semibold">Top Up Failed</p>
					<p class="mt-1 text-xs">{paymentError || 'An error occurred during transaction.'}</p>
				</div>
				<div class="flex gap-2">
					<Button variant="ghost" onclick={() => (topUpStep = 'payment')} class="flex-1"
						>Try Again</Button
					>
					<Button
						variant="outline"
						onclick={() => {
							showTopUpModal = false;
							resetTopUpModal();
						}}
						class="flex-1">Close</Button
					>
				</div>
			</div>
		{/if}
	</Modal.Content>
</Modal.Root>

<!-- Transfer Modal -->
<Modal.Root bind:open={showTransferModal}>
	<Modal.Content class="max-w-md">
		<Modal.Header>
			<Modal.Title>Transfer Balance</Modal.Title>
			<Modal.Description
				>Transfer funds from {formatCardCode(selectedCardId ?? '')} directly to another card.</Modal.Description
			>
		</Modal.Header>

		<div class="space-y-4 py-4">
			<div class="space-y-2">
				<label for="transfer-code" class="text-sm font-medium">Destination Card Code</label>
				<Input
					id="transfer-code"
					type="text"
					placeholder="xxxx-xxxx-xxxx"
					maxlength={14}
					value={transferToCode}
					oninput={handleCodeInput}
				/>
				{#if transferCodeError}
					<span class="text-xs font-medium text-red-500">{transferCodeError}</span>
				{/if}
			</div>
			<div class="space-y-2">
				<label for="transfer-amount" class="text-sm font-medium">Amount to Transfer (£)</label>
				<Input
					id="transfer-amount"
					type="text"
					inputmode="decimal"
					placeholder="e.g. 10.00"
					bind:value={transferAmount}
				/>
				{#if transferAmountError}
					<span class="text-xs font-medium text-red-500">{transferAmountError}</span>
				{/if}
			</div>
		</div>

		<Modal.Footer>
			<Button variant="outline" onclick={() => (showTransferModal = false)}>Cancel</Button>
			<Button onclick={transferCard} disabled={transferring || !isTransferValid}>
				{transferring ? 'Transferring...' : 'Transfer Balance'}
			</Button>
		</Modal.Footer>
	</Modal.Content>
</Modal.Root>
