<script lang="ts">
	interface Props {
		value: string;
		onsubmit: (handle: string) => void;
		disabled?: boolean;
	}
	let { value = $bindable(), onsubmit, disabled = false }: Props = $props();

	let results = $state<Array<{ did: string; handle: string; displayName?: string; avatar?: string }>>([]);
	let showResults = $state(false);
	let searching = $state(false);
	let debounceTimer: ReturnType<typeof setTimeout> | null = $state(null);
	let containerEl: HTMLDivElement | undefined = $state();

	function handleInput() {
		if (debounceTimer) clearTimeout(debounceTimer);

		const query = value.trim();
		if (query.length < 2) {
			results = [];
			showResults = false;
			return;
		}

		searching = true;
		debounceTimer = setTimeout(async () => {
			try {
				const res = await fetch(
					`https://typeahead.waow.tech/xrpc/app.bsky.actor.searchActorsTypeahead?q=${encodeURIComponent(query)}&limit=8`,
					{ headers: { 'X-Client': 'bsky-highlight-reel' } }
				);
				if (res.ok) {
					const data = await res.json();
					results = data.actors ?? [];
					showResults = results.length > 0;
				}
			} catch {
				results = [];
				showResults = false;
			} finally {
				searching = false;
			}
		}, 250);
	}

	function selectActor(actor: { did: string; handle: string; displayName?: string; avatar?: string }) {
		value = actor.handle;
		showResults = false;
		onsubmit(actor.handle);
	}

	function handleClickOutside(e: MouseEvent) {
		if (containerEl && !containerEl.contains(e.target as Node)) {
			showResults = false;
		}
	}
</script>

<svelte:window onclick={handleClickOutside} />

<div class="handle-input" role="combobox" aria-controls="handle-results" aria-expanded={showResults && results.length > 0} bind:this={containerEl}>
	<form onsubmit={(e) => { e.preventDefault(); showResults = false; onsubmit(value); }}>
		<div class="input-row">
			<span class="at">@</span>
			<input
				type="text"
				bind:value
				oninput={handleInput}
				onfocus={() => { if (results.length > 0) showResults = true; }}
				onkeydown={(e) => { if (e.key === 'Escape') showResults = false; }}
				placeholder="who are you?"
				autocomplete="off"
				{disabled}
			/>
			<button type="submit" disabled={disabled || !value.trim()}>
				{#if disabled}
					<span class="spinner"></span>
				{:else}
					→
				{/if}
			</button>
		</div>
	</form>

	{#if showResults && results.length > 0}
		<ul class="results" id="handle-results">
			{#each results as actor (actor.did)}
				<li>
					<button onclick={(e) => { e.stopPropagation(); selectActor(actor); }}>
						{#if actor.avatar}
							<img src={actor.avatar} alt="" class="avatar" />
						{:else}
							<div class="avatar placeholder"></div>
						{/if}
						<div class="info">
							{#if actor.displayName}
								<span class="name">{actor.displayName}</span>
							{/if}
							<span class="handle">@{actor.handle}</span>
						</div>
					</button>
				</li>
			{/each}
		</ul>
	{/if}
</div>

<style>
	.handle-input {
		position: relative;
		width: 100%;
		max-width: 400px;
	}

	.input-row {
		display: flex;
		align-items: center;
		background: transparent;
		border-bottom: 2px solid #333;
		transition: border-color 0.2s;
	}
	.input-row:focus-within {
		border-color: #f90;
	}

	.at {
		color: #555;
		font-size: 1.1rem;
		font-weight: 300;
		padding-right: 0.25rem;
		user-select: none;
	}

	input {
		flex: 1;
		background: transparent;
		border: none;
		outline: none;
		color: #e0e0e0;
		font-size: 1.1rem;
		font-weight: 300;
		padding: 0.6rem 0;
		font-family: inherit;
	}
	input::placeholder {
		color: #444;
		font-style: italic;
	}

	.input-row button {
		background: none;
		border: none;
		color: #555;
		font-size: 1.2rem;
		cursor: pointer;
		padding: 0.4rem;
		transition: color 0.2s;
	}
	.input-row button:hover:not(:disabled) {
		color: #f90;
	}
	.input-row button:disabled {
		cursor: default;
	}

	.spinner {
		display: inline-block;
		width: 14px;
		height: 14px;
		border: 2px solid #333;
		border-top-color: #f90;
		border-radius: 50%;
		animation: spin 0.6s linear infinite;
	}
	@keyframes spin {
		to { transform: rotate(360deg); }
	}

	.results {
		position: absolute;
		top: 100%;
		left: 0;
		right: 0;
		margin-top: 0.5rem;
		background: #111;
		border: 1px solid #222;
		border-radius: 8px;
		list-style: none;
		padding: 0;
		max-height: 280px;
		overflow-y: auto;
		z-index: 100;
		box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
	}

	.results li button {
		display: flex;
		align-items: center;
		gap: 0.75rem;
		width: 100%;
		padding: 0.6rem 0.75rem;
		background: none;
		border: none;
		color: inherit;
		cursor: pointer;
		text-align: left;
		font-family: inherit;
	}
	.results li button:hover {
		background: #1a1a1a;
	}
	.results li:first-child button {
		border-radius: 8px 8px 0 0;
	}
	.results li:last-child button {
		border-radius: 0 0 8px 8px;
	}

	.avatar {
		width: 32px;
		height: 32px;
		border-radius: 50%;
		object-fit: cover;
		flex-shrink: 0;
	}
	.avatar.placeholder {
		background: #222;
	}

	.info {
		display: flex;
		flex-direction: column;
		gap: 0.1rem;
		min-width: 0;
	}
	.name {
		font-size: 0.85rem;
		color: #ccc;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}
	.handle {
		font-size: 0.75rem;
		color: #666;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}
</style>
