<script lang="ts">
	import { goto } from '$app/navigation';
	import { authStore, type User } from '$lib/stores/auth.svelte';
	import { SvelteDate } from 'svelte/reactivity';
	import { browser } from '$app/environment';
	import { toast } from 'svelte-sonner';
	import { sanitizeText } from '$lib/utils/toast-safe';
	import { apiFetch } from '$lib/utils/api';
	import UserBookingModal from '$lib/components/account/UserBookingModal.svelte';
	import { isValidUKPhone, formatPhoneDisplay, toE164UK } from '$lib/utils/phone';
	import { savedCardsStore, type SavedCard } from '$lib/stores/savedCards.svelte';

	// zxcvbn-ts imports
	import { zxcvbn, zxcvbnOptions } from '@zxcvbn-ts/core';
	import * as languageCommon from '@zxcvbn-ts/language-common';
	import * as languageEn from '@zxcvbn-ts/language-en';

	// set up options so that feedback, dictionary etc. are included
	zxcvbnOptions.setOptions({
		translations: languageEn.translations,
		graphs: languageCommon.adjacencyGraphs,
		dictionary: {
			...languageCommon.dictionary,
			...languageEn.dictionary
		}
	});

	// shadcn-svelte components
	import { Button } from '$lib/components/ui/button';
	import { Checkbox } from '$lib/components/ui/checkbox';
	import * as Card from '$lib/components/ui/card';
	import { Input } from '$lib/components/ui/input';
	import { EmailInput } from '$lib/components/ui/email-input/index.js';
	import { PhoneInput } from '$lib/components/ui/phone-input/index.js';
	import { Separator } from '$lib/components/ui/separator';
	import * as AlertDialog from '$lib/components/ui/alert-dialog';
	import { Skeleton } from '$lib/components/ui/skeleton';
	import PolicyPopover from '$lib/components/ui/policyPopover.svelte';
	import * as Dialog from '$lib/components/ui/dialog';
	import Cropper from 'svelte-easy-crop';

	// =============== Auth & Page State ===============
	let pageState = $state<'loading' | 'authorized' | 'unauthorized'>('loading');

	$effect(() => {
		if (!browser) return;

		if (authStore.isLoading) {
			pageState = 'loading';
			return;
		}

		if (!authStore.isAuthenticated) {
			pageState = 'unauthorized';
			// eslint-disable-next-line svelte/no-navigation-without-resolve
			goto('/login', { replaceState: true });
			return;
		}

		pageState = 'authorized';
	});

	// =============== Tab State ===============
	let activeTab = $state<'general' | 'history' | 'referral' | 'cards' | 'admin'>('general');

	const canSaveCards = $derived(
		authStore.currentUser?.role === 'verified_email' || authStore.currentUser?.role === 'affiliate'
	);

	type Booking = {
		id: string;
		start_time: string;
		status: string;
		notes?: string;
		services: Array<{
			service_name: string;
			price: number;
			duration_minutes: number;
		}>;
		payments: Array<{
			id: string;
			amount: number;
			payment_method: string;
			payment_type: string;
			status: string;
			created_at: string;
		}>;
		total_amount: number;
		amount_paid: number;
		amount_due: number;
		duration_minutes: number;
		created_at: string;
	};

	let userData = $state<User | null>(null);
	let loadingUser = $state(true);
	let stamps = $state(0);
	// eslint-disable-next-line @typescript-eslint/no-unused-vars
	let pendingRedemption = $state(false);
	// eslint-disable-next-line @typescript-eslint/no-unused-vars
	let uploadingPic = $state(false);

	function getStampPath(slotNum: number): string {
		const petals = 7 + (slotNum % 4); // 7, 8, 9, 10 petals
		const amp = 2.5 + (slotNum % 3) * 0.5; // 2.5, 3.0, 3.5 amplitude
		const phase = slotNum * 12; // phase offset in degrees
		const R = 42; // base radius
		const centerX = 50;
		const centerY = 50;

		let path = '';
		const steps = 120;
		for (let i = 0; i < steps; i++) {
			const angleDeg = (i * 360) / steps;
			const angleRad = (angleDeg * Math.PI) / 180;
			const phaseRad = (phase * Math.PI) / 180;
			const r = R + amp * Math.sin(petals * angleRad + phaseRad);
			const x = (centerX + r * Math.cos(angleRad)).toFixed(1);
			const y = (centerY + r * Math.sin(angleRad)).toFixed(1);
			if (i === 0) {
				path += `M ${x} ${y}`;
			} else {
				path += ` L ${x} ${y}`;
			}
		}
		return path + ' Z';
	}

	const notifPrefs = $state({ emailEnabled: true, smsEnabled: true, browserPushEnabled: true });

	const loadingCards = $state(false);

	let cardToDelete = $state<SavedCard | null>(null);
	let showDeleteCardDialog = $state(false);

	async function deleteCard(card: SavedCard) {
		try {
			const res = await apiFetch(`/api/user/payment-methods/${card.id}`, {
				method: 'DELETE'
			});
			if (res.ok) {
				toast.success('Card removed');
				await savedCardsStore.invalidate();
			} else {
				toast.error('Failed to remove card');
			}
		} catch {
			toast.error('Network error');
		} finally {
			cardToDelete = null;
		}
	}

	let showAddCard = $state(false);
	let newCardNumber = $state('');
	let newCardExpiry = $state('');
	let newCardCVC = $state('');
	let addingCard = $state(false);

	// =============== Gift Card State ===============
	let giftCardBalance = $state(0);
	let loadingBalance = $state(false);

	let giftCardCode = $state('');
	let redeemingGiftCard = $state(false);
	let showRedeemConfirm = $state(false);

	// Buy Gift Card State
	let buyAmount = $state<10 | 20 | 50>(10);
	let buyRecipientType = $state<'self' | 'friend'>('self');
	let buyRecipientEmail = $state('');
	let buySelectedCard = $state('');
	let buyNewCardNumber = $state('');
	let buyNewCardExpiry = $state('');
	let buyNewCardCVC = $state('');
	let buySaveCard = $state(false);
	let buyingGiftCard = $state(false);
	let purchaseResultCode = $state<string | null>(null);

	$effect(() => {
		if (savedCardsStore.cards.length > 0 && !buySelectedCard) {
			const defaultCard =
				savedCardsStore.cards.find((c) => c.is_default) || savedCardsStore.cards[0];
			buySelectedCard = defaultCard.id;
		}
	});

	function parseExpiryParts(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 };
	}

	// Derived validations for Add Saved Card form
	const newCardExpiryParts = $derived(parseExpiryParts(newCardExpiry));
	const isNewCardExpiryInPast = $derived(
		newCardExpiryParts !== null &&
			(() => {
				const expiryDate = new SvelteDate(newCardExpiryParts.year, newCardExpiryParts.month);
				return expiryDate < new SvelteDate();
			})()
	);
	const isNewCardExpiryInvalidMonth = $derived(
		/^\d{2}\/\d{2}$/.test(newCardExpiry) && newCardExpiryParts === null
	);

	const addCardError = $derived(
		newCardNumber.length > 0 && !isValidLuhn(newCardNumber)
			? 'Invalid card number'
			: isNewCardExpiryInvalidMonth
				? 'Invalid expiry month'
				: isNewCardExpiryInPast
					? 'This card has already expired'
					: newCardCVC.length > 0 && newCardCVC.length < 3
						? 'CVC must be at least 3 digits'
						: null
	);

	const isAddCardValid = $derived(
		isValidLuhn(newCardNumber) && /^\d{2}\/\d{2}$/.test(newCardExpiry) && newCardCVC.length >= 3
	);

	// Derived validations for Buy Gift Card form
	const buyNewCardExpiryParts = $derived(parseExpiryParts(buyNewCardExpiry));
	const isBuyNewCardExpiryInPast = $derived(
		buyNewCardExpiryParts !== null &&
			(() => {
				const expiryDate = new SvelteDate(buyNewCardExpiryParts.year, buyNewCardExpiryParts.month);
				return expiryDate < new SvelteDate();
			})()
	);
	const isBuyNewCardExpiryInvalidMonth = $derived(
		/^\d{2}\/\d{2}$/.test(buyNewCardExpiry) && buyNewCardExpiryParts === null
	);

	const buyCardError = $derived(
		buyNewCardNumber.length > 0 && !isValidLuhn(buyNewCardNumber)
			? 'Invalid card number'
			: isBuyNewCardExpiryInvalidMonth
				? 'Invalid expiry month'
				: isBuyNewCardExpiryInPast
					? 'This card has already expired'
					: buyNewCardCVC.length > 0 && buyNewCardCVC.length < 3
						? 'CVC must be at least 3 digits'
						: null
	);

	const isBuyCardValid = $derived(
		buySelectedCard !== '' ||
			(isValidLuhn(buyNewCardNumber) &&
				buyNewCardExpiryParts !== null &&
				!isBuyNewCardExpiryInPast &&
				buyNewCardCVC.length >= 3)
	);

	async function fetchGiftCardBalance() {
		loadingBalance = true;
		try {
			const res = await apiFetch('/api/user/giftcards/balance');
			if (res.ok) {
				const data = await res.json();
				giftCardBalance = data.balance;
			}
		} catch {
			// ignore
		} finally {
			loadingBalance = false;
		}
	}

	async function redeemGiftCard() {
		if (giftCardCode.replace(/[^a-zA-Z0-9]/g, '').length !== 12) {
			toast.error('Invalid gift card code format');
			return;
		}
		redeemingGiftCard = true;
		try {
			const res = await apiFetch('/api/user/giftcards/redeem', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({ code: giftCardCode })
			});
			if (res.ok) {
				const data = await res.json();
				toast.success(`Success! Redeemed ${formatCurrency(data.amount_redeemed)} to your balance.`);
				giftCardCode = '';
				await fetchGiftCardBalance();
			} else {
				const errText = await res.text();
				toast.error(errText || 'Failed to redeem gift card');
			}
		} catch (err) {
			console.error('redeemGiftCard error:', err);
			toast.error('Network error');
		} finally {
			redeemingGiftCard = false;
		}
	}

	async function buyGiftCard() {
		buyingGiftCard = true;
		try {
			let cardId: string | undefined;
			let newCardToken: string | undefined;
			let saveCard = false;

			if (buySelectedCard) {
				cardId = buySelectedCard;
			} else if (buyNewCardNumber) {
				if (
					!isValidLuhn(buyNewCardNumber) ||
					!/^\d{2}\/\d{2}$/.test(buyNewCardExpiry) ||
					buyNewCardCVC.length < 3
				) {
					toast.error('Please enter valid credit card details');
					buyingGiftCard = false;
					return;
				}
				newCardToken = buyNewCardNumber;
				saveCard = buySaveCard;
			} else {
				toast.error('Please select or enter card details');
				buyingGiftCard = false;
				return;
			}

			const idempotencyKey = generateIdempotencyKey();

			const res = await apiFetch('/api/user/giftcards/buy', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					amount: buyAmount * 100, // cents
					recipient_type: buyRecipientType,
					recipient_email: buyRecipientEmail,
					card_id: cardId,
					new_card_token: newCardToken,
					save_card: saveCard,
					idempotency_key: idempotencyKey
				})
			});

			if (res.ok) {
				const data = await res.json();
				toast.success('Gift card purchased successfully!');
				purchaseResultCode = data.code;
				buyNewCardNumber = '';
				buyNewCardExpiry = '';
				buyNewCardCVC = '';
				await fetchGiftCardBalance();
				if (buySelectedCard === '') {
					await savedCardsStore.fetch();
				}
			} else {
				const errText = await res.text();
				toast.error(errText || 'Failed to purchase gift card');
			}
		} catch (err) {
			console.error('buyGiftCard error:', err);
			toast.error('Network error');
		} finally {
			buyingGiftCard = false;
		}
	}

	function formatAndPreserveCursor(
		input: HTMLInputElement,
		formatter: (val: string) => string,
		charRegex: RegExp = /\d/
	): string {
		const rawValue = input.value;
		const oldSelectionStart = input.selectionStart || 0;

		let charsBeforeCursor = 0;
		for (let i = 0; i < oldSelectionStart; i++) {
			if (charRegex.test(rawValue[i])) {
				charsBeforeCursor++;
			}
		}

		const formatted = formatter(rawValue);
		input.value = formatted;

		let newSelectionStart = 0;
		let charsFound = 0;
		for (let i = 0; i < formatted.length; i++) {
			if (charsFound === charsBeforeCursor) {
				break;
			}
			if (charRegex.test(formatted[i])) {
				charsFound++;
			}
			newSelectionStart++;
		}

		requestAnimationFrame(() => {
			input.setSelectionRange(newSelectionStart, newSelectionStart);
		});

		return formatted;
	}

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

	$effect(() => {
		if (savedCardsStore.cards.length === 0 && buySelectedCard !== '') {
			buySelectedCard = '';
		}
	});

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

	function formatCardCode(id: string): string {
		const raw = id.toLowerCase().replace(/[^a-z0-9]/g, '');
		if (raw.length <= 4) return raw.toUpperCase();
		if (raw.length <= 8) return raw.slice(0, 4).toUpperCase() + '-' + raw.slice(4, 8).toUpperCase();
		return (
			raw.slice(0, 4).toUpperCase() +
			'-' +
			raw.slice(4, 8).toUpperCase() +
			'-' +
			raw.slice(8, 12).toUpperCase()
		);
	}

	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 handleCardNumberInput(e: Event) {
		const input = e.target as HTMLInputElement;
		const formatted = formatAndPreserveCursor(input, formatCardNumber);
		newCardNumber = formatted;
	}

	function handleExpiryInput(e: Event) {
		const input = e.target as HTMLInputElement;
		const formatted = formatAndPreserveCursor(input, formatExpiryDate);
		newCardExpiry = formatted;
	}

	function handleCvcInput(e: Event) {
		const input = e.target as HTMLInputElement;
		const formatted = formatAndPreserveCursor(input, (val) =>
			val.replace(/\D/g, '').substring(0, 4)
		);
		newCardCVC = formatted;
	}

	function handleBuyCardNumberInput(e: Event) {
		const input = e.target as HTMLInputElement;
		const formatted = formatAndPreserveCursor(input, formatCardNumber);
		buyNewCardNumber = formatted;
	}

	// Uses custom formatter with MM/YY slash and preserves cursor position
	function handleBuyExpiryInput(e: Event) {
		const input = e.target as HTMLInputElement;
		const formatted = formatAndPreserveCursor(input, formatExpiryDate);
		buyNewCardExpiry = formatted;
	}

	function handleBuyCvcInput(e: Event) {
		const input = e.target as HTMLInputElement;
		const formatted = formatAndPreserveCursor(input, (val) =>
			val.replace(/\D/g, '').substring(0, 4)
		);
		buyNewCardCVC = formatted;
	}

	function generateIdempotencyKey(): string {
		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;
		return [...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('');
	}

	async function addCard() {
		if (
			!isValidLuhn(newCardNumber) ||
			!/^\d{2}\/\d{2}$/.test(newCardExpiry) ||
			newCardCVC.length < 3
		) {
			toast.error('Please fill in all card details correctly');
			return;
		}
		const cardNum = newCardNumber.replace(/\s/g, '');
		const [monthStr, yearStr] = newCardExpiry.split('/');
		const month = parseInt(monthStr, 10);
		const year = 2000 + parseInt(yearStr, 10);
		if (month < 1 || month > 12) {
			toast.error('Invalid expiry month');
			return;
		}
		const expiryDate = new Date(year, month);
		if (expiryDate < new Date()) {
			toast.error('This card has already expired');
			return;
		}
		addingCard = true;
		try {
			const res = await apiFetch('/api/user/payment-methods', {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					card_number: cardNum,
					expiry: newCardExpiry,
					cvc: newCardCVC
				})
			});
			if (res.ok) {
				toast.success('Card added');
				showAddCard = false;
				newCardNumber = '';
				newCardExpiry = '';
				newCardCVC = '';
				savedCardsStore.invalidate();
			} else {
				const errText = await res.text();
				toast.error(errText || 'Failed to add card');
			}
		} catch (err) {
			console.error('addCard error:', err);
			toast.error('Network error');
		} finally {
			addingCard = false;
		}
	}

	async function fetchNotifPrefs() {
		try {
			const res = await apiFetch('/api/user/notification-preferences');
			if (res.ok) {
				const data = await res.json();
				notifPrefs.emailEnabled = data.emailEnabled ?? true;
				notifPrefs.smsEnabled = data.smsEnabled ?? true;
				notifPrefs.browserPushEnabled = data.browserPushEnabled ?? true;
			}
		} catch {
			/* silently fail */
		}
	}

	async function saveNotifPrefs() {
		try {
			await apiFetch('/api/user/notification-preferences', {
				method: 'PUT',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify(notifPrefs)
			});
		} catch {
			/* silently fail */
		}
	}

	// Image cropper state
	let cropDialogOpen = $state(false);
	let cropImageUrl = $state('');
	let cropArea = $state<{ x: number; y: number; width: number; height: number } | null>(null);
	let crop = $state({ x: 0, y: 0 });
	let zoom = $state(1);
	let previewUrl = $state('');

	const PROFILE_PIC_MAX_SIZE = 15 * 1024 * 1024; // 15MB

	function handleFileSelect(e: Event) {
		const input = e.target as HTMLInputElement;
		const file = input.files?.[0];
		if (file) {
			if (file.size > PROFILE_PIC_MAX_SIZE) {
				toast.error('Profile picture must be under 15MB');
				return;
			}
			cropImageUrl = URL.createObjectURL(file);
			cropDialogOpen = true;
		}
	}

	async function handleCropSave() {
		if (!cropArea || !cropImageUrl) return;

		const img = new Image();
		img.src = cropImageUrl;
		await new Promise((resolve) => {
			img.onload = resolve;
		});

		const canvas = document.createElement('canvas');
		canvas.width = 350;
		canvas.height = 350;
		const ctx = canvas.getContext('2d');
		if (!ctx) return;

		ctx.drawImage(img, cropArea.x, cropArea.y, cropArea.width, cropArea.height, 0, 0, 350, 350);

		canvas.toBlob(
			(blob) => {
				if (!blob) return;

				const url = URL.createObjectURL(blob);
				previewUrl = url;

				handleProfilePicUpload(blob).then(() => {
					URL.revokeObjectURL(cropImageUrl);
					cropImageUrl = '';
					cropArea = null;
					cropDialogOpen = false;
				});
			},
			'image/jpeg',
			0.9
		);
	}

	function handleCropCancel() {
		if (cropImageUrl) {
			URL.revokeObjectURL(cropImageUrl);
		}
		cropImageUrl = '';
		cropArea = null;
		cropDialogOpen = false;
	}

	async function handleProfilePicUpload(blob: Blob) {
		uploadingPic = true;
		try {
			const formData = new FormData();
			formData.append('file', blob, 'profile.jpg');
			const uploadResponse = await apiFetch('/api/user/profile-picture', {
				method: 'POST',
				body: formData
			});
			if (uploadResponse.ok) {
				const data = await uploadResponse.json();
				if (userData) {
					userData.profilePicUrl = data.url;
				}
				toast.success('Profile picture updated');
			} else {
				toast.error('Failed to upload profile picture');
			}
		} catch (err) {
			console.error('Upload error:', err);
			toast.error('Failed to upload profile picture');
		} finally {
			uploadingPic = false;
		}
	}

	// =============== Phone Edit Mode ===============
	let editingPhone = $state(false);
	let phoneInput = $state('');
	let phoneError = $state('');
	let savingPhone = $state(false);

	// Phone validation (UK format)
	// eslint-disable-next-line @typescript-eslint/no-unused-vars
	function validatePhone(phone: string): boolean {
		return isValidUKPhone(phone);
	}

	// eslint-disable-next-line @typescript-eslint/no-unused-vars
	function formatPhoneInput(value: string): string {
		return formatPhoneDisplay(value);
	}

	function startEditPhone() {
		phoneInput = userData?.phone || '';
		phoneError = '';
		editingPhone = true;
	}

	function cancelEditPhone() {
		editingPhone = false;
		phoneInput = '';
		phoneError = '';
	}

	async function savePhone() {
		const formattedPhone = toE164UK(phoneInput);
		if (!formattedPhone) {
			phoneError = 'Invalid UK phone number';
			toast.error('Please enter a valid UK phone number');
			return;
		}

		savingPhone = true;
		const loadingToast = toast.loading('Updating phone number...');

		try {
			const response = await apiFetch('/api/user/profile', {
				method: 'PUT',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					firstName: userData?.firstName,
					lastName: userData?.lastName,
					phone: formattedPhone // already E.164 from toE164UK
				})
			});

			if (response.ok) {
				toast.success('Phone number updated successfully!', { id: loadingToast });
				editingPhone = false;
				// Refresh user data
				await fetchUserData();
			} else {
				const text = await response.text();
				toast.error(sanitizeText(text) || 'Failed to update phone number', { id: loadingToast });
			}
		} catch (err) {
			console.error('Error updating phone:', err);
			toast.error('Network error', { id: loadingToast });
		} finally {
			savingPhone = false;
		}
	}

	// =============== Name Edit Mode ===============
	let editingFirstName = $state(false);
	let editingLastName = $state(false);
	let firstNameInput = $state('');
	let lastNameInput = $state('');
	let firstNameError = $state('');
	let lastNameError = $state('');
	let savingName = $state(false);

	// Name validation (unicode letters, spaces, hyphen, apostrophe, dot)
	const nameRegex = /^[\p{L}\p{M}\s'.-]+$/u;

	function startEditFirstName() {
		firstNameInput = userData?.firstName || '';
		firstNameError = '';
		editingFirstName = true;
		editingLastName = false;
	}

	function startEditLastName() {
		lastNameInput = userData?.lastName || '';
		lastNameError = '';
		editingLastName = true;
		editingFirstName = false;
	}

	function cancelEditName() {
		editingFirstName = false;
		editingLastName = false;
		firstNameInput = '';
		lastNameInput = '';
		firstNameError = '';
		lastNameError = '';
	}

	async function saveName() {
		const newFirstName = editingFirstName ? firstNameInput.trim() : userData?.firstName || '';
		const newLastName = editingLastName ? lastNameInput.trim() : userData?.lastName || '';

		// Validate
		if (!newFirstName || !newLastName) {
			if (!newFirstName) firstNameError = 'First name is required';
			if (!newLastName) lastNameError = 'Last name is required';
			toast.error('Name is required');
			return;
		}

		if (newFirstName.length > 50) {
			firstNameError = 'First name must be 50 characters or less';
			toast.error('First name is too long');
			return;
		}
		if (newLastName.length > 50) {
			lastNameError = 'Last name must be 50 characters or less';
			toast.error('Last name is too long');
			return;
		}

		if (!nameRegex.test(newFirstName)) {
			firstNameError = 'Invalid characters in first name';
			toast.error('Please use only letters, spaces, hyphens, apostrophes, or dots');
			return;
		}
		if (!nameRegex.test(newLastName)) {
			lastNameError = 'Invalid characters in last name';
			toast.error('Please use only letters, spaces, hyphens, apostrophes, or dots');
			return;
		}

		savingName = true;
		const loadingToast = toast.loading('Updating name...');

		try {
			const response = await apiFetch('/api/user/profile', {
				method: 'PUT',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					firstName: newFirstName,
					lastName: newLastName,
					phone: userData?.phone || ''
				})
			});

			if (response.ok) {
				toast.success('Name updated successfully!', { id: loadingToast });
				editingFirstName = false;
				editingLastName = false;
				firstNameInput = '';
				lastNameInput = '';
				firstNameError = '';
				lastNameError = '';
				// Refresh user data to get updated info (including previous names)
				await fetchUserData();
			} else {
				const text = await response.text();
				toast.error(sanitizeText(text) || 'Failed to update name', { id: loadingToast });
			}
		} catch (err) {
			console.error('Error updating name:', err);
			toast.error('Network error', { id: loadingToast });
		} finally {
			savingName = false;
		}
	}

	// =============== GDPR Export Status ===============
	const GDPR_COOLDOWN_MS = 12 * 60 * 60 * 1000;
	let gdprExportMeta = $state<{ exported_at?: string } | null>(null);
	let gdprCountdown = $state<string | null>(null);
	let countdownTimer: ReturnType<typeof setInterval> | null = null;

	function updateCountdown(exportedAt: number) {
		const elapsed = Date.now() - exportedAt;
		const remaining = GDPR_COOLDOWN_MS - elapsed;
		if (remaining <= 0) {
			gdprExportMeta = null;
			gdprCountdown = null;
			return;
		}
		const hours = Math.floor(remaining / (60 * 60 * 1000));
		const minutes = Math.floor((remaining % (60 * 60 * 1000)) / (60 * 1000));
		const seconds = Math.floor((remaining % (60 * 1000)) / 1000);
		if (hours > 0) {
			gdprCountdown = `New export available in ${hours}h ${minutes}m`;
		} else if (minutes > 0) {
			gdprCountdown = `New export available in ${minutes}m ${seconds}s`;
		} else {
			gdprCountdown = `New export available in ${seconds}s`;
		}
	}

	function startCountdown(exportedAt: string) {
		stopCountdown();
		const ts = new Date(exportedAt).getTime();
		updateCountdown(ts);
		if (gdprExportMeta) {
			countdownTimer = setInterval(() => updateCountdown(ts), 1000);
		}
	}

	function stopCountdown() {
		if (countdownTimer) {
			clearInterval(countdownTimer);
			countdownTimer = null;
		}
	}

	async function fetchGdprExportStatus() {
		try {
			const res = await apiFetch('/api/user/gdpr-export');
			if (res.ok) {
				const data = await res.json();
				if (data.export_metadata?.exported_at) {
					const elapsed = Date.now() - new Date(data.export_metadata.exported_at).getTime();
					if (elapsed < GDPR_COOLDOWN_MS) {
						gdprExportMeta = data.export_metadata;
						startCountdown(data.export_metadata.exported_at);
						return;
					}
				}
			}
			// No valid export: clear everything.
			gdprExportMeta = null;
			gdprCountdown = null;
		} catch {
			// silently fail
		}
	}

	// =============== Fetch User Data ===============
	async function fetchUserData() {
		if (pageState !== 'authorized') return;

		loadingUser = true;
		try {
			const response = await apiFetch('/api/user/profile', {
				method: 'GET',
				headers: { 'Content-Type': 'application/json' }
			});

			if (response.ok) {
				const data = await response.json();
				userData = data;
				stamps = userData?.loyaltyStamps ?? 0;
				pendingRedemption = stamps >= 10;
			} else {
				toast.error('Failed to load profile data');
			}
		} catch (err) {
			console.error('Error fetching user data:', err);
			toast.error('Network error loading profile');
		} finally {
			loadingUser = false;
		}
	}

	// =============== Fetch Bookings ===============
	let upcomingBookings = $state<Booking[]>([]);
	let pastBookings = $state<Booking[]>([]);
	let pastPage = $state(1);
	let pastTotalPages = $state(1);

	let loadingUpcoming = $state(false);
	let loadingPast = $state(false);

	// =============== Fetch Upcoming Bookings (next 3) ===============
	async function fetchUpcomingBookings() {
		if (pageState !== 'authorized') return;

		loadingUpcoming = true;
		try {
			const today = new Date().toLocaleDateString('en-CA', { timeZone: 'Europe/London' });

			// Fetch more items (e.g. 10) to ensure we find upcoming ones even if the first few are past
			const response = await apiFetch(`/api/bookings?start_date=${today}&per_page=10&page=1`, {
				method: 'GET',
				headers: { 'Content-Type': 'application/json' }
			});

			if (!response.ok) {
				const text = await response.text();
				toast.error('Failed to load upcoming bookings: ' + sanitizeText(text));
				return;
			}

			const data = await response.json();
			const now = new SvelteDate();

			// Filter: Calculate end time (Start + Duration) and check if it's in the future
			const activeOrFutureBookings = (data.bookings || []).filter((b: Booking) => {
				const startTime = new SvelteDate(b.start_time);
				// Add duration (in ms)
				const endTime = new SvelteDate(startTime.getTime() + (b.duration_minutes || 0) * 60000);
				return endTime > now;
			});

			// Take only the top 3
			upcomingBookings = activeOrFutureBookings.slice(0, 3);
		} catch (err) {
			console.error('Error fetching upcoming bookings:', err);
			toast.error('Network error loading upcoming bookings');
		} finally {
			loadingUpcoming = false;
		}
	}

	// =============== Fetch Past Bookings (paginated 10 per page) ===============
	async function fetchPastBookings(page = 1) {
		if (pageState !== 'authorized') return;

		loadingPast = true;
		try {
			const today = new Date().toLocaleDateString('en-CA', { timeZone: 'Europe/London' });
			const response = await apiFetch(`/api/bookings?end_date=${today}&per_page=10&page=${page}`, {
				method: 'GET',
				headers: { 'Content-Type': 'application/json' }
			});

			if (!response.ok) {
				const text = await response.text();
				toast.error('Failed to load past bookings: ' + sanitizeText(text));
				return;
			}

			const data = await response.json();
			let bookings = data.bookings || [];

			// Only include bookings that have actually finished (endTime <= now)
			const now = new SvelteDate();
			bookings = bookings.filter((b: Booking) => {
				const startTime = new SvelteDate(b.start_time);
				const endTime = new SvelteDate(startTime.getTime() + (b.duration_minutes || 0) * 60000);
				return endTime <= now;
			});

			// FIX: Manually calculate amount_due for the list
			// The list API often returns 0 for amount_due/amount_paid,
			// so we derive it from total_amount.
			bookings = bookings.map((b: Booking) => {
				const total = b.total_amount || 0;
				const paid = b.amount_paid || 0;
				return {
					...b,
					amount_due: total - paid // Force calculate the balance
				};
			});

			// SORT LOGIC: Unpaid first, then by most recent
			bookings.sort((a: Booking, b: Booking) => {
				const aUnpaid = (a.amount_due || 0) > 0;
				const bUnpaid = (b.amount_due || 0) > 0;

				// If A is unpaid and B is not, A comes first
				if (aUnpaid && !bUnpaid) return -1;
				// If B is unpaid and A is not, B comes first
				if (!aUnpaid && bUnpaid) return 1;

				// If both have same payment status, sort by Date DESC (newest first)
				return new SvelteDate(b.start_time).getTime() - new SvelteDate(a.start_time).getTime();
			});

			pastBookings = bookings;
			pastPage = data.page || page;
			pastTotalPages = Math.ceil((data.total || 0) / (data.per_page || 10));
		} catch (err) {
			console.error('Error fetching past bookings:', err);
			toast.error('Network error loading past bookings');
		} finally {
			loadingPast = false;
		}
	}

	// =============== Pagination Helpers ===============
	function goToPastPage(page: number) {
		if (page < 1 || page > pastTotalPages) return;
		fetchPastBookings(page);
	}

	$effect(() => {
		if (pageState === 'authorized') {
			fetchUserData();
			fetchUpcomingBookings();
			fetchPastBookings();
			fetchNotifPrefs();
			fetchGdprExportStatus();
		}
	});

	// =============== Password Change ===============
	let showPasswordModal = $state(false);
	let passwordData = $state({
		current: '',
		new: '',
		confirm: ''
	});
	let changingPassword = $state(false);

	// Password strength using zxcvbn
	const newPasswordStrength = $derived(passwordData.new ? zxcvbn(passwordData.new) : null);
	const isPasswordStrongEnough = $derived(
		!passwordData.new || newPasswordStrength === null || newPasswordStrength.score >= 2
	);
	const passwordsMatch = $derived(
		passwordData.confirm === '' || passwordData.new === passwordData.confirm
	);

	async function changePassword() {
		if (passwordData.new !== passwordData.confirm) {
			toast.error('New passwords do not match');
			return;
		}

		if (passwordData.new.length < 8) {
			toast.error('Password must be at least 8 characters');
			return;
		}

		if (!isPasswordStrongEnough) {
			toast.error('Please choose a stronger password');
			return;
		}

		changingPassword = true;
		const loadingToast = toast.loading('Changing password...');

		try {
			const response = await apiFetch('/api/user/change-password', {
				method: 'PUT',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					current_password: passwordData.current,
					new_password: passwordData.new
				})
			});

			if (response.ok) {
				toast.success('Password changed successfully!', { id: loadingToast });
				showPasswordModal = false;
				passwordData = { current: '', new: '', confirm: '' };
			} else {
				const text = await response.text();
				toast.error(sanitizeText(text) || 'Failed to change password', { id: loadingToast });
			}
		} catch (err) {
			console.error('Error changing password:', err);
			toast.error('Network error', { id: loadingToast });
		} finally {
			changingPassword = false;
		}
	}

	// =============== Booking modal ==============
	// =============== Modal State ===============
	let showBookingModal = $state(false);
	let selectedBookingId = $state<string | null>(null);

	function openBookingModal(id: string) {
		selectedBookingId = id;
		showBookingModal = true;
	}

	// =============== Account Deletion ===============
	let showDeleteAlert = $state(false);
	let deleteConfirmText = $state('');
	let deletingAccount = $state(false);

	async function deleteAccount() {
		if (deleteConfirmText !== 'DELETE') {
			toast.error('Please type DELETE to confirm');
			return;
		}

		deletingAccount = true;
		const loadingToast = toast.loading('Deleting account...');

		try {
			const response = await apiFetch('/api/user/account', {
				method: 'DELETE'
			});

			if (response.ok) {
				toast.success('Account deleted successfully', { id: loadingToast });
				authStore.logout();
				// eslint-disable-next-line svelte/no-navigation-without-resolve
				goto('/');
			} else {
				const text = await response.text();
				toast.error(sanitizeText(text) || 'Failed to delete account', { id: loadingToast });
			}
		} catch (err) {
			console.error('Error deleting account:', err);
			toast.error('Network error', { id: loadingToast });
		} finally {
			deletingAccount = false;
		}
	}

	// =============== Copy Referral Code ===============
	function copyReferralCode() {
		if (userData?.referralCode) {
			navigator.clipboard.writeText(userData.referralCode.replace(/-/g, ''));
			toast.success('Referral code copied to clipboard!');
		}
	}

	function formatDateTime(dateString: string): string {
		const date = new SvelteDate(dateString);

		// Get date parts
		const day = date.getDate();
		const month = date.toLocaleString('en-GB', { month: 'short' });
		const year = date.getFullYear();

		// Get time parts
		const hours = date.getHours();
		const minutes = date.getMinutes();

		// Special cases for midnight and noon
		let timeStr;
		if (hours === 12 && minutes === 0) {
			timeStr = 'Noon';
		} else if (hours === 0 && minutes === 0) {
			timeStr = 'Midnight';
		} else {
			const period = hours >= 12 ? 'PM' : 'AM';
			const displayHours = hours % 12 || 12;
			timeStr = `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`;
		}

		return `${day} ${month} ${year}, ${timeStr}`;
	}
</script>

<svelte:head>
	<script>
		(function () {
			// Pre-hydration auth guard: reads localStorage directly because the Svelte
			// authStore hasn't initialized yet at this point (async+$state). This runs
			// synchronously in <svelte:head> before any rendering, preventing a flash
			// of protected content. The authStore handles post-hydration auth.
			try {
				var token = localStorage.getItem('authToken');
				if (!token) {
					window.location.replace('/login');
					return;
				}
				var payload = JSON.parse(atob(token.split('.')[1]));
				if (payload.exp * 1000 <= Date.now()) {
					window.location.replace('/login');
				}
			} catch (e) {
				window.location.replace('/login');
			}
		})();
	</script>
	<style>
		:root {
			--bgColorMenu: #1d1d27;
			--duration: 0.7s;
		}
	</style>
</svelte:head>

{#if pageState === 'loading'}
	<div class="mx-auto max-w-4xl space-y-6 p-4 pb-32">
		<div class="mb-8 text-center">
			<Skeleton class="mx-auto h-8 w-48" />
			<Skeleton class="mx-auto mt-2 h-4 w-64" />
		</div>
		<Card.Root>
			<Card.Content class="space-y-4 pt-6">
				{#each Array(5) as _, i (i)}
					<Skeleton class="h-12 w-full" />
				{/each}
			</Card.Content>
		</Card.Root>
	</div>
{:else if pageState === 'authorized'}
	<div class="mx-auto max-w-4xl space-y-6 p-4 pb-32">
		<!-- Header -->
		<div class="mb-8 text-center">
			<h1 class="font-['Playfair_Display'] text-4xl font-bold">My Account</h1>
			<p class="text-gray-600">Manage your profile, bookings, and settings</p>
		</div>

		<!-- Desktop Tab Menu (Show at top of content) -->
		<div class="desktop-tab-menu">
			<div class="flex rounded-lg border bg-gray-50 p-1">
				<button
					class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
					'general'
						? 'bg-white text-gray-900 shadow-sm'
						: 'text-gray-600 hover:text-gray-900'}"
					onclick={(_) => (activeTab = 'general')}
				>
					<svg
						class="mx-auto mb-1 h-4 w-4"
						viewBox="0 0 24 24"
						fill="none"
						stroke="currentColor"
						stroke-width="2"
					>
						<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
						<circle cx="12" cy="7" r="4" />
					</svg>
					General
				</button>
				{#if authStore.currentUser?.role !== 'admin'}
					<button
						class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
						'history'
							? 'bg-white text-gray-900 shadow-sm'
							: 'text-gray-600 hover:text-gray-900'}"
						onclick={(_) => (activeTab = 'history')}
					>
						<svg
							class="mx-auto mb-1 h-4 w-4"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
							<line x1="16" y1="2" x2="16" y2="6" />
							<line x1="8" y1="2" x2="8" y2="6" />
							<line x1="3" y1="10" x2="21" y2="10" />
						</svg>
						History
					</button>
				{/if}
				{#if authStore.currentUser?.role !== 'admin'}
					<button
						class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
						'referral'
							? 'bg-white text-gray-900 shadow-sm'
							: 'text-gray-600 hover:text-gray-900'}"
						onclick={(_) => (activeTab = 'referral')}
					>
						<svg
							class="mx-auto mb-1 h-4 w-4"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
							<circle cx="9" cy="7" r="4" />
							<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
							<path d="M16 3.13a4 4 0 0 1 0 7.75" />
						</svg>
						Referral
					</button>
				{/if}
				{#if canSaveCards}
					<button
						class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
						'cards'
							? 'bg-white text-gray-900 shadow-sm'
							: 'text-gray-600 hover:text-gray-900'}"
						onclick={(_) => {
							activeTab = 'cards';
							savedCardsStore.fetch();
							savedCardsStore.invalidate();
							fetchGiftCardBalance();
						}}
					>
						<svg
							class="mx-auto mb-1 h-4 w-4"
							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>
						Cards
					</button>
				{/if}
				<button
					class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
					'admin'
						? 'bg-white text-gray-900 shadow-sm'
						: 'text-gray-600 hover:text-gray-900'}"
					onclick={(_) => (activeTab = 'admin')}
				>
					<svg
						class="mx-auto mb-1 h-4 w-4"
						viewBox="0 0 24 24"
						fill="none"
						stroke="currentColor"
						stroke-width="2"
					>
						<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
						<path d="M7 11V7a5 5 0 0 1 10 0v4" />
					</svg>
					Admin
				</button>
			</div>
		</div>

		<!-- Tab Content -->
		<div class="tab-content">
			{#if activeTab === 'general'}
				<!-- General Details -->
				<Card.Root>
					<Card.Header>
						<Card.Title>Profile Information</Card.Title>
						<Card.Description>Your personal details and account information</Card.Description>
					</Card.Header>
					<Card.Content class="space-y-4">
						{#if userData}
							{@const initials =
								userData.firstName && userData.lastName
									? userData.firstName
											.split(' ')
											.map((n) => n[0])
											.join('') +
										userData.lastName
											.split(' ')
											.map((n) => n[0])
											.join('')
									: ''}
							{@const hasImage = !!userData.profilePicUrl || !!previewUrl}
							{@const displayUrl = previewUrl || userData.profilePicUrl || ''}
							<div class="flex flex-col items-center gap-4">
								{#if hasImage || initials}
									{#if hasImage}
										<img
											src={displayUrl}
											alt="Profile"
											class="h-24 w-24 rounded-full object-cover ring-4 ring-fuchsia-200"
										/>
									{:else}
										<div
											class="flex h-24 w-24 items-center justify-center rounded-full bg-gray-200 text-3xl font-bold text-gray-600 ring-4 ring-fuchsia-200"
										>
											{initials}
										</div>
									{/if}
								{:else}
									<div
										class="flex h-24 w-24 items-center justify-center rounded-full bg-gray-200 ring-4 ring-fuchsia-200"
									>
										<svg
											xmlns="http://www.w3.org/2000/svg"
											class="h-10 w-10 text-gray-400"
											fill="none"
											viewBox="0 0 24 24"
											stroke="currentColor"
										>
											<path
												stroke-linecap="round"
												stroke-linejoin="round"
												stroke-width="2"
												d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
											/>
										</svg>
									</div>
								{/if}
								<Button
									variant="outline"
									onclick={() => document.getElementById('profile-pic-input')?.click()}
								>
									Upload profile picture
								</Button>
								<input
									id="profile-pic-input"
									type="file"
									accept="image/*"
									class="hidden"
									onchange={handleFileSelect}
								/>
							</div>
						{/if}

						<Dialog.Root bind:open={cropDialogOpen}>
							<Dialog.Content class="max-w-lg">
								<Dialog.Header>
									<Dialog.Title>Crop Profile Picture</Dialog.Title>
								</Dialog.Header>
								<div class="relative h-64 w-full">
									{#if cropImageUrl}
										<Cropper
											image={cropImageUrl}
											aspect={1}
											cropShape="round"
											showGrid={false}
											bind:crop
											bind:zoom
											oncropcomplete={(e) => {
												cropArea = e.pixels;
											}}
										/>
									{/if}
								</div>
								<Dialog.Footer>
									<Button variant="outline" onclick={handleCropCancel}>Cancel</Button>
									<Button onclick={handleCropSave}>Save</Button>
								</Dialog.Footer>
							</Dialog.Content>
						</Dialog.Root>

						{#if loadingUser}
							{#each Array(6) as _, i (i)}
								<Skeleton class="h-12 w-full" />
							{/each}
						{:else if userData}
							<div class="grid gap-4 md:grid-cols-2">
								<div>
									<span class="text-sm font-medium text-gray-600">First Name</span>
									{#if editingFirstName}
										<div class="mt-1 space-y-2">
											<Input
												id="first-name"
												bind:value={firstNameInput}
												placeholder="Enter first name"
												maxlength={50}
												error={firstNameError}
											/>
											{#if firstNameError}
												<p class="text-xs text-red-500">{firstNameError}</p>
											{/if}
											<div class="flex gap-2">
												<Button
													size="sm"
													onclick={saveName}
													disabled={savingName || !firstNameInput.trim()}
												>
													{savingName ? 'Saving...' : 'Save'}
												</Button>
												<Button
													size="sm"
													variant="outline"
													onclick={cancelEditName}
													disabled={savingName}
												>
													Cancel
												</Button>
											</div>
										</div>
									{:else}
										<div
											class="mt-1 flex items-center justify-between rounded-lg border bg-gray-50 p-3 font-medium"
										>
											<span>{userData.firstName}</span>
											<Button size="sm" variant="ghost" onclick={startEditFirstName}>
												<svg
													xmlns="http://www.w3.org/2000/svg"
													class="h-4 w-4"
													viewBox="0 0 24 24"
													fill="none"
													stroke="currentColor"
													stroke-width="2"
												>
													<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
													<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
												</svg>
												Edit
											</Button>
										</div>
									{/if}
								</div>
								<div>
									<span class="text-sm font-medium text-gray-600">Last Name</span>
									{#if editingLastName}
										<div class="mt-1 space-y-2">
											<Input
												id="last-name"
												bind:value={lastNameInput}
												placeholder="Enter last name"
												maxlength={50}
												error={lastNameError}
											/>
											{#if lastNameError}
												<p class="text-xs text-red-500">{lastNameError}</p>
											{/if}
											<div class="flex gap-2">
												<Button
													size="sm"
													onclick={saveName}
													disabled={savingName || !lastNameInput.trim()}
												>
													{savingName ? 'Saving...' : 'Save'}
												</Button>
												<Button
													size="sm"
													variant="outline"
													onclick={cancelEditName}
													disabled={savingName}
												>
													Cancel
												</Button>
											</div>
										</div>
									{:else}
										<div
											class="mt-1 flex items-center justify-between rounded-lg border bg-gray-50 p-3 font-medium"
										>
											<span>{userData.lastName}</span>
											<Button size="sm" variant="ghost" onclick={startEditLastName}>
												<svg
													xmlns="http://www.w3.org/2000/svg"
													class="h-4 w-4"
													viewBox="0 0 24 24"
													fill="none"
													stroke="currentColor"
													stroke-width="2"
												>
													<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
													<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
												</svg>
												Edit
											</Button>
										</div>
									{/if}
								</div>
								<div>
									<span class="text-sm font-medium text-gray-600">Email</span>
									<div class="mt-1 rounded-lg border bg-gray-50 p-3 font-medium">
										{userData.email}
									</div>
								</div>
								<div>
									<span class="text-sm font-medium text-gray-600">Phone</span>
									{#if editingPhone}
										<div class="mt-1 space-y-2">
											<PhoneInput
												id="phone"
												bind:value={phoneInput}
												bind:error={phoneError}
												placeholder="Enter phone number"
											/>
											<div class="flex gap-2">
												<Button
													size="sm"
													onclick={savePhone}
													disabled={savingPhone || !isValidUKPhone(phoneInput)}
												>
													{savingPhone ? 'Saving...' : 'Save'}
												</Button>
												<Button
													size="sm"
													variant="outline"
													onclick={cancelEditPhone}
													disabled={savingPhone}
												>
													Cancel
												</Button>
											</div>
										</div>
									{:else}
										<div
											class="mt-1 flex items-center justify-between rounded-lg border bg-gray-50 p-3 font-medium"
										>
											<span>{userData.phone || '—'}</span>
											<Button size="sm" variant="ghost" onclick={startEditPhone}>
												<svg
													xmlns="http://www.w3.org/2000/svg"
													class="h-4 w-4"
													viewBox="0 0 24 24"
													fill="none"
													stroke="currentColor"
													stroke-width="2"
												>
													<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
													<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
												</svg>
												Edit
											</Button>
										</div>
									{/if}
								</div>
							</div>

							{#if authStore.currentUser?.role !== 'admin'}
								<Separator class="my-4" />

								{#if stamps >= 10}
									<div
										class="mb-3 rounded-xl border-2 border-emerald-200 bg-emerald-50 p-4 text-center"
									>
										<p class="text-sm font-bold tracking-wide text-emerald-800">
											Card Completed — 10% Off Your Next Booking!
										</p>
									</div>
								{/if}

								<div class="rounded-xl border-2 border-fuchsia-200 bg-fuchsia-50 p-5 sm:p-6">
									{#if userData}
										<div class="mb-5 text-center sm:text-left">
											<h3 class="text-sm font-semibold text-gray-900">Loyalty Stamp Card</h3>
											<p class="mt-0.5 text-xs text-gray-500">
												{stamps < 10
													? `Collect ${10 - stamps} more stamp${10 - stamps === 1 ? '' : 's'} to get 10% off your next booking.`
													: (() => {
															const fc = Math.floor(stamps / 10);
															return `You have ${fc === 1 ? 'a' : fc} full stampcard${fc === 1 ? '' : 's'} ready to take advantage of at your next booking!`;
														})()}
											</p>
										</div>

										<div class="grid grid-cols-5 gap-3 sm:grid-cols-10">
											{#each Array(10) as _, i (i)}
												{@const slotNum = i + 1}
												{@const rot = ((slotNum * 37 + 13) % 7) - 3}
												{#if slotNum <= (stamps > 0 ? stamps % 10 || 10 : 0)}
													<div
														class="aspect-square transition-transform duration-200 hover:scale-110"
													>
														<div
															class="relative flex h-full w-full items-center justify-center text-fuchsia-300"
															style="transform: rotate({rot}deg)"
														>
															<svg class="absolute inset-0 h-full w-full" viewBox="0 0 100 100">
																<defs>
																	<mask id="stamp-mask-{slotNum}">
																		<path d={getStampPath(slotNum)} fill="white" />
																		<path
																			d="M 50 25 L 56 43 L 75 43 L 60 53.5 L 66 71.5 L 50 62 L 34 71.5 L 40 53.5 L 25 43 L 44 43 Z"
																			fill="black"
																		/>
																	</mask>
																</defs>
																<path
																	d={getStampPath(slotNum)}
																	fill="currentColor"
																	mask="url(#stamp-mask-{slotNum})"
																/>
															</svg>
														</div>
													</div>
												{:else}
													<div
														class="aspect-square transition-transform duration-200 hover:scale-110"
													>
														<div
															class="relative flex h-full w-full items-center justify-center text-fuchsia-300/40 transition-colors duration-200 hover:text-fuchsia-400/60"
															style="transform: rotate({rot}deg)"
														>
															<svg class="absolute inset-0 h-full w-full" viewBox="0 0 100 100">
																<path
																	d={getStampPath(slotNum)}
																	fill="none"
																	stroke="currentColor"
																	stroke-width="2"
																	stroke-dasharray="3 3"
																/>
															</svg>
															<span
																class="relative z-10 text-[10px] leading-none font-semibold text-fuchsia-400/50"
																>{slotNum}</span
															>
														</div>
													</div>
												{/if}
											{/each}
										</div>
									{/if}
								</div>
							{/if}
						{/if}
					</Card.Content>
				</Card.Root>
			{:else if activeTab === 'history'}
				<!-- Upcoming Bookings -->
				{#if upcomingBookings.length > 0}
					<Card.Root class="mb-6">
						<Card.Header>
							<Card.Title>Upcoming Appointments</Card.Title>
							<Card.Description
								>Next {upcomingBookings.length < 3 ? upcomingBookings.length : 3} upcoming bookings</Card.Description
							>
						</Card.Header>
						<Card.Content class="space-y-2">
							{#if loadingUpcoming}
								{#each Array(3) as _, i (i)}
									<Skeleton class="h-16 w-full" />
								{/each}
							{:else}
								{#each upcomingBookings as b (b.id)}
									<div class="flex items-center justify-between rounded bg-gray-50 p-2">
										<div class="flex-1">
											<div class="font-medium">{formatDateTime(b.start_time)}</div>
											<div class="mt-1 flex items-center gap-2 text-xs text-gray-500">
												<!-- Show Status Chip for Upcoming -->
												<span
													class="inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium {b.status ===
													'confirmed'
														? 'bg-emerald-100 text-emerald-800'
														: b.status === 'pending'
															? 'bg-amber-100 text-amber-800'
															: b.status === 'in_progress'
																? 'bg-blue-100 text-blue-800'
																: 'bg-gray-100 text-gray-800'}"
												>
													{b.status}
												</span>

												<!-- Services: Only show if data exists -->
												{#if b.services && b.services.length > 0}
													<span>
														- {(() => {
															const services = b.services.map(
																(s) => s.service_name || 'Unknown Service'
															);
															if (services.length === 1) return services[0];
															if (services.length === 2) return services.join(' and ');
															return `${services[0]} and ${services.length - 1} other${services.length - 1 > 1 ? 's' : ''}`;
														})()}
													</span>
												{/if}
											</div>
										</div>
										<Button variant="outline" onclick={() => openBookingModal(b.id)}>View</Button>
									</div>
								{/each}
							{/if}
						</Card.Content>
					</Card.Root>
				{/if}

				<!-- Past Bookings -->
				<Card.Root>
					<Card.Header>
						<Card.Title>Past Appointments</Card.Title>
						<Card.Description>Previous bookings</Card.Description>
					</Card.Header>
					<Card.Content class="space-y-2">
						{#if loadingPast}
							{#each Array(5) as _, i (i)}
								<Skeleton class="h-16 w-full" />
							{/each}
						{:else if pastBookings.length === 0}
							<div class="py-4 text-center text-gray-500">No past bookings</div>
						{:else}
							{#each pastBookings as b (b.id)}
								<div class="flex items-center justify-between rounded bg-gray-50 p-2">
									<div class="flex-1">
										<div class="font-medium">{formatDateTime(b.start_time)}</div>
										<div class="mt-1 flex items-center gap-2 text-xs text-gray-500">
											<!-- Unpaid Chip: Matches the 'Confirmed' chip style but uses Red for urgency -->
											{#if (b.amount_due || 0) > 0}
												<span
													class="inline-flex items-center rounded-full bg-red-100 px-2 py-0.5 text-xs font-medium text-red-800"
												>
													Unpaid
												</span>
											{/if}

											<!-- Services: Hidden if empty -->
											{#if b.services && b.services.length > 0}
												<span>
													- {(() => {
														const services = b.services.map(
															(s) => s.service_name || 'Unknown Service'
														);
														if (services.length === 1) return services[0];
														if (services.length === 2) return services.join(' and ');
														return `${services[0]} and ${services.length - 1} other${services.length - 1 > 1 ? 's' : ''}`;
													})()}
												</span>
											{/if}

											{#if b.total_amount}
												<span class="font-medium text-gray-700">
													— £{b.total_amount.toFixed(2)}
												</span>
											{/if}
										</div>
									</div>
									<Button variant="outline" onclick={() => openBookingModal(b.id)}>View</Button>
								</div>
							{/each}
						{/if}

						<!-- Pagination Controls -->
						{#if pastTotalPages > 1}
							<div class="mt-2 flex justify-center gap-2">
								<Button
									variant="outline"
									size="sm"
									disabled={pastPage === 1}
									onclick={() => goToPastPage(pastPage - 1)}>Prev</Button
								>
								<span class="px-2 py-1 text-sm text-gray-700">{pastPage} / {pastTotalPages}</span>
								<Button
									variant="outline"
									size="sm"
									disabled={pastPage === pastTotalPages}
									onclick={() => goToPastPage(pastPage + 1)}>Next</Button
								>
							</div>
						{/if}
					</Card.Content>
				</Card.Root>
			{:else if activeTab === 'referral'}
				<!-- Referral Program -->
				<Card.Root>
					<Card.Header>
						<Card.Title>Referral Program</Card.Title>
						<Card.Description>Share your code and earn rewards</Card.Description>
					</Card.Header>
					<Card.Content class="space-y-4">
						{#if loadingUser}
							<Skeleton class="h-32 w-full" />
						{:else if userData?.referralCode}
							<div class="rounded-lg border p-6 text-center">
								<div class="mb-3 text-sm font-medium text-muted-foreground">Your Referral Code</div>

								<div class="mb-4 flex items-center justify-center">
									{#each userData.referralCode.match(/.{1,4}/g) as part, i (i)}<span
											class="inline-flex min-w-[3.5rem] items-center justify-center border-b-2 border-b-border px-1 pb-1 text-2xl font-bold tracking-widest text-foreground sm:min-w-[5rem] sm:text-4xl"
											>{part}</span
										>{#if i < 2}<span
												class="mx-1 text-xl font-bold text-muted-foreground select-none sm:mx-2 sm:text-3xl"
												aria-hidden="true">–</span
											>{/if}{/each}
								</div>

								<Button onclick={copyReferralCode} variant="outline" class="w-full">
									<svg
										xmlns="http://www.w3.org/2000/svg"
										class="mr-2 h-4 w-4"
										viewBox="0 0 24 24"
										fill="none"
										stroke="currentColor"
										stroke-width="2"
									>
										<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
										<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
									</svg>
									Copy Code
								</Button>
							</div>

							<div class="grid grid-cols-2 gap-4">
								<div class="rounded-lg border p-4 text-center">
									<div class="text-3xl font-bold">
										{userData.referralCodeUses || 0}
									</div>
									<div class="text-sm">Friends Referred</div>
								</div>
								<div class="rounded-lg border p-4 text-center">
									<div class="text-3xl font-bold">
										£{(userData.referralSavings || 0).toFixed(2)}
									</div>
									<div class="text-sm">Total Saved</div>
								</div>
							</div>

							<Card.Root class="mt-6 border-amber-200/60 bg-amber-50">
								<Card.Content class="pt-4 md:pt-6">
									<div class="space-y-2 text-sm text-amber-900">
										<h4 class="font-semibold text-amber-800">How it works:</h4>
										<ul class="space-y-1 pl-4">
											<li>• Share your referral code with friends</li>
											<li>• They get 10% off their first booking</li>
											<li>• You get 10% off your next booking after they claim</li>
											<li>• You earn 3 loyalty stamp for each use to keep the savings going</li>
										</ul>
									</div>
								</Card.Content>
							</Card.Root>
						{:else}
							<div class="py-8 text-center text-gray-500">No referral code available</div>
						{/if}
					</Card.Content>
				</Card.Root>
			{:else if activeTab === 'cards'}
				<!-- Saved Cards -->
				<Card.Root>
					<Card.Header>
						<Card.Title>Saved Cards</Card.Title>
						<Card.Description>Manage your saved payment methods</Card.Description>
					</Card.Header>
					<Card.Content>
						{#if loadingCards}
							<div class="space-y-3">
								<Skeleton class="h-16 w-full" />
								<Skeleton class="h-16 w-full" />
							</div>
						{:else if showAddCard}
							<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">Add New Card</h4>
									<div class="space-y-3">
										<div>
											<label for="account-cardNumber" class="text-sm font-medium text-gray-700"
												>Card Number</label
											>
											<Input
												id="account-cardNumber"
												type="text"
												inputmode="numeric"
												value={newCardNumber}
												oninput={handleCardNumberInput}
												placeholder="1234 5678 9012 3456"
												maxlength={19}
												class="mt-1"
											/>
										</div>
										<div class="grid grid-cols-2 gap-3">
											<div>
												<label for="account-cardExpiry" class="text-sm font-medium text-gray-700"
													>Expiry (MM/YY)</label
												>
												<Input
													id="account-cardExpiry"
													type="text"
													inputmode="numeric"
													value={newCardExpiry}
													oninput={handleExpiryInput}
													placeholder="MM/YY"
													maxlength={5}
													class="mt-1"
												/>
											</div>
											<div>
												<label for="account-cardCVC" class="text-sm font-medium text-gray-700"
													>CVC</label
												>
												<Input
													id="account-cardCVC"
													type="text"
													inputmode="numeric"
													value={newCardCVC}
													oninput={handleCvcInput}
													placeholder="123"
													maxlength={4}
													class="mt-1"
												/>
											</div>
										</div>
										{#if addCardError}
											<div class="mt-1 text-xs font-semibold text-red-500">{addCardError}</div>
										{/if}
									</div>
								</div>
								<div class="flex gap-3">
									<Button
										variant="ghost"
										onclick={() => {
											showAddCard = false;
											newCardNumber = '';
											newCardExpiry = '';
											newCardCVC = '';
										}}
									>
										Cancel
									</Button>
									<Button
										onclick={addCard}
										loading={addingCard}
										disabled={addingCard || !isAddCardValid}
									>
										Add Card
									</Button>
								</div>
							</div>
						{:else if savedCardsStore.cards.length === 0}
							<div class="py-8 text-center">
								<p class="text-gray-500">No saved cards yet</p>
								<Button class="mt-4" onclick={() => (showAddCard = true)}>Add a Card</Button>
							</div>
						{:else}
							<div class="space-y-3">
								{#each savedCardsStore.cards as card (card.id)}
									<div class="flex items-center justify-between rounded-lg border p-4">
										<div class="flex items-center gap-3">
											<div
												class="flex h-10 w-14 items-center justify-center rounded bg-gray-100 text-xs font-medium"
											>
												{card.brand}
											</div>
											<div>
												<div class="text-sm font-medium">
													**** {card.last_4}
												</div>
												<div class="text-xs text-gray-500">
													Expires {String(card.exp_month).padStart(2, '0')}/{card.exp_year}
												</div>
											</div>
										</div>
										<Button
											size="sm"
											variant="ghost"
											class="text-red-600 hover:bg-red-50 hover:text-red-700"
											onclick={() => {
												cardToDelete = card;
												showDeleteCardDialog = true;
											}}
										>
											Remove
										</Button>
									</div>
								{/each}
								<Button variant="outline" class="w-full" onclick={() => (showAddCard = true)}>
									+ Add a Card
								</Button>
							</div>
						{/if}
					</Card.Content>
				</Card.Root>

				<!-- Redeem & Buy Gift Cards -->
				<div class="mt-6 grid gap-6 md:grid-cols-2">
					<!-- Redeem Gift Card -->
					<Card.Root>
						<Card.Header>
							<Card.Title class="flex items-center gap-2">
								<svg
									xmlns="http://www.w3.org/2000/svg"
									class="h-5 w-5"
									fill="none"
									viewBox="0 0 24 24"
									stroke="currentColor"
									stroke-width="2"
								>
									<path
										stroke-linecap="round"
										stroke-linejoin="round"
										d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7"
									/>
								</svg>
								Redeem Gift Card
							</Card.Title>
							<Card.Description
								>Redeem a gift card directly to your account balance.</Card.Description
							>
						</Card.Header>
						<Card.Content class="space-y-4">
							<div class="flex items-center justify-between rounded-lg bg-accent p-4">
								<div>
									<div class="text-xs font-semibold tracking-wider text-muted-foreground uppercase">
										Your Balance
									</div>
									<div class="mt-1 text-2xl font-bold text-card-foreground">
										{loadingBalance ? '...' : formatCurrency(giftCardBalance)}
									</div>
								</div>
							</div>

							<div class="space-y-2">
								<label for="redeem-code" class="text-sm font-medium text-gray-700"
									>Enter Gift Card Code</label
								>
								<div class="flex gap-2">
									<Input
										id="redeem-code"
										type="text"
										placeholder="xxxx-xxxx-xxxx"
										maxlength={14}
										value={giftCardCode}
										oninput={handleGiftCardInput}
										class="font-mono"
									/>
									<Button
										onclick={() => (showRedeemConfirm = true)}
										disabled={redeemingGiftCard ||
											giftCardCode.replace(/[^a-zA-Z0-9]/g, '').length !== 12}
									>
										{redeemingGiftCard ? 'Redeeming...' : 'Redeem'}
									</Button>
								</div>
							</div>
						</Card.Content>
					</Card.Root>

					<AlertDialog.Root bind:open={showRedeemConfirm}>
						<AlertDialog.Content>
							<AlertDialog.Header>
								<AlertDialog.Title>Redeem Gift Card</AlertDialog.Title>
								<AlertDialog.Description>
									Claiming this gift card will add its remaining balance directly to your account
									balance, which can be used toward future bookings.
								</AlertDialog.Description>
							</AlertDialog.Header>
							<div class="space-y-3 px-6 py-4 text-sm text-muted-foreground">
								<div class="space-y-2 rounded-lg border bg-amber-50/50 p-3">
									<p>
										<strong class="text-foreground">What happens when I claim?</strong>
									</p>
									<ul class="list-disc space-y-1 pl-4">
										<li>The gift card value is added to your account balance.</li>
										<li>
											Account balances do not expire, but gift card codes become invalid once
											redeemed.
										</li>
										<li>This action is final and cannot be reversed.</li>
									</ul>
								</div>
								<div class="space-y-2 rounded-lg border bg-blue-50/50 p-3">
									<p>
										<strong class="text-foreground">Legal &amp; GDPR Information</strong>
									</p>
									<ul class="list-disc space-y-1 pl-4">
										<li>
											Your personal data (name, email, transaction history) is processed in
											accordance with UK data protection law.
										</li>
										<li>
											Financial records are retained for 7 years as required by HMRC, after which
											personally identifiable information is anonymised.
										</li>
										<li>
											You can request a full copy of your data or deletion of your account at any
											time via your account settings.
										</li>
									</ul>
								</div>
								<!-- TODO: Link full Terms & Conditions once the T&Cs page is created -->
								<p class="text-xs text-muted-foreground italic">
									By redeeming this gift card, you agree to our Terms &amp; Conditions.
									<em>Link T&amp;Cs here once available.</em>
								</p>
							</div>
							<AlertDialog.Footer>
								<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
								<AlertDialog.Action onclick={redeemGiftCard}
									>Confirm &amp; Redeem</AlertDialog.Action
								>
							</AlertDialog.Footer>
						</AlertDialog.Content>
					</AlertDialog.Root>

					<!-- Buy Gift Card -->
					<Card.Root>
						<Card.Header>
							<Card.Title class="flex items-center gap-2">
								<svg
									xmlns="http://www.w3.org/2000/svg"
									class="h-5 w-5"
									fill="none"
									viewBox="0 0 24 24"
									stroke="currentColor"
									stroke-width="2"
								>
									<rect x="2" y="5" width="20" height="14" rx="2" ry="2" />
									<line x1="2" y1="10" x2="22" y2="10" />
								</svg>
								Buy a Gift Card
							</Card.Title>
							<Card.Description
								>Purchase a gift card online for yourself or a friend.</Card.Description
							>
						</Card.Header>
						<Card.Content class="space-y-4">
							{#if purchaseResultCode}
								<div class="space-y-3 rounded-lg border border-green-100 bg-green-50 p-4">
									<div class="flex items-center gap-2 text-sm font-medium text-green-800">
										<svg
											xmlns="http://www.w3.org/2000/svg"
											class="h-5 w-5 text-green-600"
											viewBox="0 0 20 20"
											fill="currentColor"
										>
											<path
												fill-rule="evenodd"
												d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
												clip-rule="evenodd"
											/>
										</svg>
										Purchase Successful!
									</div>
									{#if buyRecipientType === 'self'}
										<p class="text-xs text-green-700">
											Your purchase of <strong>{formatCurrency(buyAmount)}</strong> has been automatically
											added to your account balance!
										</p>
									{:else}
										<p class="text-xs text-green-700">Here is your gift card code:</p>
										<div
											class="rounded border border-green-200 bg-white py-2 text-center font-mono text-lg font-bold tracking-wider text-green-800"
										>
											{formatCardCode(purchaseResultCode)}
										</div>
										<p class="text-[10px] text-green-600 italic">
											Please save this code! It has been emailed to the recipient.
										</p>
									{/if}
									<Button
										size="sm"
										variant="outline"
										onclick={() => (purchaseResultCode = null)}
										class="w-full"
									>
										Buy Another Card
									</Button>
								</div>
							{:else}
								<div class="space-y-2">
									<span class="block text-xs font-semibold tracking-wider text-gray-500 uppercase"
										>Select Value</span
									>
									<div class="grid grid-cols-3 gap-2">
										{#each [10, 20, 50] as amount (amount)}
											<button
												type="button"
												class="rounded-lg border py-2.5 text-center text-sm font-semibold transition-colors {buyAmount ===
												amount
													? 'border-input bg-accent text-card-foreground'
													: 'border-gray-200 hover:bg-gray-50'}"
												onclick={() => (buyAmount = amount as 10 | 20 | 50)}
											>
												{formatCurrency(amount)}
											</button>
										{/each}
									</div>
								</div>

								<div class="space-y-2">
									<span class="block text-xs font-semibold tracking-wider text-gray-500 uppercase"
										>Recipient</span
									>
									<div class="grid grid-cols-2 gap-2">
										<button
											type="button"
											class="rounded-lg border py-2 text-center text-xs font-medium transition-colors {buyRecipientType ===
											'self'
												? 'border-input bg-accent text-card-foreground'
												: 'border-gray-200 hover:bg-gray-50'}"
											onclick={() => (buyRecipientType = 'self')}
										>
											For Myself (Auto-Redeem)
										</button>
										<button
											type="button"
											class="rounded-lg border py-2 text-center text-xs font-medium transition-colors {buyRecipientType ===
											'friend'
												? 'border-input bg-accent text-card-foreground'
												: 'border-gray-200 hover:bg-gray-50'}"
											onclick={() => (buyRecipientType = 'friend')}
										>
											For a Friend (Gift Code)
										</button>
									</div>
								</div>

								{#if buyRecipientType === 'friend'}
									<div class="space-y-2">
										<label for="recipient-email" class="text-sm font-medium text-gray-700"
											>Friend's Email (Optional)</label
										>
										<EmailInput
											id="recipient-email"
											bind:value={buyRecipientEmail}
											placeholder="friend@example.com (blank to send to yourself)"
											class="mt-1"
										/>
									</div>
								{/if}

								<div class="space-y-3 border-t pt-2">
									<span class="block text-xs font-semibold tracking-wider text-gray-500 uppercase"
										>Payment Method</span
									>
									{#if savedCardsStore.cards.length > 0}
										<div class="space-y-2">
											{#each savedCardsStore.cards as card (card.id)}
												<button
													type="button"
													class="flex w-full items-center justify-between rounded-lg border p-3 text-left {buySelectedCard ===
													card.id
														? 'border-input bg-accent'
														: 'border-gray-200 hover:bg-gray-50'}"
													onclick={() => {
														buySelectedCard = card.id;
													}}
												>
													<div class="flex items-center gap-3">
														<div
															class="flex h-8 min-w-12 items-center justify-center rounded bg-gray-100 px-2 text-xs font-medium text-gray-700 uppercase"
														>
															{card.brand}
														</div>
														<div class="text-sm">
															<span class="font-mono">**** {card.last_4}</span>
															<span class="ml-2 text-xs text-gray-400"
																>Exp {String(card.exp_month).padStart(2, '0')}/{card.exp_year}</span
															>
														</div>
													</div>
													{#if buySelectedCard === card.id}
														<span class="text-xs font-semibold text-primary">Selected</span>
													{/if}
												</button>
											{/each}
											<button
												type="button"
												class="flex w-full items-center justify-between rounded-lg border p-3 text-left {buySelectedCard ===
												''
													? 'border-input bg-accent'
													: 'border-gray-200 hover:bg-gray-50'}"
												onclick={() => {
													buySelectedCard = '';
												}}
											>
												<div class="flex items-center gap-3">
													<div
														class="flex h-8 min-w-12 items-center justify-center rounded border border-dashed border-gray-300 text-xs font-medium text-gray-400"
													>
														NEW
													</div>
													<span class="animate-pulse text-sm font-medium text-gray-700"
														>Use a new card</span
													>
												</div>
												{#if buySelectedCard === ''}
													<span class="text-xs font-semibold text-primary">Selected</span>
												{/if}
											</button>
										</div>
									{/if}

									{#if buySelectedCard === ''}
										<div class="space-y-3 rounded-lg border bg-gray-50 p-3">
											<div>
												<label for="buy-card-num" class="text-xs font-medium text-gray-600"
													>Card Number</label
												>
												<Input
													id="buy-card-num"
													type="text"
													inputmode="numeric"
													placeholder="1234 5678 9012 3456"
													value={buyNewCardNumber}
													oninput={handleBuyCardNumberInput}
													maxlength={19}
													class="mt-1 h-8 bg-white text-xs"
												/>
											</div>
											<div class="grid grid-cols-2 gap-3">
												<div>
													<label for="buy-card-exp" class="text-xs font-medium text-gray-600"
														>Expiry (MM/YY)</label
													>
													<Input
														id="buy-card-exp"
														type="text"
														inputmode="numeric"
														placeholder="MM/YY"
														value={buyNewCardExpiry}
														oninput={handleBuyExpiryInput}
														maxlength={5}
														class="mt-1 h-8 bg-white text-xs"
													/>
												</div>
												<div>
													<label for="buy-card-cvc" class="text-xs font-medium text-gray-600"
														>CVC</label
													>
													<Input
														id="buy-card-cvc"
														type="text"
														inputmode="numeric"
														placeholder="123"
														value={buyNewCardCVC}
														oninput={handleBuyCvcInput}
														maxlength={4}
														class="mt-1 h-8 bg-white text-xs"
													/>
												</div>
											</div>
											{#if buyCardError}
												<div class="mt-1 text-[10px] font-semibold text-red-500">
													{buyCardError}
												</div>
											{/if}
											<div class="flex items-center gap-2 pt-1">
												<Checkbox id="buy-save-card" bind:checked={buySaveCard} />
												<label for="buy-save-card" class="text-[10px] text-gray-500"
													>Save card for future purchases</label
												>
											</div>
										</div>
									{/if}
								</div>

								<Button
									onclick={buyGiftCard}
									disabled={buyingGiftCard || !isBuyCardValid}
									class="mt-2 w-full"
								>
									{buyingGiftCard ? 'Processing Payment...' : `Pay ${formatCurrency(buyAmount)}`}
								</Button>
							{/if}
						</Card.Content>
					</Card.Root>
				</div>
			{:else if activeTab === 'admin'}
				<!-- Admin Settings -->
				<Card.Root>
					<Card.Header>
						<Card.Title>Account Settings</Card.Title>
						<Card.Description>Manage your security and account preferences</Card.Description>
					</Card.Header>
					<Card.Content class="space-y-6">
						<!-- Change Password -->
						<div>
							<h3 class="mb-2 text-sm font-semibold">Password</h3>
							<p class="mb-3 text-sm text-gray-600">
								Update your password to keep your account secure
							</p>
							<Button onclick={() => (showPasswordModal = true)} variant="outline">
								<svg
									xmlns="http://www.w3.org/2000/svg"
									class="mr-2 h-4 w-4"
									viewBox="0 0 24 24"
									fill="none"
									stroke="currentColor"
									stroke-width="2"
								>
									<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
									<path d="M7 11V7a5 5 0 0 1 10 0v4" />
								</svg>
								Change Password
							</Button>
						</div>

						<Separator />

						<!-- Log Out Button -->
						<div>
							<h3 class="mb-2 text-sm font-semibold">Session</h3>
							<p class="mb-3 text-sm text-gray-600">Log out of this account on this device.</p>
							<Button onclick={() => authStore.logout()} variant="outline">
								<svg
									xmlns="http://www.w3.org/2000/svg"
									class="mr-2 h-4 w-4"
									viewBox="0 0 24 24"
									fill="none"
									stroke="currentColor"
									stroke-width="2"
								>
									<path d="M17 8l4 4-4 4" />
									<path d="M3 12h18" />
								</svg>
								Log Out
							</Button>
						</div>

						<Separator />

						<!-- Export My Data -->
						<div>
							<h3 class="mb-2 text-sm font-semibold">Data Privacy</h3>
							<p class="mb-3 text-sm text-gray-600">
								View and export all personal data we hold about you
							</p>
							<!-- eslint-disable-next-line svelte/no-navigation-without-resolve -->
							<Button onclick={() => goto('/gdpr')} variant="outline">
								<svg
									xmlns="http://www.w3.org/2000/svg"
									class="mr-2 h-4 w-4"
									viewBox="0 0 24 24"
									fill="none"
									stroke="currentColor"
									stroke-width="2"
								>
									<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
									<polyline points="7 10 12 15 17 10" />
									<line x1="12" y1="15" x2="12" y2="3" />
								</svg>
								Export My Data
							</Button>
							{#if gdprCountdown}
								<p class="mt-2 text-xs text-gray-500">
									{gdprCountdown}
								</p>
							{/if}
						</div>

						<Separator />

						<!-- Notification Preferences (non-admin users only) -->
						{#if authStore.currentUser?.role !== 'admin'}
							<div>
								<h3 class="mb-2 text-sm font-semibold">Notifications</h3>
								<p class="mb-3 text-sm text-gray-600">
									Choose how you receive booking reminders and updates
								</p>
								<div class="space-y-3">
									<div class="flex items-center justify-between rounded-lg border p-3">
										<div>
											<div class="text-sm font-medium">Email</div>
											<div class="text-xs text-gray-500">Booking confirmations and reminders</div>
										</div>
										<label class="relative inline-flex cursor-pointer items-center">
											<input
												type="checkbox"
												class="peer sr-only"
												checked={notifPrefs.emailEnabled}
												onchange={async () => {
													notifPrefs.emailEnabled = !notifPrefs.emailEnabled;
													await saveNotifPrefs();
												}}
											/>
											<div
												class="peer h-5 w-9 rounded-full border border-gray-200 bg-gray-200 peer-checked:bg-fuchsia-300 after:absolute after:start-[2px] after:top-[2px] after:h-4 after:w-4 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:after:translate-x-full peer-checked:after:border-white"
											></div>
										</label>
									</div>

									<div class="flex items-center justify-between rounded-lg border p-3">
										<div>
											<div class="text-sm font-medium">SMS</div>
											<div class="text-xs text-gray-500">Text message reminders</div>
										</div>
										<label class="relative inline-flex cursor-pointer items-center">
											<input
												type="checkbox"
												class="peer sr-only"
												checked={notifPrefs.smsEnabled}
												onchange={async () => {
													notifPrefs.smsEnabled = !notifPrefs.smsEnabled;
													await saveNotifPrefs();
												}}
											/>
											<div
												class="peer h-5 w-9 rounded-full border border-gray-200 bg-gray-200 peer-checked:bg-fuchsia-300 after:absolute after:start-[2px] after:top-[2px] after:h-4 after:w-4 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:after:translate-x-full peer-checked:after:border-white"
											></div>
										</label>
									</div>

									<div class="flex items-center justify-between rounded-lg border p-3">
										<div>
											<div class="text-sm font-medium">Browser</div>
											<div class="text-xs text-gray-500">In-browser notifications</div>
										</div>
										<label class="relative inline-flex cursor-pointer items-center">
											<input
												type="checkbox"
												class="peer sr-only"
												checked={notifPrefs.browserPushEnabled}
												onchange={async () => {
													notifPrefs.browserPushEnabled = !notifPrefs.browserPushEnabled;
													await saveNotifPrefs();
												}}
											/>
											<div
												class="peer h-5 w-9 rounded-full border border-gray-200 bg-gray-200 peer-checked:bg-fuchsia-300 after:absolute after:start-[2px] after:top-[2px] after:h-4 after:w-4 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:after:translate-x-full peer-checked:after:border-white"
											></div>
										</label>
									</div>
								</div>
							</div>
							<Separator />
						{/if}

						<div>
							<h3 class="mb-2 text-sm font-semibold">Policies</h3>
							<p class="mb-3 text-sm text-gray-600">
								View our cancellation, deposit, and no-show policies
							</p>
							<PolicyPopover>
								{#snippet trigger()}
									<Button variant="outline">
										<svg
											xmlns="http://www.w3.org/2000/svg"
											class="mr-2 h-4 w-4"
											viewBox="0 0 24 24"
											fill="none"
											stroke="currentColor"
											stroke-width="2"
											><path
												d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
											/><polyline points="14 2 14 8 20 8" /><line
												x1="16"
												y1="13"
												x2="8"
												y2="13"
											/><line x1="16" y1="17" x2="8" y2="17" /></svg
										>
										Cancellation & Deposit Policy
									</Button>
								{/snippet}
							</PolicyPopover>
						</div>

						<Separator />

						{#if authStore.currentUser?.role !== 'admin'}
							<!-- Delete Account -->
							<div>
								<h3 class="mb-2 text-sm font-semibold text-red-600">Danger Zone</h3>
								<p class="mb-3 text-sm text-gray-600">
									Once you delete your account, there is no going back. Please be certain.
								</p>
								<Button onclick={() => (showDeleteAlert = true)} variant="destructive">
									<svg
										xmlns="http://www.w3.org/2000/svg"
										class="mr-2 h-4 w-4"
										viewBox="0 0 24 24"
										fill="none"
										stroke="currentColor"
										stroke-width="2"
									>
										<polyline points="3 6 5 6 21 6" />
										<path
											d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
										/>
									</svg>
									Delete Account
								</Button>
							</div>
						{/if}
					</Card.Content>
				</Card.Root>
			{/if}
		</div>

		<!-- Mobile Tab Menu (Fixed at bottom) -->
		<div class="mobile-tab-menu">
			<div class="flex rounded-lg border bg-gray-50 p-1">
				<button
					class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
					'general'
						? 'bg-white text-gray-900 shadow-sm'
						: 'text-gray-600 hover:text-gray-900'}"
					onclick={(_) => (activeTab = 'general')}
				>
					<svg
						class="mx-auto mb-1 h-4 w-4"
						viewBox="0 0 24 24"
						fill="none"
						stroke="currentColor"
						stroke-width="2"
					>
						<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
						<circle cx="12" cy="7" r="4" />
					</svg>
					General
				</button>

				{#if authStore.currentUser?.role !== 'admin'}
					<button
						class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
						'history'
							? 'bg-white text-gray-900 shadow-sm'
							: 'text-gray-600 hover:text-gray-900'}"
						onclick={(_) => (activeTab = 'history')}
					>
						<svg
							class="mx-auto mb-1 h-4 w-4"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
							<line x1="16" y1="2" x2="16" y2="6" />
							<line x1="8" y1="2" x2="8" y2="6" />
							<line x1="3" y1="10" x2="21" y2="10" />
						</svg>
						History
					</button>
				{/if}

				{#if authStore.currentUser?.role !== 'admin'}
					<button
						class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
						'referral'
							? 'bg-white text-gray-900 shadow-sm'
							: 'text-gray-600 hover:text-gray-900'}"
						onclick={(_) => (activeTab = 'referral')}
					>
						<svg
							class="mx-auto mb-1 h-4 w-4"
							viewBox="0 0 24 24"
							fill="none"
							stroke="currentColor"
							stroke-width="2"
						>
							<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
							<circle cx="9" cy="7" r="4" />
							<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
							<path d="M16 3.13a4 4 0 0 1 0 7.75" />
						</svg>
						Referral
					</button>
				{/if}

				{#if canSaveCards}
					<button
						class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
						'cards'
							? 'bg-white text-gray-900 shadow-sm'
							: 'text-gray-600 hover:text-gray-900'}"
						onclick={(_) => {
							activeTab = 'cards';
							savedCardsStore.fetch();
							savedCardsStore.invalidate();
							fetchGiftCardBalance();
						}}
					>
						<svg
							class="mx-auto mb-1 h-4 w-4"
							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>
						Cards
					</button>
				{/if}

				<button
					class="flex-1 rounded-md px-2 py-2 text-xs font-medium transition-colors {activeTab ===
					'admin'
						? 'bg-white text-gray-900 shadow-sm'
						: 'text-gray-600 hover:text-gray-900'}"
					onclick={(_) => (activeTab = 'admin')}
				>
					<svg
						class="mx-auto mb-1 h-4 w-4"
						viewBox="0 0 24 24"
						fill="none"
						stroke="currentColor"
						stroke-width="2"
					>
						<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
						<path d="M7 11V7a5 5 0 0 1 10 0v4" />
					</svg>
					Admin
				</button>
			</div>
		</div>
	</div>

	<!-- Password Change Modal -->
	{#if showPasswordModal}
		<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
			<Card.Root class="w-full max-w-md">
				<Card.Header>
					<Card.Title>Change Password</Card.Title>
					<Card.Description>Enter your current and new password</Card.Description>
				</Card.Header>
				<Card.Content class="space-y-4">
					<div>
						<label for="current-password" class="text-sm font-medium">Current Password</label>
						<Input
							id="current-password"
							type="password"
							bind:value={passwordData.current}
							placeholder="Enter current password"
							class="mt-1"
						/>
					</div>
					<div>
						<label for="new-password" class="text-sm font-medium">New Password</label>
						<Input
							id="new-password"
							type="password"
							bind:value={passwordData.new}
							placeholder="Enter new password"
							class="mt-1"
						/>
						{#if passwordData.new && newPasswordStrength}
							<div class="mt-2 space-y-1">
								<div class="flex h-1.5 w-full overflow-hidden rounded bg-gray-200">
									<div
										class="transition-all duration-300"
										style="width: {(newPasswordStrength.score + 1) *
											20}%; background-color: {newPasswordStrength.score < 2
											? '#ef4444'
											: newPasswordStrength.score === 2
												? '#f59e0b'
												: newPasswordStrength.score === 3
													? '#22c55e'
													: '#15803d'}"
									></div>
								</div>
								<p
									class="text-xs {newPasswordStrength.score < 2
										? 'text-red-500'
										: newPasswordStrength.score === 2
											? 'text-amber-500'
											: 'text-green-600'}"
								>
									{newPasswordStrength.feedback.warning
										? newPasswordStrength.feedback.warning
										: `Strength: ${['Very weak', 'Weak', 'Fair', 'Strong', 'Very strong'][newPasswordStrength.score]}`}
								</p>
								{#if newPasswordStrength.feedback.suggestions.length > 0}
									<p class="text-xs text-gray-500">
										{newPasswordStrength.feedback.suggestions[0]}
									</p>
								{/if}
							</div>
						{/if}
					</div>
					<div>
						<label for="confirm-password" class="text-sm font-medium">Confirm New Password</label>
						<Input
							id="confirm-password"
							type="password"
							bind:value={passwordData.confirm}
							placeholder="Confirm new password"
							class="mt-1"
						/>
						{#if !passwordsMatch}
							<p class="mt-1 text-xs text-red-500">Passwords do not match</p>
						{/if}
					</div>
				</Card.Content>
				<Card.Footer class="flex justify-end gap-2">
					<Button
						variant="outline"
						onclick={() => {
							showPasswordModal = false;
							passwordData = { current: '', new: '', confirm: '' };
						}}
					>
						Cancel
					</Button>
					<Button onclick={changePassword} disabled={changingPassword || !isPasswordStrongEnough}>
						{changingPassword ? 'Changing...' : 'Change Password'}
					</Button>
				</Card.Footer>
			</Card.Root>
		</div>
	{/if}

	<!-- Delete Account Alert -->
	<AlertDialog.Root bind:open={showDeleteAlert}>
		<AlertDialog.Content class="z-60">
			<AlertDialog.Header>
				<AlertDialog.Title>Delete Account?</AlertDialog.Title>
				<AlertDialog.Description>
					This action cannot be undone. This will permanently delete your account and remove all
					your data from our servers.
				</AlertDialog.Description>
			</AlertDialog.Header>
			<div class="px-6 py-4">
				<label for="delete-confirm" class="text-sm font-medium"
					>Type <strong>DELETE</strong> to confirm:</label
				>
				<Input
					id="delete-confirm"
					type="text"
					bind:value={deleteConfirmText}
					placeholder="DELETE"
					class="mt-2"
				/>
			</div>
			<AlertDialog.Footer>
				<AlertDialog.Cancel
					onclick={() => {
						deleteConfirmText = '';
					}}
				>
					Cancel
				</AlertDialog.Cancel>
				<Button
					variant="destructive"
					onclick={deleteAccount}
					disabled={deletingAccount || deleteConfirmText !== 'DELETE'}
				>
					{deletingAccount ? 'Deleting...' : 'Delete Account'}
				</Button>
			</AlertDialog.Footer>
		</AlertDialog.Content>
	</AlertDialog.Root>

	<!-- Delete Saved Card Confirmation -->
	<AlertDialog.Root
		bind:open={showDeleteCardDialog}
		onOpenChange={(open) => {
			if (!open) cardToDelete = null;
		}}
	>
		<AlertDialog.Content>
			<AlertDialog.Header>
				<AlertDialog.Title>Remove saved card?</AlertDialog.Title>
				<AlertDialog.Description>
					{#if cardToDelete}
						Remove {cardToDelete.brand} card ending in {cardToDelete.last_4}?
					{/if}
					You can add it again later.
				</AlertDialog.Description>
			</AlertDialog.Header>
			<AlertDialog.Footer>
				<AlertDialog.Cancel onclick={() => (cardToDelete = null)}>Cancel</AlertDialog.Cancel>
				<AlertDialog.Action
					onclick={() => cardToDelete && deleteCard(cardToDelete)}
					class="bg-red-600 hover:bg-red-700"
				>
					Remove
				</AlertDialog.Action>
			</AlertDialog.Footer>
		</AlertDialog.Content>
	</AlertDialog.Root>
{/if}

<!-- User Booking Modal -->
<UserBookingModal bind:open={showBookingModal} bookingId={selectedBookingId ?? ''} />

<style>
	/* Mobile Tab Menu Styles */
	.mobile-tab-menu {
		position: fixed;
		bottom: 0;
		left: 0;
		right: 0;
		z-index: 50;
		display: block;
		padding-bottom: env(safe-area-inset-bottom, 0);
	}

	.mobile-tab-menu > div {
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
	}

	.desktop-tab-menu {
		display: none;
		margin-bottom: 1.5rem;
	}

	@media (min-width: 768px) {
		.mobile-tab-menu {
			display: none;
		}

		.desktop-tab-menu {
			display: block;
		}
	}
</style>
