<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import MdiIcon from './MdiIcon.svelte';
	import { t } from '$lib/i18n';

	let {
		value = $bindable<string>('UTC'),
	}: {
		value: string;
	} = $props();

	// --- Catalog -----------------------------------------------------------

	const timezones = $derived.by<string[]>(() => {
		try {
			const intl = Intl as unknown as { supportedValuesOf?: (k: string) => string[] };
			if (typeof intl.supportedValuesOf === 'function') {
				return intl.supportedValuesOf('timeZone');
			}
		} catch { /* fall through */ }
		return ['UTC'];
	});

	const detectedTz = (() => {
		try { return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'; }
		catch { return 'UTC'; }
	})();

	// --- Live clock --------------------------------------------------------

	let now = $state(new Date());
	let tickHandle: ReturnType<typeof setInterval> | null = null;
	onMount(() => {
		tickHandle = setInterval(() => { now = new Date(); }, 1000);
	});
	onDestroy(() => { if (tickHandle) clearInterval(tickHandle); });

	function splitTz(tz: string): { region: string; city: string } {
		if (!tz || tz === 'UTC' || tz === 'Etc/UTC') return { region: 'UTC', city: 'UTC' };
		const parts = tz.split('/');
		if (parts.length === 1) return { region: 'Other', city: parts[0].replace(/_/g, ' ') };
		const city = parts[parts.length - 1].replace(/_/g, ' ');
		const region = parts.slice(0, -1).join(' / ').replace(/_/g, ' ');
		return { region, city };
	}

	function fmtTime(tz: string): string {
		try {
			return new Intl.DateTimeFormat('en-GB', {
				timeZone: tz,
				hour: '2-digit',
				minute: '2-digit',
				second: '2-digit',
				hour12: false,
			}).format(now);
		} catch { return '--:--:--'; }
	}

	function fmtDate(tz: string): string {
		try {
			return new Intl.DateTimeFormat(undefined, {
				timeZone: tz,
				weekday: 'short',
				day: 'numeric',
				month: 'short',
			}).format(now);
		} catch { return ''; }
	}

	function fmtOffset(tz: string): string {
		try {
			const parts = new Intl.DateTimeFormat('en-US', {
				timeZone: tz,
				timeZoneName: 'shortOffset',
			}).formatToParts(now);
			const off = parts.find(p => p.type === 'timeZoneName')?.value ?? '';
			return off || 'UTC';
		} catch { return ''; }
	}

	// --- Selected state ----------------------------------------------------

	const selected = $derived.by(() => {
		const s = splitTz(value || 'UTC');
		return {
			iana: value || 'UTC',
			region: s.region,
			city: s.city,
			time: fmtTime(value || 'UTC'),
			date: fmtDate(value || 'UTC'),
			offset: fmtOffset(value || 'UTC'),
		};
	});

	// --- Picker ------------------------------------------------------------

	let open = $state(false);
	let query = $state('');
	let highlightIdx = $state(0);
	let inputEl = $state<HTMLInputElement | null>(null);
	let panelEl = $state<HTMLDivElement | null>(null);

	const filtered = $derived.by(() => {
		const q = query.trim().toLowerCase().replace(/\s+/g, '_');
		if (!q) return timezones;
		return timezones.filter(tz => tz.toLowerCase().includes(q));
	});

	// Group filtered tz list by region prefix for visual hierarchy.
	interface Group { region: string; items: string[] }
	const groups = $derived.by<Group[]>(() => {
		const map = new Map<string, string[]>();
		for (const tz of filtered) {
			const region = tz.includes('/') ? tz.split('/')[0] : 'Other';
			if (!map.has(region)) map.set(region, []);
			map.get(region)!.push(tz);
		}
		const REGION_ORDER = ['UTC', 'Europe', 'America', 'Asia', 'Africa', 'Australia', 'Pacific', 'Atlantic', 'Indian', 'Antarctica', 'Arctic', 'Etc', 'Other'];
		return [...map.entries()]
			.sort(([a], [b]) => {
				const ai = REGION_ORDER.indexOf(a);
				const bi = REGION_ORDER.indexOf(b);
				return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);
			})
			.map(([region, items]) => ({ region, items }));
	});

	// Flattened index for keyboard navigation.
	const flat = $derived<string[]>(groups.flatMap(g => g.items));

	function openPicker() {
		open = true;
		query = '';
		highlightIdx = Math.max(0, flat.indexOf(value));
		requestAnimationFrame(() => {
			inputEl?.focus();
			scrollToHighlight();
		});
	}

	function closePicker() {
		open = false;
		query = '';
	}

	function selectTz(tz: string) {
		value = tz;
		closePicker();
	}

	function onKeydown(e: KeyboardEvent) {
		if (e.key === 'Escape') { closePicker(); return; }
		if (e.key === 'ArrowDown') {
			e.preventDefault();
			highlightIdx = Math.min(highlightIdx + 1, flat.length - 1);
			scrollToHighlight();
		} else if (e.key === 'ArrowUp') {
			e.preventDefault();
			highlightIdx = Math.max(highlightIdx - 1, 0);
			scrollToHighlight();
		} else if (e.key === 'Enter') {
			e.preventDefault();
			if (flat[highlightIdx]) selectTz(flat[highlightIdx]);
		}
	}

	function scrollToHighlight() {
		requestAnimationFrame(() => {
			panelEl?.querySelector('.tz-opt-hl')?.scrollIntoView({ block: 'nearest' });
		});
	}

	$effect(() => { query; highlightIdx = 0; });

	// Close on outside click
	function onDocClick(e: MouseEvent) {
		if (!open) return;
		const target = e.target as Node;
		if (panelEl && !panelEl.contains(target)) closePicker();
	}
	onMount(() => {
		document.addEventListener('mousedown', onDocClick);
	});
	onDestroy(() => {
		document.removeEventListener('mousedown', onDocClick);
	});
</script>

<div class="tz-root">
	<!-- Selected card -->
	<button
		type="button"
		class="tz-card"
		class:tz-card-open={open}
		onclick={() => (open ? closePicker() : openPicker())}
		aria-haspopup="listbox"
		aria-expanded={open}
	>
		<div class="tz-card-left">
			<div class="tz-region">{selected.region}</div>
			<div class="tz-city">{selected.city}</div>
			<div class="tz-sub">
				<span class="tz-iana">{selected.iana}</span>
				{#if selected.date}
					<span class="tz-dot">·</span>
					<span class="tz-date">{selected.date}</span>
				{/if}
			</div>
		</div>
		<div class="tz-card-right">
			<div class="tz-clock">{selected.time}</div>
			<div class="tz-offset">{selected.offset}</div>
		</div>
		<span class="tz-chev" aria-hidden="true">
			<MdiIcon name={open ? 'mdiChevronUp' : 'mdiChevronDown'} size={16} />
		</span>
	</button>

	{#if open}
		<div class="tz-panel" bind:this={panelEl} role="listbox">
			<!-- Search -->
			<div class="tz-search-row">
				<MdiIcon name="mdiMagnify" size={14} />
				<input
					bind:this={inputEl}
					bind:value={query}
					onkeydown={onKeydown}
					placeholder={t('timezone.searchPlaceholder')}
					class="tz-search"
					autocomplete="off"
					spellcheck="false"
					type="text"
				/>
				<kbd class="tz-kbd">ESC</kbd>
			</div>

			<!-- Quick picks -->
			{#if !query}
				<div class="tz-quick">
					<button
						type="button"
						class="tz-quick-btn"
						class:tz-quick-active={value === detectedTz}
						onclick={() => selectTz(detectedTz)}
					>
						<MdiIcon name="mdiCrosshairsGps" size={12} />
						<span class="tz-quick-label">{t('timezone.detect')}</span>
						<span class="tz-quick-val">{detectedTz}</span>
					</button>
					<button
						type="button"
						class="tz-quick-btn"
						class:tz-quick-active={value === 'UTC' || value === 'Etc/UTC'}
						onclick={() => selectTz('UTC')}
					>
						<MdiIcon name="mdiEarth" size={12} />
						<span class="tz-quick-label">{t('timezone.utc')}</span>
						<span class="tz-quick-val">UTC+00</span>
					</button>
				</div>
			{/if}

			<!-- Grouped list -->
			<div class="tz-list">
				{#if filtered.length === 0}
					<div class="tz-empty">{t('timezone.noMatches')}</div>
				{:else}
					{#each groups as g (g.region)}
						<div class="tz-group">
							<div class="tz-group-head">
								<span class="tz-group-name">{g.region}</span>
								<span class="tz-group-count">{g.items.length}</span>
							</div>
							{#each g.items as tz (tz)}
								{@const parts = splitTz(tz)}
								{@const idx = flat.indexOf(tz)}
								{@const hl = idx === highlightIdx}
								{@const sel = tz === value}
								<button
									type="button"
									role="option"
									aria-selected={sel}
									class="tz-opt"
									class:tz-opt-hl={hl}
									class:tz-opt-sel={sel}
									onmouseenter={() => (highlightIdx = idx)}
									onclick={() => selectTz(tz)}
								>
									<span class="tz-opt-city">{parts.city}</span>
									<span class="tz-opt-iana">{tz}</span>
									<span class="tz-opt-offset">{fmtOffset(tz)}</span>
								</button>
							{/each}
						</div>
					{/each}
				{/if}
			</div>
		</div>
	{/if}
</div>

<style>
	.tz-root {
		position: relative;
		width: 100%;
		max-width: 34rem;
	}

	/* ---- Selected card ------------------------------------------------ */
	.tz-card {
		position: relative;
		display: grid;
		grid-template-columns: 1fr auto auto;
		align-items: center;
		gap: 0.875rem;
		width: 100%;
		padding: 0.75rem 1rem 0.75rem 0.875rem;
		border: 1px solid var(--color-border);
		border-radius: 0.625rem;
		background:
			linear-gradient(135deg,
				color-mix(in srgb, var(--color-primary) 5%, transparent) 0%,
				transparent 55%),
			var(--color-background);
		color: var(--color-foreground);
		text-align: left;
		cursor: pointer;
		transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s;
	}
	.tz-card:hover {
		border-color: color-mix(in srgb, var(--color-primary) 45%, var(--color-border));
	}
	.tz-card.tz-card-open {
		border-color: var(--color-primary);
		box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 12%, transparent);
	}

	.tz-card-left {
		display: flex;
		flex-direction: column;
		gap: 0.1rem;
		min-width: 0;
	}
	.tz-region {
		font-size: 0.6rem;
		text-transform: uppercase;
		letter-spacing: 0.1em;
		font-weight: 600;
		color: color-mix(in srgb, var(--color-primary) 70%, var(--color-muted-foreground));
	}
	.tz-city {
		font-family: var(--font-sans);
		font-size: 1.25rem;
		font-weight: 500;
		line-height: 1.1;
		letter-spacing: -0.01em;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	.tz-sub {
		display: flex;
		align-items: center;
		gap: 0.4rem;
		font-size: 0.7rem;
		color: var(--color-muted-foreground);
		min-width: 0;
	}
	.tz-iana {
		font-family: var(--font-mono);
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	.tz-dot { opacity: 0.5; }

	.tz-card-right {
		display: flex;
		flex-direction: column;
		align-items: flex-end;
		gap: 0.2rem;
	}
	.tz-clock {
		font-family: var(--font-mono);
		font-size: 1.25rem;
		font-weight: 500;
		letter-spacing: 0.02em;
		color: var(--color-foreground);
		line-height: 1;
		/* Stable width so seconds ticker doesn't shift layout */
		font-variant-numeric: tabular-nums;
	}
	.tz-offset {
		font-family: var(--font-mono);
		font-size: 0.6rem;
		font-weight: 600;
		letter-spacing: 0.04em;
		padding: 0.1rem 0.375rem;
		border-radius: 9999px;
		background: color-mix(in srgb, var(--color-primary) 12%, transparent);
		color: var(--color-primary);
		border: 1px solid color-mix(in srgb, var(--color-primary) 25%, transparent);
	}

	.tz-chev {
		color: var(--color-muted-foreground);
		display: inline-flex;
		align-items: center;
	}

	/* ---- Panel -------------------------------------------------------- */
	.tz-panel {
		position: absolute;
		top: calc(100% + 0.375rem);
		left: 0;
		right: 0;
		z-index: 20;
		background: var(--color-card, var(--color-background));
		border: 1px solid var(--color-border);
		border-radius: 0.625rem;
		box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);
		overflow: hidden;
		display: flex;
		flex-direction: column;
		max-height: 26rem;
		animation: tz-pop 0.15s ease-out;
	}
	@keyframes tz-pop {
		from { opacity: 0; transform: translateY(-3px); }
		to   { opacity: 1; transform: translateY(0); }
	}

	.tz-search-row {
		display: flex;
		align-items: center;
		gap: 0.5rem;
		padding: 0.5rem 0.75rem;
		border-bottom: 1px solid var(--color-border);
		color: var(--color-muted-foreground);
	}
	.tz-search {
		flex: 1;
		border: none;
		outline: none;
		background: transparent;
		font-size: 0.85rem;
		color: var(--color-foreground);
		padding: 0.125rem 0;
		min-width: 0;
	}
	.tz-kbd {
		font-size: 0.55rem;
		font-family: var(--font-mono);
		padding: 0.1rem 0.3rem;
		border-radius: 0.2rem;
		background: var(--color-muted);
		color: var(--color-muted-foreground);
		border: 1px solid var(--color-border);
	}

	.tz-quick {
		display: flex;
		gap: 0.375rem;
		padding: 0.5rem 0.625rem;
		border-bottom: 1px solid var(--color-border);
		flex-wrap: wrap;
	}
	.tz-quick-btn {
		display: inline-flex;
		align-items: center;
		gap: 0.375rem;
		padding: 0.25rem 0.5rem;
		border: 1px solid var(--color-border);
		border-radius: 9999px;
		background: var(--color-background);
		font-size: 0.7rem;
		color: var(--color-foreground);
		cursor: pointer;
		transition: border-color 0.12s, background 0.12s, color 0.12s;
	}
	.tz-quick-btn:hover {
		border-color: color-mix(in srgb, var(--color-primary) 40%, var(--color-border));
		color: var(--color-primary);
	}
	.tz-quick-active {
		border-color: var(--color-primary);
		background: color-mix(in srgb, var(--color-primary) 8%, transparent);
		color: var(--color-primary);
	}
	.tz-quick-label {
		font-weight: 500;
	}
	.tz-quick-val {
		font-family: var(--font-mono);
		font-size: 0.65rem;
		opacity: 0.7;
	}

	.tz-list {
		overflow-y: auto;
		padding: 0.25rem 0;
		scrollbar-width: thin;
	}
	.tz-empty {
		padding: 1rem;
		text-align: center;
		font-size: 0.8rem;
		color: var(--color-muted-foreground);
	}

	.tz-group {
		margin-bottom: 0.125rem;
	}
	.tz-group-head {
		display: flex;
		align-items: baseline;
		justify-content: space-between;
		padding: 0.375rem 0.75rem 0.25rem;
		font-size: 0.55rem;
		text-transform: uppercase;
		letter-spacing: 0.12em;
		font-weight: 600;
		color: var(--color-muted-foreground);
		position: sticky;
		top: 0;
		background: var(--color-card, var(--color-background));
		border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent);
		z-index: 1;
	}
	.tz-group-count {
		font-family: var(--font-mono);
		opacity: 0.6;
	}

	.tz-opt {
		display: grid;
		grid-template-columns: 1fr 1fr auto;
		align-items: center;
		gap: 0.625rem;
		width: 100%;
		padding: 0.35rem 0.75rem;
		border: none;
		background: transparent;
		color: var(--color-foreground);
		cursor: pointer;
		text-align: left;
		transition: background 0.1s;
	}
	.tz-opt.tz-opt-hl {
		background: var(--color-muted);
	}
	.tz-opt.tz-opt-sel {
		background: color-mix(in srgb, var(--color-primary) 8%, transparent);
	}
	.tz-opt-city {
		font-size: 0.85rem;
		font-weight: 500;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	.tz-opt.tz-opt-sel .tz-opt-city {
		color: var(--color-primary);
	}
	.tz-opt-iana {
		font-family: var(--font-mono);
		font-size: 0.7rem;
		color: var(--color-muted-foreground);
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
	.tz-opt-offset {
		font-family: var(--font-mono);
		font-size: 0.65rem;
		color: var(--color-muted-foreground);
		padding: 0.1rem 0.375rem;
		border-radius: 0.25rem;
		background: var(--color-muted);
		white-space: nowrap;
	}
	.tz-opt.tz-opt-hl .tz-opt-offset {
		background: color-mix(in srgb, var(--color-primary) 15%, var(--color-muted));
	}
</style>
