<script lang="ts">
	import { onMount, onDestroy, tick } from 'svelte';
	import { Editor } from '@tiptap/core';
	import StarterKit from '@tiptap/starter-kit';
	import Image from '@tiptap/extension-image';
	import Link from '@tiptap/extension-link';
	import Placeholder from '@tiptap/extension-placeholder';
	import { Markdown } from 'tiptap-markdown';
	import { TiptapMention } from '$lib/editor/tiptapMention';
	import { createTiptapWikiLink } from '$lib/editor/tiptapWikiLink';
	import { TiptapSlashCommand } from '$lib/editor/tiptapSlashCommand';
	import { storeImage, getImageUrl, resolveImageUrls, objectUrlToKaImg } from '$lib/db/imageStore';
	import BubbleMenu from '@tiptap/extension-bubble-menu';
	import { Table, TableRow, TableCell, TableHeader } from '@tiptap/extension-table';
	import '$lib/editor/editor.css';

	interface Props {
		content?: string;
		placeholder?: string;
		minHeight?: string;
		/** true = private pages only, false = business only, null/undefined = all */
		wikiScope?: boolean | null;
		onchange?: (markdown: string) => void;
		onsave?: () => void;
		onsavearchive?: () => void;
	}
	let { content = '', placeholder = '', minHeight = '80px', wikiScope = null, onchange, onsave, onsavearchive }: Props = $props();

	let element: HTMLDivElement;
	let bubbleMenuEl: HTMLDivElement;
	let tableMenuEl: HTMLDivElement;
	let wrapper: HTMLDivElement;
	let editor: Editor | null = null;
	let skipNextUpdate = false;
	let lastExternalContent = $state(content);
	// Gate bubble menus: only show after user has intentionally selected text.
	// Uses a debounce timer to ignore iOS transient selections triggered by a tap.
	let hasSelection = false;
	let selectionTimer: ReturnType<typeof setTimeout> | null = null;

	export function getMarkdown(): string {
		if (!editor) return '';
		const raw = editor.storage.markdown.getMarkdown();
		return objectUrlToKaImg(raw);
	}

	export function clear() {
		if (!editor) return;
		editor.commands.clearContent(true);
	}

	function emitChange() {
		if (!editor || skipNextUpdate) {
			skipNextUpdate = false;
			return;
		}
		const md = getMarkdown();
		onchange?.(md);
	}

	function handlePaste(_view: any, event: ClipboardEvent): boolean {
		const items = event.clipboardData?.items;
		if (!items) return false;

		for (const item of items) {
			if (item.type.startsWith('image/')) {
				event.preventDefault();
				const blob = item.getAsFile();
				if (blob) {
					storeImage(blob)
						.then((id) => getImageUrl(id))
						.then((url) => {
							if (url && editor) {
								editor.chain().focus().setImage({ src: url }).run();
							}
						});
				}
				return true;
			}
		}
		return false;
	}

	onMount(async () => {
		let initialContent = content;
		if (content.includes('ka-img:')) {
			initialContent = await resolveImageUrls(content);
		}

		// Wait for bind:this refs (bubbleMenuEl, tableMenuEl, wrapper) to be in DOM
		await tick();

		editor = new Editor({
			element,
			extensions: [
				StarterKit,
				Image.configure({ inline: true, allowBase64: false }),
				Link.configure({ autolink: true, openOnClick: false }),
				Placeholder.configure({ placeholder }),
				Markdown.configure({ html: false, transformCopiedText: true, transformPastedText: true }),
				TiptapMention,
				createTiptapWikiLink(wikiScope),
				TiptapSlashCommand,
				Table.configure({ resizable: false }),
				TableRow,
				TableCell,
				TableHeader,
				BubbleMenu.configure({
					element: bubbleMenuEl,
					shouldShow: ({ editor: e, from, to }) => {
						if (!hasSelection) return false;
						if (e.isActive('image')) return false;
						if (e.isActive('table')) return false;
						return from !== to;
					},
				}),
				BubbleMenu.configure({
					element: tableMenuEl,
					pluginKey: 'tableMenu',
					shouldShow: ({ editor: e }) => {
						if (!hasSelection) return false;
						return e.isActive('table');
					},
				}),
			],
			content: initialContent,
			editorProps: {
				handlePaste,
				handleKeyDown: (_view, event) => {
					if (event.key === 'Enter' && (event.ctrlKey || event.metaKey) && event.shiftKey) {
						onsavearchive?.();
						return true;
					}
					if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
						onsave?.();
						return true;
					}
					return false;
				},
				attributes: {
					style: `min-height: ${minHeight}`
				}
			},
			onUpdate: () => emitChange(),
			onSelectionUpdate: ({ editor: e }) => {
				const { from, to, empty } = e.state.selection;
				if (selectionTimer) clearTimeout(selectionTimer);
				// Only count as intentional selection if range spans at least 2 chars.
				// Filters out iOS tap-induced caret placements (from === to or range of 1).
				if (!empty && to - from >= 2) {
					selectionTimer = setTimeout(() => {
						hasSelection = true;
						if (!e.isActive('table')) bubbleMenuEl?.classList.add('ka-bubble-active');
						if (e.isActive('table')) tableMenuEl?.classList.add('ka-bubble-active');
					}, 50);
				} else if (e.isActive('table')) {
					// Table cursor (caret inside table) — show table menu without text selection
					hasSelection = true;
					tableMenuEl?.classList.add('ka-bubble-active');
					bubbleMenuEl?.classList.remove('ka-bubble-active');
				} else {
					hasSelection = false;
					bubbleMenuEl?.classList.remove('ka-bubble-active');
					tableMenuEl?.classList.remove('ka-bubble-active');
				}
			},
			onBlur: () => {
				if (selectionTimer) clearTimeout(selectionTimer);
				hasSelection = false;
				bubbleMenuEl?.classList.remove('ka-bubble-active');
				tableMenuEl?.classList.remove('ka-bubble-active');
			},
			onTransaction: ({ editor: e }) => {
				// Force Svelte reactivity by reassigning
				editor = e;
			}
		});

		// Bind to DOM — Tiptap needs this for Svelte
		element.querySelector('.ProseMirror')?.setAttribute('data-ready', 'true');
	});

	// React to external content changes
	$effect(() => {
		if (!editor || content === lastExternalContent) return;
		lastExternalContent = content;

		const currentMd = getMarkdown();
		if (content === currentMd) return;

		skipNextUpdate = true;
		if (content.includes('ka-img:')) {
			resolveImageUrls(content).then(resolved => {
				editor?.commands.setContent(resolved);
			});
		} else {
			editor.commands.setContent(content);
		}
	});

	onDestroy(() => {
		if (selectionTimer) clearTimeout(selectionTimer);
		editor?.destroy();
	});
</script>

<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
<div class="ka-editor-wrapper" bind:this={wrapper} onclick={(e) => e.stopPropagation()}>
	<div bind:this={element}></div>
	<div bind:this={tableMenuEl} class="ka-bubble-menu ka-table-menu">
		<span class="bubble-label">Zeile</span>
		<button class="bubble-btn" title="Zeile darunter einfügen"
			onmousedown={(e) => { e.preventDefault(); editor?.commands.addRowAfter(); }}
		>+</button>
		<button class="bubble-btn" title="Zeile löschen"
			onmousedown={(e) => { e.preventDefault(); editor?.commands.deleteRow(); }}
		>−</button>
		<span class="bubble-sep"></span>
		<span class="bubble-label">Spalte</span>
		<button class="bubble-btn" title="Spalte rechts einfügen"
			onmousedown={(e) => { e.preventDefault(); editor?.commands.addColumnAfter(); }}
		>+</button>
		<button class="bubble-btn" title="Spalte löschen"
			onmousedown={(e) => { e.preventDefault(); editor?.commands.deleteColumn(); }}
		>−</button>
		<span class="bubble-sep"></span>
		<button class="bubble-btn bubble-btn-danger" title="Tabelle löschen"
			onmousedown={(e) => { e.preventDefault(); editor?.commands.deleteTable(); }}
		>✕</button>
	</div>
	<div bind:this={bubbleMenuEl} class="ka-bubble-menu">
		<button class="bubble-btn"
			class:bubble-btn-active={editor?.isActive('bold')}
			onpointerdown={(e) => { e.preventDefault(); editor?.chain().focus().toggleBold().run(); }}
			title="Bold"
		>B</button>
		<button class="bubble-btn bubble-btn-italic"
			class:bubble-btn-active={editor?.isActive('italic')}
			onpointerdown={(e) => { e.preventDefault(); editor?.chain().focus().toggleItalic().run(); }}
			title="Italic"
		>I</button>
		<button class="bubble-btn"
			class:bubble-btn-active={editor?.isActive('heading', { level: 2 })}
			onpointerdown={(e) => { e.preventDefault(); editor?.chain().focus().toggleHeading({ level: 2 }).run(); }}
			title="Heading 2"
		>H2</button>
		<button class="bubble-btn"
			class:bubble-btn-active={editor?.isActive('link')}
			onpointerdown={(e) => {
				e.preventDefault();
				const href = editor?.getAttributes('link').href ?? '';
				const url = window.prompt('URL:', href);
				if (url) editor?.chain().focus().setLink({ href: url }).run();
				else if (url === '') editor?.chain().focus().unsetLink().run();
			}}
			title="Link"
		>↗</button>
	</div>
</div>
