<script lang="ts">
	import { toast } from 'svelte-sonner';
	import { onMount } from 'svelte';
	import * as Dialog from '$lib/components/ui/dialog';
	import { Button } from '$lib/components/ui/button';
	import { Input } from '$lib/components/ui/input';
	import { apiFetch } from '$lib/utils/api';
	import { formatCardCode } from '$lib/utils/format';
	import { formatUserName } from '$lib/utils/nameDisplay';

	interface Props {
		amount: number;
		itemType: string;
		action: 'create' | 'topup';
		giftCardId?: string;
		userId?: string;
		isGuestMode?: boolean;
		guestEmail?: string;
		delivery?: 'account' | 'code';
		onClose: () => void;
		onComplete: (result: TillSaleResult) => void;
	}

	const {
		amount,
		itemType,
		action,
		giftCardId = undefined,
		userId = undefined,
		isGuestMode = false,
		guestEmail = undefined,
		delivery: deliveryProp = undefined,
		onClose,
		onComplete
	}: Props = $props();

	type TillSaleResult = {
		id: string;
		item_id?: string;
		total_amount: number;
		payment_method: string;
		status: string;
		user?: { id: string; name: string };
		giftcard_code?: string;
	};

	type PaymentStep =
		| 'customer-selection'
		| 'payment-selection'
		| 'cash-entering'
		| 'cash-confirming'
		| 'card-machine'
		| 'card-polling'
		| 'card-details-entering'
		| 'saved-card-selecting'
		| 'success'
		| 'error';

	let step = $state<PaymentStep>('customer-selection');
	let error = $state<string | null>(null);
	let result = $state<TillSaleResult | null>(null);

	// Idempotency key — generated once per modal open, reused on retry
	let idempotencyKey = $state<string>('');

	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;
	}

	// Selected customer info
	let selectedCustomer = $state<{
		id: string;
		name: string;
		previousFirstName?: string | null;
		previousLastName?: string | null;
	} | null>(null);
	let isGuest = $state(false);

	// Delivery choice — how the gift card value is given to the customer
	// 'code' = generate physical gift code (members & guests)
	// 'account' = add to user's account balance (members only)
	let delivery = $state<'account' | 'code'>('code');

	// Cash
	let cashAmount = $state('');
	const cashAmountNum = $derived(Number(cashAmount) || 0);
	const changeDue = $derived(Math.max(0, cashAmountNum - amount));
	const cashEntryComplete = $derived(cashAmountNum >= amount);
	// Remember cash received for success screen display
	let cashTendered = $state(0);

	// Card Machine
	let _checkoutId = $state<string | null>(null);
	// Preserve the item_id from the card machine initial response (polling overwrites result)
	let cardMachineItemID = $state<string | null>(null);

	// Customer Selection - Current Customer
	let loadingCurrentCustomer = $state(false);
	let currentCustomerInfo = $state<{
		id: string;
		name: string;
		email?: string;
		phone?: string;
		previousFirstName?: string | null;
		previousLastName?: string | null;
	} | null>(null);

	// Customer Selection - tab
	let customerTab = $state<'current' | 'member' | 'guest'>('current');

	// Customer Selection - Search
	let userQuery = $state('');
	let users = $state<
		Array<{
			id: string;
			fullName: string;
			email?: string;
			phone?: string;
			previousFirstName?: string | null;
			previousLastName?: string | null;
		}>
	>([]);
	let loadingUsers = $state(false);
	let currentPage = $state(1);
	let totalPages = $state(1);
	let searchTotal = $state(0);

	// Customer Selection - Guest / Walk-in
	let creatingGuest = $state(false);

	// Saved Cards (member users)
	let savedCardList = $state<
		Array<{
			id: string;
			card_brand: string;
			card_last4: string;
			card_expiry?: string;
			cardholder_name?: string;
		}>
	>([]);
	let loadingSavedCards = $state(false);
	let selectedSavedCardId = $state<string | null>(null);
	let sendingSavedCard = $state(false);

	// Ephemeral Card Details (guest only, not saved)
	let ephemeralCardNumber = $state('');
	let ephemeralCardExpiry = $state('');
	let ephemeralCardCVC = $state('');
	let sendingEphemeralCard = $state(false);

	// =============== Card Utilities ===============

	function isValidLuhn(cardNumber: string): boolean {
		const s = cardNumber.replace(/\D/g, '');
		let sum = 0;
		let alternate = false;
		for (let i = s.length - 1; i >= 0; i--) {
			let n = parseInt(s[i], 10);
			if (alternate) {
				n *= 2;
				if (n > 9) n -= 9;
			}
			sum += n;
			alternate = !alternate;
		}
		return sum % 10 === 0 && s.length >= 13 && s.length <= 19;
	}

	function formatCardNumber(value: string): string {
		const digits = value.replace(/\D/g, '').substring(0, 16);
		const groups = digits.match(/.{1,4}/g);
		return groups ? groups.join(' ') : digits;
	}

	function formatExpiryDate(value: string): string {
		const digits = value.replace(/\D/g, '').substring(0, 4);
		if (digits.length >= 3) {
			return digits.substring(0, 2) + '/' + digits.substring(2);
		}
		return digits;
	}

	function handleEphemeralCardNumberInput(e: Event) {
		const input = e.target as HTMLInputElement;
		const start = input.selectionStart ?? 0;
		const beforeLen = ephemeralCardNumber.length;
		ephemeralCardNumber = formatCardNumber(input.value);
		const afterLen = ephemeralCardNumber.length;
		requestAnimationFrame(() =>
			input.setSelectionRange(start + (afterLen - beforeLen), start + (afterLen - beforeLen))
		);
	}

	function handleEphemeralExpiryInput(e: Event) {
		const input = e.target as HTMLInputElement;
		const start = input.selectionStart ?? 0;
		const beforeLen = ephemeralCardExpiry.length;
		ephemeralCardExpiry = formatExpiryDate(input.value);
		const afterLen = ephemeralCardExpiry.length;
		requestAnimationFrame(() =>
			input.setSelectionRange(start + (afterLen - beforeLen), start + (afterLen - beforeLen))
		);
	}

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

	const isEphemeralCardValid = $derived(
		isValidLuhn(ephemeralCardNumber) &&
			/^\d{2}\/\d{2}$/.test(ephemeralCardExpiry) &&
			ephemeralCardCVC.length >= 3
	);

	const ephemeralCardError = $derived(
		ephemeralCardNumber.length > 0 && !isValidLuhn(ephemeralCardNumber)
			? 'Invalid card number'
			: /^\d{2}\/\d{2}$/.test(ephemeralCardExpiry) &&
				  (() => {
						const parts = parseEpiry(ephemeralCardExpiry);
						return parts && (parts.month < 1 || parts.month > 12);
				  })()
				? 'Invalid expiry month'
				: ephemeralCardCVC.length > 0 && ephemeralCardCVC.length < 3
					? 'CVC must be at least 3 digits'
					: null
	);

	function parseEpiry(value: string): { month: number; year: number } | null {
		if (!/^\d{2}\/\d{2}$/.test(value)) return null;
		const [monthStr, yearStr] = value.split('/');
		const month = parseInt(monthStr, 10);
		const year = 2000 + parseInt(yearStr, 10);
		if (month < 1 || month > 12) return null;
		return { month, year };
	}

	// =============== Formatting ===============

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

	function resetView() {
		idempotencyKey = '';
		step = 'customer-selection';
		error = null;
		cashAmount = '';
		_checkoutId = null;
		cardMachineItemID = null;
		selectedCustomer = null;
		isGuest = false;
		delivery = 'code';
		userQuery = '';
		users = [];
		currentCustomerInfo = null;
		customerTab = 'current';
		currentPage = 1;
		totalPages = 1;
		searchTotal = 0;
		ephemeralCardNumber = '';
		ephemeralCardExpiry = '';
		ephemeralCardCVC = '';
		savedCardList = [];
		loadingSavedCards = false;
		selectedSavedCardId = null;
		sendingSavedCard = false;
		cashTendered = 0;
	}

	// Auto-fetch current customer on mount
	onMount(() => {
		if (deliveryProp) delivery = deliveryProp;
		fetchCurrentCustomer();
		if (userId) {
			selectedCustomer = { id: userId, name: '' };
			isGuest = false;
			delivery = deliveryProp ?? 'account';
			step = 'payment-selection';
		} else if (isGuestMode) {
			createGuestCustomer();
		}
	});

	// Auto-fetch saved cards when entering payment-selection for members
	$effect(() => {
		if (step === 'payment-selection' && !isGuest && selectedCustomer) {
			fetchSavedCards();
		}
	});

	// =============== Customer Selection ===============

	async function fetchCurrentCustomer() {
		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
					};
				} else {
					toast.error('No current or next appointment found');
				}
			} else {
				toast.error('Failed to load current appointment');
			}
		} catch {
			toast.error('Network error loading appointment');
		} finally {
			loadingCurrentCustomer = false;
		}
	}

	function selectCurrentCustomer() {
		if (!currentCustomerInfo) return;
		selectedCustomer = {
			id: currentCustomerInfo.id,
			name: currentCustomerInfo.name,
			previousFirstName: currentCustomerInfo.previousFirstName,
			previousLastName: currentCustomerInfo.previousLastName
		};
		isGuest = currentCustomerInfo.email?.endsWith('@guest.invalid') || false;
		delivery = action === 'topup' || isGuest ? 'code' : 'account';
		step = 'payment-selection';
	}

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

	function goToPage(p: number) {
		if (p < 1 || p > totalPages) return;
		searchCustomers(p);
	}

	async function createGuestCustomer() {
		creatingGuest = true;
		try {
			const timestamp = Date.now();
			const firstName = 'Walk-in';
			const lastName = 'Guest';
			const email = guestEmail || `till-${timestamp}@guest.invalid`;
			const phone = '07451234567';

			const res = await apiFetch('/api/users/guest', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					firstName,
					lastName,
					phone,
					email
				})
			});
			if (res.ok) {
				const data = await res.json();
				selectedCustomer = { id: data.id, name: 'Walk-in Guest' };
				isGuest = true;
				step = 'payment-selection';
			} else {
				const errText = await res.text();
				toast.error(errText || 'Failed to create guest user');
			}
		} catch {
			toast.error('Network error creating guest user');
		} finally {
			creatingGuest = false;
		}
	}

	function selectCustomer(user: {
		id: string;
		fullName: string;
		email?: string;
		previousFirstName?: string | null;
		previousLastName?: string | null;
	}) {
		selectedCustomer = {
			id: user.id,
			name: user.fullName,
			previousFirstName: user.previousFirstName,
			previousLastName: user.previousLastName
		};
		isGuest = user.email?.endsWith('@guest.invalid') || false;
		delivery = action === 'topup' || isGuest ? 'code' : 'account';
		step = 'payment-selection';
	}

	// =============== Payment ===============

	async function handleCashPayment() {
		if (!cashEntryComplete) return;
		step = 'cash-confirming';
		cashTendered = cashAmountNum;
		try {
			const body: Record<string, unknown> = {
				item_type: itemType,
				action: action,
				amount: amount,
				payment_method: 'cash',
				idempotency_key: getIdempotencyKey()
			};
			if (giftCardId) body.gift_card_id = giftCardId;
			if (selectedCustomer) body.user_id = selectedCustomer.id;
			if (delivery === '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();
				result = { ...data, user: selectedCustomer ?? undefined };
				step = 'success';
			} else {
				error = await res.text();
				step = 'error';
			}
		} catch (_err) {
			error = 'Network error processing cash payment';
			step = 'error';
		}
	}

	async function handleCardMachinePayment() {
		step = 'card-machine';
		try {
			const body: Record<string, unknown> = {
				item_type: itemType,
				action: action,
				amount: amount,
				payment_method: 'card_machine',
				idempotency_key: getIdempotencyKey()
			};
			if (giftCardId) body.gift_card_id = giftCardId;
			if (selectedCustomer) body.user_id = selectedCustomer.id;
			if (delivery === '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;
					step = 'card-polling';
					pollCheckout(data.checkout_id);
				} else {
					result = { ...data, user: selectedCustomer ?? undefined };
					step = 'success';
				}
			} else {
				error = await res.text();
				step = 'error';
			}
		} catch (_err) {
			error = 'Network error initiating card machine payment';
			step = 'error';
		}
	}

	async function pollCheckout(ckId: string) {
		const maxAttempts = 60;
		let attempts = 0;
		const poll = async () => {
			while (attempts < maxAttempts) {
				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') {
							result = {
								id: data.payment_id || ckId,
								item_id: cardMachineItemID ?? undefined,
								giftcard_code: data.giftcard_code || undefined,
								total_amount: amount,
								payment_method: 'card_machine',
								status: 'completed',
								user: selectedCustomer ?? undefined
							};
							step = 'success';
							return;
						}
					}
				} catch {
					// Continue polling
				}
			}
			error = 'Card machine payment timed out. Please check Square dashboard.';
			step = 'error';
		};
		poll();
	}

	async function handleEphemeralCardPayment() {
		if (!isEphemeralCardValid) return;
		sendingEphemeralCard = true;
		try {
			const cardNum = ephemeralCardNumber.replace(/\s/g, '');
			const [monthStr, yearStr] = ephemeralCardExpiry.split('/');
			const expMonth = parseInt(monthStr, 10);
			const expYear = 2000 + parseInt(yearStr, 10);

			const body: Record<string, unknown> = {
				item_type: itemType,
				action: action,
				amount: amount,
				payment_method: 'online_square',
				idempotency_key: getIdempotencyKey(),
				card_number: cardNum,
				card_exp_month: expMonth,
				card_exp_year: expYear,
				card_cvc: ephemeralCardCVC
			};
			if (giftCardId) body.gift_card_id = giftCardId;
			if (selectedCustomer) body.user_id = selectedCustomer.id;
			if (delivery === '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();
				result = { ...data, user: selectedCustomer ?? undefined };
				step = 'success';
			} else {
				error = await res.text();
				step = 'error';
			}
		} catch (_err) {
			error = 'Network error processing card payment';
			step = 'error';
		} finally {
			sendingEphemeralCard = false;
		}
	}

	// =============== Saved Cards (members only) ===============

	async function fetchSavedCards() {
		if (!selectedCustomer) return;
		loadingSavedCards = true;
		savedCardList = [];
		try {
			const res = await apiFetch(`/api/admin/users/${selectedCustomer.id}/payment-methods`);
			if (res.ok) {
				const data = await res.json();
				savedCardList = data.payment_methods || data || [];
			}
		} catch {
			toast.error('Failed to load saved cards');
		} finally {
			loadingSavedCards = false;
		}
	}

	async function handleSavedCardPayment() {
		if (!selectedSavedCardId) return;
		sendingSavedCard = true;
		try {
			const body: Record<string, unknown> = {
				item_type: itemType,
				action: action,
				amount: amount,
				payment_method: 'saved_card',
				idempotency_key: getIdempotencyKey(),
				user_saved_card_id: selectedSavedCardId
			};
			if (giftCardId) body.gift_card_id = giftCardId;
			if (selectedCustomer) body.user_id = selectedCustomer.id;
			if (delivery === '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();
				result = { ...data, user: selectedCustomer ?? undefined };
				step = 'success';
			} else {
				error = await res.text();
				step = 'error';
			}
		} catch {
			error = 'Network error processing saved card payment';
			step = 'error';
		} finally {
			sendingSavedCard = false;
		}
	}

	function handleDone() {
		if (result) onComplete(result);
		onClose();
	}
</script>

<Dialog.Root
	open={true}
	onOpenChange={(open) => {
		if (!open) onClose();
	}}
>
	<Dialog.Content class="max-h-[85vh] max-w-md overflow-y-auto">
		{#if step === 'success' && result}
			<Dialog.Header>
				<Dialog.Title>Payment Successful</Dialog.Title>
				<Dialog.Description>
					{action === 'create' ? 'Gift card created' : 'Gift card topped up'} and payment recorded.
				</Dialog.Description>
			</Dialog.Header>
			<div class="space-y-4 py-4">
				<div class="flex flex-col items-center justify-center py-4">
					<div class="mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-100">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							class="h-8 w-8 text-green-600"
							viewBox="0 0 20 20"
							fill="currentColor"
						>
							<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>
					</div>
					<h3 class="text-xl font-semibold text-gray-900">Payment Complete</h3>
					<p class="mt-1 text-sm text-gray-500">
						{formatCurrency(result.total_amount)} via {result.payment_method}
					</p>
					{#if result.payment_method === 'cash' && cashTendered > 0}
						<div class="mt-3 w-full rounded-md border border-green-200 bg-green-50 p-3">
							<div class="flex justify-between text-sm">
								<span class="font-medium text-green-800">Cash Received</span>
								<span class="font-semibold text-green-800">{formatCurrency(cashTendered)}</span>
							</div>
							<div class="mt-1 flex justify-between text-sm">
								<span class="font-medium text-green-800">Change Due</span>
								<span class="font-semibold text-green-800"
									>{formatCurrency(cashTendered - result.total_amount)}</span
								>
							</div>
						</div>
					{/if}
					{#if action === 'create' && delivery === 'code'}
						<div
							class="mt-3 w-full rounded-md border border-fuchsia-100 bg-fuchsia-50 p-3 text-center"
						>
							<p class="text-xs font-medium text-fuchsia-700">Gift Card Code</p>
							<p class="mt-1 font-mono text-lg font-bold tracking-wider text-fuchsia-900">
								{formatCardCode(result.giftcard_code || result.item_id || '')}
							</p>
							<p class="mt-1 text-xs text-fuchsia-500">Show this code to the customer</p>
						</div>
					{/if}
					{#if action === 'create' && delivery === 'account'}
						<div class="mt-3 w-full rounded-md border border-blue-100 bg-blue-50 p-3 text-center">
							<p class="text-sm font-medium text-blue-800">
								✓ Gift card added to {selectedCustomer?.name || 'account'}
							</p>
							<p class="mt-1 text-xs text-blue-600">Balance is available immediately</p>
						</div>
						<!-- TODO: Send email notification to customer -->
					{/if}
				</div>
				<Button onclick={handleDone} class="w-full">Done</Button>
			</div>
		{:else if step === 'error'}
			<Dialog.Header>
				<Dialog.Title>Payment Failed</Dialog.Title>
			</Dialog.Header>
			<div class="space-y-4 py-4">
				<div class="rounded-md border border-red-200 bg-red-50 p-3">
					<p class="text-sm text-red-800">{error || 'An error occurred'}</p>
				</div>
				<div class="flex gap-3">
					<Button variant="ghost" onclick={onClose} class="flex-1">Cancel</Button>
					<Button onclick={resetView} class="flex-1">Try Again</Button>
				</div>
			</div>
		{:else if step === 'customer-selection'}
			<Dialog.Header>
				<Dialog.Title>Select Customer</Dialog.Title>
				<Dialog.Description>
					Choose a customer for this {formatCurrency(amount)}
					{action === 'create' ? 'gift card' : 'topup'}.
				</Dialog.Description>
			</Dialog.Header>

			<div class="space-y-4 py-2">
				<div class="flex justify-between rounded-md border bg-white p-4">
					<span class="text-base font-semibold text-gray-700">Total</span>
					<span class="text-xl font-bold text-gray-900">{formatCurrency(amount)}</span>
				</div>

				<!-- Tabs -->
				<div class="flex gap-6 border-b border-gray-200">
					<button
						class="pb-2 text-sm font-medium transition-colors {customerTab === 'current'
							? 'border-b-2 border-primary text-primary'
							: 'text-gray-500 hover:text-gray-700'}"
						onclick={() => {
							customerTab = 'current';
						}}
					>
						Current Customer
					</button>
					<button
						class="pb-2 text-sm font-medium transition-colors {customerTab === 'member'
							? 'border-b-2 border-primary text-primary'
							: 'text-gray-500 hover:text-gray-700'}"
						onclick={() => {
							customerTab = 'member';
						}}
					>
						Member
					</button>
					<button
						class="pb-2 text-sm font-medium transition-colors {customerTab === 'guest'
							? 'border-b-2 border-primary text-primary'
							: 'text-gray-500 hover:text-gray-700'}"
						onclick={() => {
							customerTab = 'guest';
						}}
					>
						Guest / Non-Member
					</button>
				</div>

				<!-- Tab: Current Customer -->
				{#if customerTab === 'current'}
					<div class="space-y-3">
						{#if currentCustomerInfo}
							<div class="space-y-3 rounded-lg border p-4">
								<div class="flex items-center gap-3">
									<div
										class="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-sm font-semibold text-primary"
									>
										{currentCustomerInfo.name.charAt(0).toUpperCase()}
									</div>
									<div>
										<div class="text-sm font-medium">
											{formatUserName(
												currentCustomerInfo.name,
												currentCustomerInfo.previousFirstName,
												currentCustomerInfo.previousLastName
											)}
										</div>
										{#if currentCustomerInfo.email}
											<div class="text-xs text-gray-500">{currentCustomerInfo.email}</div>
										{/if}
									</div>
								</div>
								<Button onclick={selectCurrentCustomer} class="w-full">Select This Customer</Button>
							</div>
						{:else if loadingCurrentCustomer}
							<div class="flex justify-center py-8">
								<span
									class="inline-block h-6 w-6 animate-spin rounded-full border-2 border-gray-300 border-t-primary"
								></span>
							</div>
						{:else}
							<div class="rounded-lg border border-dashed p-6 text-center">
								<p class="text-sm text-gray-500">No current appointment found.</p>
							</div>
						{/if}
					</div>

					<!-- Tab: Member Search -->
				{:else if customerTab === 'member'}
					<div class="space-y-3">
						<div class="flex items-center space-x-2">
							<div class="relative flex-1">
								<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
									<svg
										class="h-4 w-4 text-gray-400"
										fill="none"
										stroke="currentColor"
										viewBox="0 0 24 24"
									>
										<path
											stroke-linecap="round"
											stroke-linejoin="round"
											stroke-width="2"
											d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
										/>
									</svg>
								</div>
								<input
									type="text"
									class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 pl-9 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={userQuery}
									onkeydown={(e) => {
										if (e.key === 'Enter') {
											e.preventDefault();
											searchCustomers(1);
										}
									}}
								/>
							</div>
							<Button
								onclick={() => {
									currentPage = 1;
									searchCustomers(1);
								}}
								disabled={!userQuery.trim() || loadingUsers}
							>
								{loadingUsers ? '...' : 'Search'}
							</Button>
						</div>

						<div class="max-h-[260px] overflow-y-auto rounded-md border border-gray-200">
							{#if users.length === 0 && !loadingUsers}
								<div class="flex items-center justify-center p-6 text-sm text-gray-500">
									{userQuery
										? 'No customers found. Try a different search.'
										: 'Search for a customer above to get started.'}
								</div>
							{:else if users.length > 0}
								<ul class="divide-y divide-gray-200 {loadingUsers ? 'opacity-60' : ''}">
									{#each users as userItem (userItem.id)}
										<li>
											<button
												type="button"
												class="flex w-full cursor-pointer items-center justify-between px-4 py-3 text-left transition-colors hover:bg-fuchsia-50"
												onclick={() => selectCustomer(userItem)}
											>
												<div>
													<div class="text-base font-medium">
														{formatUserName(
															userItem.fullName,
															userItem.previousFirstName,
															userItem.previousLastName
														)}
													</div>
													<div class="text-xs text-gray-500">
														{userItem.email}{#if userItem.email && userItem.phone}
															&middot;
														{/if}{userItem.phone}
													</div>
												</div>
											</button>
										</li>
									{/each}
								</ul>
							{/if}
						</div>

						{#if searchTotal > 5}
							<div class="flex items-center justify-between text-xs text-gray-500">
								<button
									class="font-medium text-primary disabled:text-gray-300"
									disabled={currentPage <= 1}
									onclick={() => goToPage(currentPage - 1)}
								>
									&larr; Previous
								</button>
								<span>Page {currentPage} of {totalPages}</span>
								<button
									class="font-medium text-primary disabled:text-gray-300"
									disabled={currentPage >= totalPages}
									onclick={() => goToPage(currentPage + 1)}
								>
									Next &rarr;
								</button>
							</div>
						{/if}
					</div>

					<!-- Tab: Guest / Walk-in -->
				{:else}
					<div class="space-y-4">
						<p class="text-sm text-gray-600">
							A walk-in customer purchasing a gift card. No details required.
						</p>
						<Button onclick={createGuestCustomer} disabled={creatingGuest} class="w-full">
							{creatingGuest ? 'Creating...' : 'Continue as Guest'}
						</Button>
					</div>
				{/if}

				<div class="flex gap-3 pt-2">
					<Button variant="ghost" onclick={onClose} class="flex-1">Back</Button>
				</div>
			</div>
		{:else if step === 'payment-selection'}
			<Dialog.Header>
				<Dialog.Title>Take Payment</Dialog.Title>
				<Dialog.Description>
					Charge {formatCurrency(amount)} for {action === 'create' ? 'gift card' : 'topup'}
					{#if selectedCustomer}
						&mdash; {formatUserName(
							selectedCustomer.name,
							selectedCustomer.previousFirstName,
							selectedCustomer.previousLastName
						)}
					{/if}.
				</Dialog.Description>
			</Dialog.Header>

			<div class="space-y-4 py-4">
				<div class="flex justify-between rounded-md border border-gray-200 bg-white p-4">
					<span class="text-base font-semibold text-gray-700">Total</span>
					<span class="text-xl font-bold text-gray-900">{formatCurrency(amount)}</span>
				</div>

				{#if action === 'create' && !isGuest}
					<div class="space-y-1.5">
						<span
							class="block text-xs font-semibold tracking-wider text-gray-500 text-muted-foreground uppercase"
							>Fulfillment Method</span
						>
						<div class="flex gap-2">
							<button
								type="button"
								class="flex-1 rounded-lg border px-4 py-2 text-center text-sm font-medium transition-colors {delivery ===
								'account'
									? 'border-input bg-fuchsia-100 text-foreground'
									: 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50'}"
								onclick={() => (delivery = 'account')}
							>
								Add to Account
							</button>
							<button
								type="button"
								class="flex-1 rounded-lg border px-4 py-2 text-center text-sm font-medium transition-colors {delivery ===
								'code'
									? 'border-input bg-fuchsia-100 text-foreground'
									: 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50'}"
								onclick={() => (delivery = 'code')}
							>
								Generate Code
							</button>
						</div>
					</div>
				{/if}

				<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={handleCardMachinePayment}
					>
						<svg
							class="mx-auto mb-2 h-8 w-8"
							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={() => (step = 'cash-entering')}
					>
						<svg
							class="mx-auto mb-2 h-8 w-8"
							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>
					{#if !isGuest && savedCardList.length > 0}
						<button
							type="button"
							class="rounded-lg border border-input py-6 text-center text-sm font-semibold transition-colors hover:bg-fuchsia-50"
							onclick={() => {
								fetchSavedCards();
								step = 'saved-card-selecting';
							}}
						>
							<svg
								class="mx-auto mb-2 h-8 w-8"
								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>
							Saved Card
						</button>
					{/if}
					{#if isGuest}
						<button
							type="button"
							class="rounded-lg border border-input py-6 text-center text-sm font-semibold transition-colors hover:bg-fuchsia-50"
							onclick={() => (step = 'card-details-entering')}
						>
							<svg
								class="mx-auto mb-2 h-8 w-8"
								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>
					{/if}
				</div>

				<div class="flex gap-3 pt-2">
					<Button
						variant="ghost"
						onclick={() => {
							step = 'customer-selection';
							selectedCustomer = null;
						}}
						class="flex-1">Back</Button
					>
				</div>
			</div>
		{:else if step === 'saved-card-selecting'}
			<Dialog.Header>
				<Dialog.Title>Saved Cards</Dialog.Title>
				<Dialog.Description>
					Select a saved card for {selectedCustomer?.name || 'this customer'}
				</Dialog.Description>
			</Dialog.Header>

			<div class="space-y-4 py-4">
				{#if loadingSavedCards}
					<div class="flex justify-center py-8">
						<span
							class="inline-block h-6 w-6 animate-spin rounded-full border-2 border-gray-300 border-t-primary"
						></span>
					</div>
				{:else if savedCardList.length === 0}
					<div class="rounded-lg border border-dashed p-6 text-center">
						<p class="text-sm text-gray-500">No saved cards found for this customer.</p>
					</div>
				{:else}
					<div class="space-y-2">
						{#each savedCardList as card (card.id)}
							<button
								type="button"
								class="flex w-full items-center justify-between rounded-lg border p-4 text-left transition-colors hover:bg-gray-50 {selectedSavedCardId ===
								card.id
									? 'border-primary bg-primary/5'
									: 'border-input'}"
								onclick={() => (selectedSavedCardId = card.id)}
							>
								<div>
									<span class="text-sm font-medium">{card.card_brand} **** {card.card_last4}</span>
									{#if card.card_expiry}
										<span class="ml-2 text-xs text-gray-500">exp {card.card_expiry}</span>
									{/if}
								</div>
								<div
									class="h-4 w-4 rounded-full border-2 {selectedSavedCardId === card.id
										? 'border-primary bg-primary'
										: 'border-gray-300'}"
								></div>
							</button>
						{/each}
					</div>
				{/if}

				<div class="flex gap-3">
					<Button
						variant="ghost"
						onclick={() => {
							step = 'payment-selection';
							selectedSavedCardId = null;
						}}
						class="flex-1">Back</Button
					>
					<Button
						onclick={handleSavedCardPayment}
						class="flex-1"
						disabled={!selectedSavedCardId || sendingSavedCard}
					>
						{sendingSavedCard ? 'Processing...' : `Pay ${formatCurrency(amount)}`}
					</Button>
				</div>
			</div>
		{:else}
			<Dialog.Header>
				<Dialog.Title>Take Payment</Dialog.Title>
				<Dialog.Description>
					Charge {formatCurrency(amount)} for {action === 'create' ? 'gift card' : 'topup'}
					{#if selectedCustomer}
						&mdash; {formatUserName(
							selectedCustomer.name,
							selectedCustomer.previousFirstName,
							selectedCustomer.previousLastName
						)}
					{/if}.
				</Dialog.Description>
			</Dialog.Header>

			<div class="space-y-4 py-4">
				<div class="flex justify-between rounded-md border border-gray-200 bg-white p-4">
					<span class="text-base font-semibold text-gray-700">Total</span>
					<span class="text-xl font-bold text-gray-900">{formatCurrency(amount)}</span>
				</div>

				{#if step === 'cash-entering'}
					<div>
						<label for="cash-amount" class="text-sm font-medium text-gray-700">Cash Received</label>
						<div class="relative mt-1">
							<span class="absolute top-1/2 left-3 -translate-y-1/2 text-gray-500">&pound;</span>
							<Input
								id="cash-amount"
								type="text"
								inputmode="decimal"
								value={cashAmount}
								oninput={(e) => (cashAmount = (e.target as HTMLInputElement).value)}
								class="pl-7 text-lg font-semibold"
							/>
						</div>
					</div>

					{#if cashEntryComplete}
						<div class="rounded-md border border-green-200 bg-green-50 p-4">
							<div class="flex justify-between">
								<span class="text-sm font-medium text-green-800">Change Due</span>
								<span class="text-lg font-bold text-green-800">{formatCurrency(changeDue)}</span>
							</div>
						</div>
					{/if}

					<div class="flex gap-3">
						<Button variant="ghost" onclick={() => (step = 'payment-selection')} class="flex-1"
							>Back</Button
						>
						<Button onclick={handleCashPayment} class="flex-1" disabled={!cashEntryComplete}>
							Confirm Cash
						</Button>
					</div>
				{:else if step === 'cash-confirming'}
					<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-green-600"
						></div>
						<p class="text-lg font-medium text-gray-700">Processing cash payment...</p>
					</div>
				{:else if step === 'card-machine'}
					<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-green-600"
						></div>
						<p class="text-lg font-medium text-gray-700">Initiating card machine...</p>
					</div>
				{:else if step === 'card-polling'}
					<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-green-600"
						></div>
						<p class="text-lg font-medium text-gray-700">Waiting for customer to tap card...</p>
						<p class="mt-2 text-sm text-gray-500">This may take a few moments</p>
					</div>
					<div class="flex gap-3 pt-2">
						<Button variant="ghost" onclick={resetView} class="flex-1">Cancel</Button>
					</div>
				{:else if step === 'card-details-entering'}
					<div class="space-y-4">
						<div class="rounded-lg border border-gray-100 bg-gray-50 p-4">
							<h4 class="mb-3 text-sm font-medium text-gray-700">Enter Card Details</h4>
							<p class="mb-3 text-xs text-gray-500">Card will be charged once — not saved.</p>
							<div class="space-y-3">
								<div>
									<label for="till-card-number" class="text-sm font-medium text-gray-700"
										>Card Number</label
									>
									<Input
										id="till-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="till-card-expiry" class="text-sm font-medium text-gray-700"
											>Expiry (MM/YY)</label
										>
										<Input
											id="till-card-expiry"
											type="text"
											inputmode="numeric"
											value={ephemeralCardExpiry}
											oninput={handleEphemeralExpiryInput}
											placeholder="MM/YY"
											maxlength={5}
											class="mt-1"
										/>
									</div>
									<div>
										<label for="till-card-cvc" class="text-sm font-medium text-gray-700">CVC</label>
										<Input
											id="till-card-cvc"
											type="text"
											inputmode="numeric"
											value={ephemeralCardCVC}
											oninput={handleEphemeralCvcInput}
											placeholder="123"
											maxlength={4}
											class="mt-1"
										/>
									</div>
								</div>
								{#if ephemeralCardError}
									<div class="text-xs font-semibold text-red-500">{ephemeralCardError}</div>
								{/if}
							</div>
						</div>
						<div class="flex gap-3">
							<Button variant="ghost" onclick={() => (step = 'payment-selection')} class="flex-1"
								>Back</Button
							>
							<Button
								onclick={handleEphemeralCardPayment}
								class="flex-1"
								disabled={!isEphemeralCardValid || sendingEphemeralCard}
							>
								{sendingEphemeralCard ? 'Processing...' : `Pay ${formatCurrency(amount)}`}
							</Button>
						</div>
					</div>
				{/if}
			</div>
		{/if}
	</Dialog.Content>
</Dialog.Root>
