/**
 * Slash command ProseMirror plugin for Tiptap.
 *
 * Detects '/' typed at the start of an empty block and shows a floating menu
 * with block type options. Keyboard navigation: arrow keys + Enter + Escape.
 */

import { Plugin, PluginKey } from '@tiptap/pm/state';
import type { EditorView } from '@tiptap/pm/view';
import type { Editor } from '@tiptap/core';

/** Inline SVG icons for slash menu items (16×16, stroke-based, currentColor) */
const SLASH_ICONS: Record<string, string> = {
	text: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="4" y1="3" x2="12" y2="3"/><line x1="8" y1="3" x2="8" y2="13"/><line x1="6" y1="13" x2="10" y2="13"/></svg>',
	heading1: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M2 3v10M8 3v10M2 8h6"/><text x="10.5" y="13" font-size="7" fill="currentColor" stroke="none" font-family="system-ui" font-weight="700">1</text></svg>',
	heading2: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M2 3v10M8 3v10M2 8h6"/><text x="10.5" y="13" font-size="7" fill="currentColor" stroke="none" font-family="system-ui" font-weight="700">2</text></svg>',
	heading3: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M2 3v10M8 3v10M2 8h6"/><text x="10.5" y="13" font-size="7" fill="currentColor" stroke="none" font-family="system-ui" font-weight="700">3</text></svg>',
	bulletList: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="6" y1="4" x2="14" y2="4"/><line x1="6" y1="8" x2="14" y2="8"/><line x1="6" y1="12" x2="14" y2="12"/><circle cx="3" cy="4" r="1" fill="currentColor" stroke="none"/><circle cx="3" cy="8" r="1" fill="currentColor" stroke="none"/><circle cx="3" cy="12" r="1" fill="currentColor" stroke="none"/></svg>',
	orderedList: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="7" y1="4" x2="14" y2="4"/><line x1="7" y1="8" x2="14" y2="8"/><line x1="7" y1="12" x2="14" y2="12"/><text x="1.5" y="5.5" font-size="5" fill="currentColor" stroke="none" font-family="system-ui" font-weight="600">1</text><text x="1.5" y="9.5" font-size="5" fill="currentColor" stroke="none" font-family="system-ui" font-weight="600">2</text><text x="1.5" y="13.5" font-size="5" fill="currentColor" stroke="none" font-family="system-ui" font-weight="600">3</text></svg>',
	taskList: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="5" height="5" rx="1"/><polyline points="3.5 4.5 4.5 5.5 6 3.5"/><line x1="9" y1="4.5" x2="14" y2="4.5"/><rect x="2" y="9" width="5" height="5" rx="1"/><line x1="9" y1="11.5" x2="14" y2="11.5"/></svg>',
	codeBlock: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="1.5" y="1.5" width="13" height="13" rx="2"/><polyline points="5 6 3.5 8 5 10"/><polyline points="11 6 12.5 8 11 10"/><line x1="9" y1="5" x2="7" y2="11"/></svg>',
	blockquote: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="2" x2="3" y2="14"/><line x1="7" y1="4" x2="14" y2="4"/><line x1="7" y1="8" x2="14" y2="8"/><line x1="7" y1="12" x2="12" y2="12"/></svg>',
	horizontalRule: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="2" y1="8" x2="14" y2="8"/><circle cx="4" cy="8" r="0.5" fill="currentColor"/><circle cx="8" cy="8" r="0.5" fill="currentColor"/><circle cx="12" cy="8" r="0.5" fill="currentColor"/></svg>',
	image: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="1.5" y="2.5" width="13" height="11" rx="2"/><circle cx="5.5" cy="6" r="1.5"/><path d="M14.5 10.5l-3.5-3.5-5 5"/></svg>',
};

export interface SlashMenuItem {
	title: string;
	icon: string;
	description: string;
	command: (editor: Editor) => void;
}

export const SLASH_ITEMS: SlashMenuItem[] = [
	{
		title: 'Text',
		icon: 'text',
		description: 'Plain paragraph text',
		command: (e) => e.chain().focus().setParagraph().run(),
	},
	{
		title: 'Heading 1',
		icon: 'heading1',
		description: 'Large section heading',
		command: (e) => e.chain().focus().setHeading({ level: 1 }).run(),
	},
	{
		title: 'Heading 2',
		icon: 'heading2',
		description: 'Medium section heading',
		command: (e) => e.chain().focus().setHeading({ level: 2 }).run(),
	},
	{
		title: 'Heading 3',
		icon: 'heading3',
		description: 'Small section heading',
		command: (e) => e.chain().focus().setHeading({ level: 3 }).run(),
	},
	{
		title: 'Bullet List',
		icon: 'bulletList',
		description: 'Unordered bullet list',
		command: (e) => e.chain().focus().toggleBulletList().run(),
	},
	{
		title: 'Numbered List',
		icon: 'orderedList',
		description: 'Ordered numbered list',
		command: (e) => e.chain().focus().toggleOrderedList().run(),
	},
	{
		title: 'Task List',
		icon: 'taskList',
		description: 'Checklist with checkboxes',
		command: (e) => e.chain().focus().toggleTaskList().run(),
	},
	{
		title: 'Code Block',
		icon: 'codeBlock',
		description: 'Syntax-highlighted code block',
		command: (e) => e.chain().focus().toggleCodeBlock().run(),
	},
	{
		title: 'Blockquote',
		icon: 'blockquote',
		description: 'Indented quote block',
		command: (e) => e.chain().focus().toggleBlockquote().run(),
	},
	{
		title: 'Horizontal Rule',
		icon: 'horizontalRule',
		description: 'Visual divider line',
		command: (e) => e.chain().focus().setHorizontalRule().run(),
	},
	{
		title: 'Image',
		icon: 'image',
		description: 'Insert an image from URL',
		command: (e) => {
			const event = new CustomEvent('slash-insert-image', { bubbles: true, composed: true });
			(e.view.dom as HTMLElement).dispatchEvent(event);
		},
	},
	{
		title: 'Code Snippet',
		icon: 'codeBlock',
		description: 'Create a new code snippet note',
		command: (e) => {
			const event = new CustomEvent('slash-create-typed-note', { bubbles: true, composed: true, detail: { type: 'CODE' } });
			(e.view.dom as HTMLElement).dispatchEvent(event);
		},
	},
	{
		title: 'Voice Note',
		icon: 'text',
		description: 'Create a new voice recording note',
		command: (e) => {
			const event = new CustomEvent('slash-create-typed-note', { bubbles: true, composed: true, detail: { type: 'AUDIO' } });
			(e.view.dom as HTMLElement).dispatchEvent(event);
		},
	},
];

const pluginKey = new PluginKey('slashCommand');

export function createSlashCommandPlugin(editor: Editor, shadowRoot: ShadowRoot): Plugin {
	let menuEl: HTMLDivElement | null = null;
	let selectedIndex = 0;
	let filteredItems: SlashMenuItem[] = [];
	let query = '';
	let active = false;
	let triggerPos = -1;

	function show(view: EditorView) {
		if (!menuEl) {
			menuEl = document.createElement('div');
			menuEl.className = 'slash-menu';
			shadowRoot.appendChild(menuEl);
		}
		active = true;
		selectedIndex = 0;
		query = '';
		filteredItems = SLASH_ITEMS;
		updateMenuContent();
		positionMenu(view);
		menuEl.style.display = 'block';
	}

	function hide() {
		active = false;
		query = '';
		triggerPos = -1;
		if (menuEl) menuEl.style.display = 'none';
	}

	function updateMenuContent() {
		if (!menuEl) return;
		menuEl.innerHTML = `<div class="slash-menu__header">Insert block</div>` +
			filteredItems
			.map(
				(item, i) =>
					`<div class="slash-menu-item${i === selectedIndex ? ' selected' : ''}" data-index="${i}">
						<span class="slash-menu-icon">${SLASH_ICONS[item.icon] || item.icon}</span>
						<div class="slash-menu-text">
							<div class="slash-menu-title">${item.title}</div>
							<div class="slash-menu-desc">${item.description}</div>
						</div>
						${i === selectedIndex ? '<span class="slash-menu-hint">Enter</span>' : ''}
					</div>`,
			)
			.join('');

		// Click handlers
		menuEl.querySelectorAll('.slash-menu-item').forEach((el) => {
			el.addEventListener('pointerdown', (e) => {
				e.preventDefault();
				const idx = parseInt((el as HTMLElement).dataset.index || '0');
				executeItem(idx);
			});
			el.addEventListener('pointerenter', () => {
				selectedIndex = parseInt((el as HTMLElement).dataset.index || '0');
				updateMenuContent();
			});
		});
	}

	function positionMenu(view: EditorView) {
		if (!menuEl) return;
		const { from } = view.state.selection;
		const coords = view.coordsAtPos(from);
		const shadowHost = shadowRoot.host as HTMLElement;
		const hostRect = shadowHost.getBoundingClientRect();

		let left = coords.left - hostRect.left;
		const menuWidth = 240;
		const maxLeft = window.innerWidth - menuWidth - 8 - hostRect.left;
		left = Math.max(4, Math.min(left, maxLeft));
		menuEl.style.left = `${left}px`;
		menuEl.style.top = `${coords.bottom - hostRect.top + 4}px`;
	}

	function filterItems() {
		const q = query.toLowerCase();
		filteredItems = q
			? SLASH_ITEMS.filter(
					(item) =>
						item.title.toLowerCase().includes(q) || item.description.toLowerCase().includes(q),
				)
			: SLASH_ITEMS;
		selectedIndex = Math.min(selectedIndex, Math.max(0, filteredItems.length - 1));
		updateMenuContent();
	}

	function executeItem(index: number) {
		const item = filteredItems[index];
		if (!item) return;

		// Delete the slash + query text
		const { state } = editor.view;
		const tr = state.tr.delete(triggerPos, state.selection.from);
		editor.view.dispatch(tr);

		item.command(editor);
		hide();
	}

	return new Plugin({
		key: pluginKey,
		props: {
			handleKeyDown(view, event) {
				if (active) {
					if (event.key === 'ArrowDown') {
						event.preventDefault();
						selectedIndex = (selectedIndex + 1) % filteredItems.length;
						updateMenuContent();
						return true;
					}
					if (event.key === 'ArrowUp') {
						event.preventDefault();
						selectedIndex = (selectedIndex - 1 + filteredItems.length) % filteredItems.length;
						updateMenuContent();
						return true;
					}
					if (event.key === 'Enter') {
						event.preventDefault();
						executeItem(selectedIndex);
						return true;
					}
					if (event.key === 'Escape') {
						event.preventDefault();
						hide();
						return true;
					}
					if (event.key === 'Backspace') {
						if (query.length === 0) {
							// Backspace deletes the '/', close menu
							hide();
							return false; // let ProseMirror handle the deletion
						}
						query = query.slice(0, -1);
						filterItems();
						return false; // let ProseMirror handle the deletion
					}
					if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
						query += event.key;
						filterItems();
						if (filteredItems.length === 0) {
							hide();
						}
						return false; // let ProseMirror insert the character
					}
				}
				return false;
			},

			handleTextInput(view, from, to, text) {
				if (text === '/' && !active) {
					// Check if cursor is at start of an empty block
					const { $from } = view.state.selection;
					const isAtStart = $from.parentOffset === 0;
					const isEmpty = $from.parent.textContent === '';
					if (isAtStart && isEmpty) {
						triggerPos = from;
						// Defer show to after the '/' is inserted
						setTimeout(() => show(view), 0);
					}
				}
				return false;
			},
		},

		view() {
			return {
				update(view) {
					if (active && menuEl) {
						positionMenu(view);
					}
				},
				destroy() {
					if (menuEl) {
						menuEl.remove();
						menuEl = null;
					}
				},
			};
		},
	});
}
