<script lang="ts">
	import { onMount } from 'svelte';
	import { currentCollective } from '$lib/stores/collective';
	import { currentUser } from '$lib/stores/auth';
	import { createList } from '$lib/stores/lists';
	import {
		listTitleCatalog,
		loadTitleCatalog,
		fetchTitleSuggestions,
		computeNextNumber
	} from '$lib/stores/listTitles';
	import { parseTitle } from '$lib/utils/list-title';
	import * as m from '$lib/paraglide/messages';
	import Spinner from '$lib/components/Spinner.svelte';

	interface Props {
		open: boolean;
		onClose: () => void;
		onCreated?: (id: string, finalName: string, wasAutoNumbered: boolean) => void;
	}

	const { open, onClose, onCreated }: Props = $props();

	let value = $state('');
	let suggestions = $state<string[]>([]);
	let suggestionsTimer: ReturnType<typeof setTimeout> | null = null;
	let creating = $state(false);
	let error = $state<string | null>(null);

	// Track open transitions so we only prefill on the false→true edge.
	// A naive `$effect(() => { if (open) value = default })` re-runs whenever
	// `$currentCollective` updates (e.g. realtime UPDATE after the user types
	// something), clobbering the typed value. Mirroring `open` into a local
	// state and comparing isolates the trigger to the actual open edge.
	let wasOpen = $state(false);

	$effect(() => {
		if (open && !wasOpen) {
			value = $currentCollective?.default_list_title ?? '';
			error = null;
			if ($currentCollective) {
				void loadTitleCatalog($currentCollective.id);
			}
			wasOpen = true;
		} else if (!open && wasOpen) {
			// Reset on close so reopening starts fresh.
			value = '';
			suggestions = [];
			error = null;
			wasOpen = false;
		}
	});

	// ── Suggestions (debounced) ─────────────────────────────────────────────

	function refreshSuggestions(prefix: string) {
		if (suggestionsTimer) clearTimeout(suggestionsTimer);
		const cid = $currentCollective?.id;
		if (!cid) {
			suggestions = [];
			return;
		}
		suggestionsTimer = setTimeout(async () => {
			suggestions = await fetchTitleSuggestions(cid, prefix);
		}, 150);
	}

	// Drive suggestions reactively off `value` (which is `bind:value`d below).
	// Using `$effect` instead of an explicit onInput keeps the click-on-chip /
	// click-on-suggestion paths consistent — they assign `value` and the
	// effect re-fires automatically.
	$effect(() => {
		// Read `value` so this effect depends on it.
		const v = value;
		refreshSuggestions(v);
	});

	// ── Catalog match (drives the suggestion chip + auto-suffix) ────────────

	const catalogTitles = $derived($listTitleCatalog.map((r) => r.title));

	const trimmed = $derived(value.trim());
	const parsed = $derived(parseTitle(trimmed));

	/**
	 * True when the typed value exactly matches a catalog entry
	 * case-insensitively AND has no `#N` suffix yet. That's the only state
	 * where auto-suffix fires (rule 9 from the orchestrating prompt).
	 */
	const catalogMatch = $derived.by(() => {
		if (!parsed.prefix || parsed.number !== null) return null;
		const norm = parsed.prefix.toLowerCase();
		return catalogTitles.find((t) => t.toLowerCase() === norm) ?? null;
	});

	// Suggestion chip — `Sugerencia: Compra #6`. We compute the proposed
	// number lazily when the catalog match changes.
	let proposedNumber = $state<number | null>(null);

	$effect(() => {
		const cm = catalogMatch;
		const cid = $currentCollective?.id;
		if (!cm || !cid) {
			proposedNumber = null;
			return;
		}
		void computeNextNumber(cid, cm).then((n) => {
			// Only update if the inputs haven't drifted while we awaited.
			if (catalogMatch === cm && $currentCollective?.id === cid) {
				proposedNumber = n;
			}
		});
	});

	const suggestionChipLabel = $derived.by(() => {
		if (!catalogMatch || proposedNumber === null) return null;
		return `${catalogMatch} #${proposedNumber}`;
	});

	function applyChip() {
		if (!suggestionChipLabel) return;
		value = suggestionChipLabel;
		void refreshSuggestions(value);
	}

	function applySuggestion(s: string) {
		value = s;
		void refreshSuggestions(value);
	}

	// ── Submit ──────────────────────────────────────────────────────────────

	const submitDisabled = $derived(trimmed.length === 0 || creating);

	async function submit() {
		if (submitDisabled || !$currentCollective || !$currentUser) return;
		creating = true;
		error = null;

		let finalName = trimmed;
		let wasAutoNumbered = false;

		// Auto-suffix only if (a) value exactly matches a catalog entry and
		// (b) `computeNextNumber` returns a value. Otherwise insert as-is.
		if (catalogMatch) {
			const n = await computeNextNumber($currentCollective.id, catalogMatch);
			finalName = `${catalogMatch} #${n}`;
			wasAutoNumbered = true;
		}

		const created = await createList($currentCollective.id, finalName, $currentUser.id);
		creating = false;
		if (!created) {
			error = 'Failed to create list.';
			return;
		}
		onClose();
		onCreated?.(created.id, finalName, wasAutoNumbered);
	}

	function onKeydown(e: KeyboardEvent) {
		if (e.key === 'Escape') {
			e.preventDefault();
			onClose();
		} else if (e.key === 'Enter') {
			// Always-prevent default to keep the form behaviour identical
			// whether the input or the submit button has focus; we drive the
			// submit explicitly so the disabled check applies uniformly.
			e.preventDefault();
			void submit();
		}
	}
</script>

{#if open}
	<div
		class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4"
		data-testid="create-list-modal"
		role="dialog"
		aria-modal="true"
		onclick={(e) => {
			if (e.target === e.currentTarget) onClose();
		}}
		onkeydown={onKeydown}
		tabindex="-1"
	>
		<div
			class="w-full max-w-md rounded-lg bg-surface p-5 shadow-[0px_20px_40px_rgba(15,23,42,0.2)] dark:bg-surface-raised"
		>
			<h2 class="mb-4 text-base font-semibold text-slate-900 dark:text-slate-50">
				{m.create_list_modal_title()}
			</h2>

			<form
				onsubmit={(e) => {
					e.preventDefault();
					void submit();
				}}
				class="space-y-3"
			>
				<div>
					<label
						for="create-list-modal-name"
						class="mb-1 block text-xs font-medium text-text-secondary"
					>
						{m.create_list_input_label()}
					</label>
					<!-- svelte-ignore a11y_autofocus -->
					<input
						id="create-list-modal-name"
						data-testid="create-list-modal-name"
						type="text"
						bind:value
						autofocus
						placeholder={m.create_list_input_placeholder()}
						maxlength="120"
						class="w-full rounded-lg border border-slate-300 bg-surface px-3 py-2 text-sm focus:border-slate-500 focus:outline-none dark:border-slate-600 dark:bg-slate-800"
					/>
				</div>

				{#if suggestionChipLabel}
					<button
						type="button"
						data-testid="create-list-suggestion-chip"
						onclick={applyChip}
						class="inline-flex items-center rounded-md bg-slate-100 px-2.5 py-1 text-xs font-medium text-slate-700 hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700"
					>
						{m.create_list_suggestion_chip({ title: suggestionChipLabel })}
					</button>
				{/if}

				{#if suggestions.length > 0}
					<ul
						data-testid="create-list-suggestion-list"
						class="max-h-40 overflow-y-auto rounded-md border border-slate-200 bg-surface text-sm dark:border-slate-700"
					>
						{#each suggestions as s (s)}
							<li>
								<button
									type="button"
									data-testid={`create-list-suggestion-${s}`}
									onclick={() => applySuggestion(s)}
									class="block w-full px-3 py-1.5 text-left text-slate-700 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700"
								>
									{s}
								</button>
							</li>
						{/each}
					</ul>
				{/if}

				{#if error}
					<p class="text-sm text-destructive" data-testid="create-list-modal-error">
						{error}
					</p>
				{/if}

				<div class="flex justify-end gap-2 pt-1">
					<button
						type="button"
						onclick={onClose}
						class="rounded-md px-3 py-1.5 text-sm font-medium text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800"
					>
						{m.common_cancel()}
					</button>
					<button
						type="submit"
						data-testid="create-list-modal-submit"
						disabled={submitDisabled}
						class="inline-flex items-center gap-2 rounded-md bg-slate-900 px-4 py-1.5 text-sm font-semibold text-white hover:opacity-90 disabled:opacity-50 dark:bg-slate-50 dark:text-slate-900"
					>
						{#if creating}
							<Spinner size="sm" />
							<span>{m.loading()}</span>
						{:else}
							{m.create_list_submit()}
						{/if}
					</button>
				</div>
			</form>
		</div>
	</div>
{/if}
