/**
 * Keyboard shortcuts for the Transcribe panel power path (issue #327).
 *
 * A pure input-to-callback translator: it owns no state and has no
 * save/persistence responsibility. Panel state and every command are passed in
 * as callbacks backed by the page's existing context setters, so the listener
 * never closes over stale `$state`. It is the single owner of the panel's
 * global `keydown` — including `Esc` (decision B1).
 */
export type TranscribeShortcutOptions = {
	isPanelOpen: () => boolean; // reads transcribeMode ($state)
	isCheatsheetOpen: () => boolean; // Esc ladder rung 1 + "?" open-only guard
	panelMode: () => 'read' | 'edit'; // for the n-only-in-edit guard
	goToNextRegion: () => void; // j
	goToPrevRegion: () => void; // k
	toggleMode: () => void; // e
	closePanel: () => void; // Esc ladder rung 3
	startDrawMode: () => void; // n (edit mode only)
	toggleTrainingMark: () => void; // t (no-op when no active block)
	deleteCurrentRegion: () => void; // Delete (confirm modal)
	openCheatsheet: () => void; // ?
};

function isEditableTarget(target: EventTarget | null): boolean {
	if (!(target instanceof HTMLElement)) return false;
	const tag = target.tagName;
	return tag === 'INPUT' || tag === 'TEXTAREA' || target.isContentEditable;
}

// `node` is unused: the listener is global (window) so a shortcut fires no
// matter where focus sits on the page. It is still authored as a Svelte action
// (`use:transcribeShortcuts`) so its lifecycle is tied to the host element's
// mount/unmount and `destroy()` reliably removes the listener.
export function transcribeShortcuts(_node: HTMLElement, initial: TranscribeShortcutOptions) {
	let options = initial;

	function handleKeydown(event: KeyboardEvent) {
		// "?" is focus-independent: it fires regardless of the focus guard, but
		// only with no Ctrl/Alt/Meta (Shift is allowed — "?" is Shift+ß on QWERTZ).
		if (event.key === '?' && !event.ctrlKey && !event.altKey && !event.metaKey) {
			if (!options.isPanelOpen()) return;
			event.preventDefault();
			if (!options.isCheatsheetOpen()) options.openCheatsheet();
			return;
		}

		if (!options.isPanelOpen()) return;

		// Esc precedence ladder (decision B1) — top rung wins.
		if (event.key === 'Escape') {
			if (options.isCheatsheetOpen()) return; // rung 1: the <dialog> closes itself
			if (isEditableTarget(event.target)) return; // rung 2: let TipTap handle its Esc
			event.preventDefault(); // rung 3: close the panel
			options.closePanel();
			return;
		}

		// Every remaining shortcut is inactive while focus is inside an editable.
		if (isEditableTarget(event.target)) return;

		switch (event.key) {
			case 'j':
				event.preventDefault();
				options.goToNextRegion();
				break;
			case 'k':
				event.preventDefault();
				options.goToPrevRegion();
				break;
			case 'e':
				event.preventDefault();
				options.toggleMode();
				break;
			case 'n':
				if (options.panelMode() !== 'edit') return; // silent no-op in read mode
				event.preventDefault();
				options.startDrawMode();
				break;
			case 't':
				event.preventDefault();
				options.toggleTrainingMark();
				break;
			case 'Delete':
				event.preventDefault();
				options.deleteCurrentRegion();
				break;
		}
	}

	window.addEventListener('keydown', handleKeydown);

	return {
		update(next: TranscribeShortcutOptions) {
			options = next;
		},
		destroy() {
			window.removeEventListener('keydown', handleKeydown);
		}
	};
}
