<script lang="ts">
	/**
	 * Universell editor-komponent.
	 * Fase 1: Tiptap med plaintext, #-mentions, markdown formatting.
	 *
	 * Modi:
	 *   compact  — chat-input (Enter = submit, ingen synlig toolbar)
	 *   extended — notater/lengre tekst (Enter = newline, toolbar synlig)
	 *
	 * Bruk:
	 *   <Editor mode="compact" onSubmit={(content) => ...} placeholder="Skriv en melding..." />
	 *   <Editor mode="extended" bind:content onUpdate={() => ...} placeholder="Skriv her..." />
	 */
	import { onMount, onDestroy } from 'svelte';
	import { Editor } from '@tiptap/core';
	import StarterKit from '@tiptap/starter-kit';
	import Placeholder from '@tiptap/extension-placeholder';
	import Mention from '@tiptap/extension-mention';

	interface Entity {
		id: string;
		name: string;
		type: string;
		aliases: string[];
	}

	let {
		mode = 'compact',
		placeholder = '',
		content = $bindable(''),
		autofocus = false,
		onSubmit = (_html: string, _json: Record<string, unknown>, _mentions: Entity[]) => {},
		onUpdate = () => {},
		onExpand = () => {}
	}: {
		mode?: 'compact' | 'extended';
		placeholder?: string;
		content?: string;
		autofocus?: boolean;
		onSubmit?: (html: string, json: Record<string, unknown>, mentions: Entity[]) => void;
		onUpdate?: () => void;
		onExpand?: () => void;
	} = $props();

	let editorEl: HTMLDivElement;
	let editor = $state<Editor | null>(null);
	let mentions: Entity[] = [];
	let expanded = $state(false);
	let hasContent = $state(false);
	let rawMode = $state(false);
	let rawContent = $state('');

	// Mention suggestions state
	let suggestions = $state<Entity[]>([]);
	let selectedIndex = $state(0);
	let suggestionsEl: HTMLDivElement | undefined;
	let mentionPopupPos = $state<{ top: number; left: number } | null>(null);
	let mentionCommandFn: ((item: any) => void) | null = null;

	const typeColors: Record<string, string> = {
		person: '#3b82f6',
		organisasjon: '#f59e0b',
		sted: '#10b981',
		tema: '#8b5cf6',
		konsept: '#ec4899'
	};

	function getEditorContent() {
		if (!editor) return { html: '', json: {} };
		return {
			html: editor.getHTML(),
			json: editor.getJSON()
		};
	}

	function clearEditor() {
		editor?.commands.clearContent();
		mentions = [];
		hasContent = false;
		expanded = false;
	}

	async function searchEntities(query: string): Promise<Entity[]> {
		try {
			const res = await fetch(`/api/entities?q=${encodeURIComponent(query)}&limit=8`);
			if (res.ok) return await res.json();
		} catch { /* ignore */ }
		return [];
	}

	function handleSubmit() {
		if (!editor || !hasContent) return;
		const { html, json } = getEditorContent();
		onSubmit(html, json, [...mentions]);
		clearEditor();
	}

	onMount(() => {
		editor = new Editor({
			element: editorEl,
			extensions: [
				StarterKit,
				Placeholder.configure({
					placeholder
				}),
				Mention.configure({
					HTMLAttributes: {
						class: 'mention'
					},
					suggestion: {
						char: '#',
						items: async ({ query }: { query: string }) => {
							if (query.length < 1) return [];
							return await searchEntities(query);
						},
						render: () => {
							return {
								onStart: (props: any) => {
									mentionCommandFn = props.command;
									suggestions = props.items;
									selectedIndex = 0;
									updatePopupPosition(props.clientRect);
								},
								onUpdate: (props: any) => {
									mentionCommandFn = props.command;
									suggestions = props.items;
									selectedIndex = 0;
									updatePopupPosition(props.clientRect);
								},
								onKeyDown: (props: any) => {
									if (props.event.key === 'ArrowDown') {
										selectedIndex = (selectedIndex + 1) % suggestions.length;
										return true;
									}
									if (props.event.key === 'ArrowUp') {
										selectedIndex = (selectedIndex - 1 + suggestions.length) % suggestions.length;
										return true;
									}
									if (props.event.key === 'Enter' || props.event.key === 'Tab') {
										if (suggestions.length > 0) {
											mentionCommandFn?.(suggestions[selectedIndex]);
											return true;
										}
									}
									if (props.event.key === 'Escape') {
										suggestions = [];
										return true;
									}
									return false;
								},
								onExit: () => {
									mentionCommandFn = null;
									suggestions = [];
									mentionPopupPos = null;
								}
							};
						},
						command: ({ editor: ed, range, props: item }: any) => {
							ed.chain()
								.focus()
								.insertContentAt(range, [
									{ type: 'mention', attrs: { id: item.id, label: item.name } },
									{ type: 'text', text: ' ' }
								])
								.run();
							mentions.push(item);
						}
					}
				})
			],
			content: content || '',
			editorProps: {
				attributes: {
					class: `editor-content ${mode}`,
					'data-mode': mode
				},
				handleKeyDown: (_view, event) => {
					// Compact mode: Enter = submit (unless shift held or suggestions open)
					if (mode === 'compact' && event.key === 'Enter' && !event.shiftKey) {
						if (suggestions.length > 0) return false; // Let mention handler deal with it
						event.preventDefault();
						handleSubmit();
						return true;
					}
					return false;
				}
			},
			onUpdate: ({ editor: ed }) => {
				content = ed.getHTML();
				hasContent = !ed.isEmpty;
				onUpdate();
			},
			autofocus: autofocus ? 'end' : false
		});
	});

	onDestroy(() => {
		editor?.destroy();
	});

	function updatePopupPosition(clientRect: (() => DOMRect | null) | null) {
		if (!clientRect) return;
		const rect = clientRect();
		if (!rect) return;
		const editorRect = editorEl.getBoundingClientRect();
		mentionPopupPos = {
			left: rect.left - editorRect.left,
			top: rect.top - editorRect.top - 4
		};
	}

	function toggleExpand() {
		expanded = !expanded;
		if (expanded) onExpand();
		if (!expanded) rawMode = false;
		editor?.commands.focus();
	}

	function toggleRawMode() {
		if (!editor) return;
		if (rawMode) {
			// Switching from raw → rendered: push textarea content into Tiptap
			editor.commands.setContent(rawContent);
			content = rawContent;
			hasContent = !editor.isEmpty;
			rawMode = false;
			editor.commands.focus();
		} else {
			// Switching from rendered → raw: snapshot HTML into textarea
			rawContent = editor.getHTML();
			rawMode = true;
		}
	}

	function handleRawInput(e: Event) {
		rawContent = (e.target as HTMLTextAreaElement).value;
		content = rawContent;
		hasContent = rawContent.replace(/<[^>]*>/g, '').trim().length > 0;
	}

	function handleGlobalKeydown(e: KeyboardEvent) {
		// Ctrl+/ or Cmd+/ toggles raw mode
		if ((e.ctrlKey || e.metaKey) && e.key === '/') {
			e.preventDefault();
			toggleRawMode();
		}
	}
</script>

<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="editor-wrapper" class:compact={mode === 'compact'} class:extended={mode === 'extended'} class:expanded onkeydown={handleGlobalKeydown}>
	{#if mode === 'extended' || expanded}
		<div class="toolbar">
			{#if !rawMode}
				<button type="button" class="tool" class:active={editor?.isActive('bold')}
					onclick={() => editor?.chain().focus().toggleBold().run()}
					title="Bold (Ctrl+B)">B</button>
				<button type="button" class="tool" class:active={editor?.isActive('italic')}
					onclick={() => editor?.chain().focus().toggleItalic().run()}
					title="Italic (Ctrl+I)"><em>I</em></button>
				<button type="button" class="tool" class:active={editor?.isActive('strike')}
					onclick={() => editor?.chain().focus().toggleStrike().run()}
					title="Strikethrough">S̶</button>
				<button type="button" class="tool" class:active={editor?.isActive('code')}
					onclick={() => editor?.chain().focus().toggleCode().run()}
					title="Inline code">{'<>'}</button>
				<span class="separator"></span>
				<button type="button" class="tool" class:active={editor?.isActive('heading', { level: 1 })}
					onclick={() => editor?.chain().focus().toggleHeading({ level: 1 }).run()}
					title="Heading 1">H1</button>
				<button type="button" class="tool" class:active={editor?.isActive('heading', { level: 2 })}
					onclick={() => editor?.chain().focus().toggleHeading({ level: 2 }).run()}
					title="Heading 2">H2</button>
				<button type="button" class="tool" class:active={editor?.isActive('heading', { level: 3 })}
					onclick={() => editor?.chain().focus().toggleHeading({ level: 3 }).run()}
					title="Heading 3">H3</button>
				<span class="separator"></span>
				<button type="button" class="tool" class:active={editor?.isActive('bulletList')}
					onclick={() => editor?.chain().focus().toggleBulletList().run()}
					title="Bullet list">•</button>
				<button type="button" class="tool" class:active={editor?.isActive('orderedList')}
					onclick={() => editor?.chain().focus().toggleOrderedList().run()}
					title="Numbered list">1.</button>
				<button type="button" class="tool" class:active={editor?.isActive('blockquote')}
					onclick={() => editor?.chain().focus().toggleBlockquote().run()}
					title="Quote">&#10077;</button>
				<button type="button" class="tool" class:active={editor?.isActive('codeBlock')}
					onclick={() => editor?.chain().focus().toggleCodeBlock().run()}
					title="Code block">{'{ }'}</button>
				<span class="separator"></span>
			{/if}
			<button type="button" class="tool raw-toggle" class:active={rawMode}
				onclick={toggleRawMode}
				title="Bytt raw/rendered (Ctrl+/)">{'</>'}</button>
		</div>
	{/if}

	<div class="editor-container">
		{#if rawMode}
			<textarea
				class="raw-editor"
				value={rawContent}
				oninput={handleRawInput}
				spellcheck={false}
			></textarea>
		{:else}
			<div bind:this={editorEl} class="editor-mount"></div>
		{/if}

		{#if mode === 'compact' && !expanded}
			<button type="button" class="expand-btn" onclick={toggleExpand} title="Utvid editor">
				<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
					<polyline points="15 3 21 3 21 9"></polyline>
					<polyline points="9 21 3 21 3 15"></polyline>
					<line x1="21" y1="3" x2="14" y2="10"></line>
					<line x1="3" y1="21" x2="10" y2="14"></line>
				</svg>
			</button>
			<button
				type="button"
				class="send-btn"
				onclick={handleSubmit}
				disabled={!hasContent}
				aria-label="Send"
			>
				<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
					<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
				</svg>
			</button>
		{/if}
	</div>

	{#if mode === 'compact' && expanded}
		<div class="editor-bottom-bar">
			<button type="button" class="expand-btn" onclick={toggleExpand} title="Minimer">
				<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
					<polyline points="4 14 10 14 10 20"></polyline>
					<polyline points="20 10 14 10 14 4"></polyline>
					<line x1="14" y1="10" x2="21" y2="3"></line>
					<line x1="3" y1="21" x2="10" y2="14"></line>
				</svg>
			</button>
			<button
				type="button"
				class="send-btn"
				onclick={handleSubmit}
				disabled={!hasContent}
				aria-label="Send"
			>
				<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
					<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
				</svg>
			</button>
		</div>
	{/if}

	<!-- Mention suggestions popup -->
	{#if suggestions.length > 0 && mentionPopupPos}
		<div
			class="mention-popup"
			bind:this={suggestionsEl}
			style:left="{mentionPopupPos.left}px"
			style:bottom="calc(100% - {mentionPopupPos.top}px)"
		>
			{#each suggestions as entity, i (entity.id)}
				<button
					type="button"
					class="suggestion"
					class:selected={i === selectedIndex}
					onmousedown={(e) => {
						e.preventDefault();
						mentionCommandFn?.(entity);
					}}
				>
					<span class="dot" style:background={typeColors[entity.type] ?? '#8b92a5'}></span>
					<span class="name">{entity.name}</span>
					{#if entity.aliases?.length > 0}
						<span class="alias">{entity.aliases[0]}</span>
					{/if}
					<span class="type">{entity.type}</span>
				</button>
			{/each}
		</div>
	{/if}
</div>

<style>
	.editor-wrapper {
		position: relative;
	}

	.editor-container {
		display: flex;
		align-items: flex-end;
		gap: 0.4rem;
		background: #0f1117;
		border: 1px solid #2d3148;
		border-radius: 6px;
		padding: 0.4rem;
	}

	.compact .editor-container {
		align-items: center;
	}

	.extended .editor-container,
	.expanded .editor-container {
		flex-direction: column;
		align-items: stretch;
		border-top-left-radius: 0;
		border-top-right-radius: 0;
	}

	.editor-mount {
		flex: 1;
		min-width: 0;
	}

	/* Tiptap editor content styling */
	.editor-wrapper :global(.editor-content) {
		outline: none;
		color: #e1e4e8;
		font-size: 0.85rem;
		font-family: inherit;
		line-height: 1.5;
		word-break: break-word;
	}

	.editor-wrapper :global(.editor-content.compact) {
		max-height: 120px;
		overflow-y: auto;
	}

	.expanded :global(.editor-content.compact) {
		min-height: 120px;
		max-height: 40vh;
	}

	.editor-wrapper :global(.editor-content.extended) {
		min-height: 200px;
		max-height: 60vh;
		overflow-y: auto;
	}

	/* Raw editor (source view) */
	.raw-editor {
		flex: 1;
		min-width: 0;
		min-height: 200px;
		max-height: 60vh;
		background: transparent;
		color: #a0a8b8;
		border: none;
		outline: none;
		resize: none;
		font-family: 'JetBrains Mono', monospace;
		font-size: 0.8rem;
		line-height: 1.5;
		padding: 0;
		tab-size: 2;
	}

	.compact .raw-editor {
		min-height: 120px;
		max-height: 40vh;
	}

	.editor-wrapper :global(.editor-content p) {
		margin: 0;
	}

	.editor-wrapper :global(.editor-content p + p) {
		margin-top: 0.4em;
	}

	/* Heading styles (extended mode) */
	.editor-wrapper :global(.editor-content h1) {
		font-size: 1.4rem;
		font-weight: 700;
		margin: 0.8em 0 0.3em;
		color: #f1f3f5;
	}

	.editor-wrapper :global(.editor-content h2) {
		font-size: 1.15rem;
		font-weight: 600;
		margin: 0.7em 0 0.25em;
		color: #f1f3f5;
	}

	.editor-wrapper :global(.editor-content h3) {
		font-size: 1rem;
		font-weight: 600;
		margin: 0.6em 0 0.2em;
		color: #f1f3f5;
	}

	/* List styles */
	.editor-wrapper :global(.editor-content ul),
	.editor-wrapper :global(.editor-content ol) {
		padding-left: 1.2em;
		margin: 0.3em 0;
	}

	.editor-wrapper :global(.editor-content li) {
		margin: 0.1em 0;
	}

	/* Blockquote */
	.editor-wrapper :global(.editor-content blockquote) {
		border-left: 3px solid #3b82f6;
		padding-left: 0.75em;
		margin: 0.4em 0;
		color: #a0a8b8;
	}

	/* Code */
	.editor-wrapper :global(.editor-content code) {
		background: #1e2235;
		padding: 0.15em 0.3em;
		border-radius: 3px;
		font-size: 0.8rem;
		font-family: 'JetBrains Mono', monospace;
	}

	.editor-wrapper :global(.editor-content pre) {
		background: #1e2235;
		padding: 0.6em;
		border-radius: 6px;
		margin: 0.4em 0;
		overflow-x: auto;
	}

	.editor-wrapper :global(.editor-content pre code) {
		background: none;
		padding: 0;
	}

	/* Inline formatting */
	.editor-wrapper :global(.editor-content strong) {
		font-weight: 700;
		color: #f1f3f5;
	}

	.editor-wrapper :global(.editor-content em) {
		font-style: italic;
	}

	.editor-wrapper :global(.editor-content s) {
		text-decoration: line-through;
		color: #8b92a5;
	}

	/* Mentions */
	.editor-wrapper :global(.mention) {
		color: #8b5cf6;
		background: rgba(139, 92, 246, 0.12);
		padding: 0.1em 0.3em;
		border-radius: 3px;
		font-weight: 500;
		cursor: pointer;
	}

	.editor-wrapper :global(.mention::before) {
		content: '#';
	}

	/* Placeholder */
	.editor-wrapper :global(.tiptap p.is-editor-empty:first-child::before) {
		content: attr(data-placeholder);
		color: #8b92a5;
		float: left;
		height: 0;
		pointer-events: none;
	}

	/* Toolbar */
	.toolbar {
		display: flex;
		gap: 0.15rem;
		padding: 0.3rem 0.4rem;
		background: #0f1117;
		border: 1px solid #2d3148;
		border-bottom: none;
		border-radius: 6px 6px 0 0;
		flex-wrap: wrap;
	}

	.tool {
		display: flex;
		align-items: center;
		justify-content: center;
		min-width: 28px;
		height: 26px;
		padding: 0 0.35rem;
		background: none;
		border: none;
		border-radius: 4px;
		color: #8b92a5;
		font-family: inherit;
		font-size: 0.72rem;
		font-weight: 600;
		cursor: pointer;
	}

	.tool:hover {
		background: #1e2235;
		color: #e1e4e8;
	}

	.tool.active {
		background: #1e2235;
		color: #3b82f6;
	}

	.raw-toggle {
		margin-left: auto;
		font-family: 'JetBrains Mono', monospace;
		font-size: 0.65rem;
		letter-spacing: -0.02em;
	}

	.separator {
		width: 1px;
		height: 18px;
		background: #2d3148;
		align-self: center;
		margin: 0 0.15rem;
	}

	/* Bottom bar (expanded compact mode) */
	.editor-bottom-bar {
		display: flex;
		justify-content: flex-end;
		align-items: center;
		gap: 0.4rem;
		padding: 0.3rem 0.4rem 0;
	}

	/* Send button (compact mode) */
	.send-btn {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 32px;
		height: 32px;
		background: #3b82f6;
		border: none;
		border-radius: 6px;
		color: white;
		cursor: pointer;
		flex-shrink: 0;
	}

	.send-btn:disabled {
		background: #1e2235;
		color: #8b92a5;
		cursor: not-allowed;
	}

	.send-btn:not(:disabled):hover {
		background: #2563eb;
	}

	/* Expand button */
	.expand-btn {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 28px;
		height: 28px;
		background: none;
		border: none;
		border-radius: 4px;
		color: #8b92a5;
		cursor: pointer;
		flex-shrink: 0;
	}

	.expand-btn:hover {
		background: #1e2235;
		color: #e1e4e8;
	}

	/* Mention popup */
	.mention-popup {
		position: absolute;
		background: #161822;
		border: 1px solid #2d3148;
		border-radius: 6px;
		max-height: 200px;
		overflow-y: auto;
		z-index: 50;
		box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.3);
		min-width: 200px;
	}

	.suggestion {
		display: flex;
		align-items: center;
		gap: 0.4rem;
		padding: 0.4rem 0.5rem;
		width: 100%;
		border: none;
		background: none;
		color: #e1e4e8;
		font-family: inherit;
		font-size: 0.8rem;
		cursor: pointer;
		text-align: left;
	}

	.suggestion:hover, .suggestion.selected {
		background: #1e2235;
	}

	.dot {
		width: 7px;
		height: 7px;
		border-radius: 50%;
		flex-shrink: 0;
	}

	.name {
		flex: 1;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.alias {
		color: #8b92a5;
		font-size: 0.7rem;
	}

	.type {
		color: #8b92a5;
		font-size: 0.7rem;
		flex-shrink: 0;
	}
</style>
