<script lang="ts">
	import { MAX_COMMAND_HISTORY, MAX_MESSAGE_LENGTH } from '$lib/config';

	// parent hands us the submit handler and an optional completion source.
	let {
		onsubmit,
		complete
	}: { onsubmit: (raw: string) => void; complete?: (prefix: string) => string[] } = $props();

	let value = $state('');
	let history = $state<string[]>([]);
	// -1 means "not in history" (editing the live buffer).
	let historyIndex = $state(-1);
	let draft = $state('');
	let input = $state<HTMLInputElement | null>(null);
	// where we are in the completion candidates on repeated tab.
	let completionCycle = $state(0);

	// length counter for plain messages (not commands). only shows once you
	// pass 80% of the limit so it stays out of the way.
	let isCommand = $derived(value.trimStart().startsWith('/'));
	let showCounter = $derived(!isCommand && value.length >= Math.floor(MAX_MESSAGE_LENGTH * 0.8));
	let remaining = $derived(MAX_MESSAGE_LENGTH - value.length);

	function submit() {
		const raw = value.trim();
		if (raw.length === 0) return;
		// cap message length (also guards command length).
		const bounded = raw.slice(0, MAX_MESSAGE_LENGTH);

		history = [...history.slice(-(MAX_COMMAND_HISTORY - 1)), bounded];
		historyIndex = -1;
		draft = '';
		value = '';
		onsubmit(bounded);
	}

	function recallHistory(direction: -1 | 1) {
		if (history.length === 0) return;
		if (historyIndex === -1) {
			// stepping into history: stash the current draft first.
			if (direction === 1) return; // nothing newer than the live draft
			draft = value;
			historyIndex = history.length - 1;
		} else {
			historyIndex += direction;
		}
		if (historyIndex >= history.length) {
			// past the newest entry: back to the live draft.
			historyIndex = -1;
			value = draft;
			return;
		}
		if (historyIndex < 0) historyIndex = 0;
		value = history[historyIndex] ?? '';
	}

	function tabComplete() {
		if (!complete) return;
		// only complete the first token (the command name).
		if (/\s/.test(value.trim())) return;
		const candidates = complete(value.trim());
		if (candidates.length === 0) return;
		if (candidates.length === 1) {
			value = candidates[0] ?? value;
			completionCycle = 0;
			return;
		}
		// multiple matches: cycle through them on repeated tab presses.
		value = candidates[completionCycle % candidates.length] ?? value;
		completionCycle += 1;
	}

	function onKeydown(event: KeyboardEvent) {
		if (event.key !== 'Tab') completionCycle = 0;
		if (event.key === 'Enter') {
			event.preventDefault();
			submit();
		} else if (event.key === 'ArrowUp') {
			event.preventDefault();
			recallHistory(-1);
		} else if (event.key === 'ArrowDown') {
			event.preventDefault();
			recallHistory(1);
		} else if (event.key === 'Tab') {
			event.preventDefault();
			tabComplete();
		}
	}
</script>

<form
	class="flex items-center gap-2 border-t px-3 py-2"
	style="border-color: var(--color-border); background: var(--color-surface);"
	onsubmit={(e) => {
		e.preventDefault();
		submit();
	}}
>
	<label for="command-input" class="select-none font-bold" style="color: var(--color-accent);">
		&gt;
	</label>
	<input
		bind:this={input}
		bind:value
		id="command-input"
		name="command-input"
		type="text"
		autocomplete="off"
		autocapitalize="off"
		spellcheck="false"
		maxlength={MAX_MESSAGE_LENGTH}
		placeholder="Type a message, or /help for commands"
		class="flex-1 bg-transparent outline-none"
		style="color: var(--color-text-primary); font-family: var(--font-terminal);"
		aria-label="command input"
		onkeydown={onKeydown}
	/>
	{#if showCounter}
		<span
			class="shrink-0 select-none text-xs tabular-nums"
			style="color: {remaining <= 0 ? 'var(--color-error)' : 'var(--color-text-muted)'};"
			aria-live="polite"
		>
			{remaining}
		</span>
	{/if}
</form>
