<script lang="ts">
	import { Code, Palette, X, Globe, Layers } from 'lucide-svelte';
	import { onMount, onDestroy, tick } from 'svelte';
	import { EditorState, Compartment } from '@codemirror/state';
	import { EditorView, keymap } from '@codemirror/view';
	import { html } from '@codemirror/lang-html';
	import { css } from '@codemirror/lang-css';
	import { defaultKeymap } from '@codemirror/commands';
	import { vim } from '@replit/codemirror-vim';
	import { emacs } from '@replit/codemirror-emacs';
	import { state as appState } from './state.svelte';
	import { createTheme } from './theme';
	import type { Item } from './types';

	type EditMode = 'normal' | 'vim' | 'emacs';

	const themeCompartment = new Compartment();
	const keymapCompartment = new Compartment();

	let { mode = 'item', item, onClose }: { mode?: 'item' | 'global'; item?: Item; onClose: () => void } = $props();

	let activeTab = $state<'html' | 'css' | 'shared' | 'app'>(mode === 'item' ? 'html' : 'shared');
	let editMode = $state<EditMode>('normal');
	let previousItemId = $state<string | null>(null);

	function getKeymapExtension(m: EditMode) {
		switch (m) {
			case 'vim':
				return vim();
			case 'emacs':
				return emacs();
			default:
				return [];
		}
	}

	function cycleEditMode() {
		const modes: EditMode[] = ['normal', 'vim', 'emacs'];
		const idx = modes.indexOf(editMode);
		editMode = modes[(idx + 1) % modes.length];
	}

	let htmlContainer: HTMLDivElement | null = $state(null);
	let cssContainer: HTMLDivElement | null = $state(null);
	let sharedContainer: HTMLDivElement | null = $state(null);
	let appContainer: HTMLDivElement | null = $state(null);

	let htmlEditor: EditorView | null = null;
	let cssEditor: EditorView | null = null;
	let sharedEditor: EditorView | null = null;
	let appEditor: EditorView | null = null;

	function createItemEditor(
		container: HTMLDivElement,
		lang: 'html' | 'css',
		initialValue: string
	): EditorView {
		const extensions = [
			keymapCompartment.of(getKeymapExtension(editMode)),
			keymap.of(defaultKeymap),
			themeCompartment.of(createTheme()),
			lang === 'html' ? html() : css(),
			EditorView.updateListener.of((update) => {
				if (update.docChanged && item) {
					const value = update.state.doc.toString();
					if (lang === 'html') {
						appState.updateItem(item.id, { html: value });
					} else {
						appState.updateItem(item.id, { css: value });
					}
				}
			})
		];

		return new EditorView({
			state: EditorState.create({ doc: initialValue, extensions }),
			parent: container
		});
	}

	function createGlobalEditor(
		container: HTMLDivElement,
		type: 'shared' | 'app',
		initialValue: string
	): EditorView {
		const extensions = [
			keymapCompartment.of(getKeymapExtension(editMode)),
			keymap.of(defaultKeymap),
			themeCompartment.of(createTheme()),
			css(),
			EditorView.updateListener.of((update) => {
				if (update.docChanged) {
					const value = update.state.doc.toString();
					if (type === 'shared') {
						appState.updateSharedCss(value);
					} else {
						appState.updateAppCss(value);
					}
				}
			})
		];

		return new EditorView({
			state: EditorState.create({ doc: initialValue, extensions }),
			parent: container
		});
	}

	onMount(() => {
		if (mode === 'item' && item) {
			if (htmlContainer) {
				htmlEditor = createItemEditor(htmlContainer, 'html', item.html);
			}
			if (cssContainer) {
				cssEditor = createItemEditor(cssContainer, 'css', item.css);
			}
		}
		if (sharedContainer) {
			sharedEditor = createGlobalEditor(sharedContainer, 'shared', appState.manifest.sharedCss);
		}
		if (appContainer) {
			appEditor = createGlobalEditor(appContainer, 'app', appState.manifest.appCss);
		}
	});

	onDestroy(() => {
		htmlEditor?.destroy();
		cssEditor?.destroy();
		sharedEditor?.destroy();
		appEditor?.destroy();
	});

	let previousAppCss = $state(appState.manifest.appCss);

	$effect(() => {
		const currentAppCss = appState.manifest.appCss;
		if (currentAppCss !== previousAppCss) {
			previousAppCss = currentAppCss;
			tick().then(() => {
				const newTheme = createTheme();
				htmlEditor?.dispatch({ effects: themeCompartment.reconfigure(newTheme) });
				cssEditor?.dispatch({ effects: themeCompartment.reconfigure(newTheme) });
				sharedEditor?.dispatch({ effects: themeCompartment.reconfigure(newTheme) });
				appEditor?.dispatch({ effects: themeCompartment.reconfigure(newTheme) });
			});
		}
	});

	$effect(() => {
		const ext = getKeymapExtension(editMode);
		htmlEditor?.dispatch({ effects: keymapCompartment.reconfigure(ext) });
		cssEditor?.dispatch({ effects: keymapCompartment.reconfigure(ext) });
		sharedEditor?.dispatch({ effects: keymapCompartment.reconfigure(ext) });
		appEditor?.dispatch({ effects: keymapCompartment.reconfigure(ext) });
	});

	$effect(() => {
		if (mode === 'item' && item && item.id !== previousItemId) {
			previousItemId = item.id;
			if (htmlEditor) {
				htmlEditor.dispatch({
					changes: { from: 0, to: htmlEditor.state.doc.length, insert: item.html }
				});
			}
			if (cssEditor) {
				cssEditor.dispatch({
					changes: { from: 0, to: cssEditor.state.doc.length, insert: item.css }
				});
			}
		}
	});
</script>

<div class="panel">
	<div class="header">
		<div class="tabs">
			{#if mode === 'item'}
				<button class:active={activeTab === 'html'} onclick={() => (activeTab = 'html')} title="HTML"><Code size={12} /></button>
				<button class:active={activeTab === 'css'} onclick={() => (activeTab = 'css')} title="Item CSS"><Palette size={12} /></button>
			{/if}
			<button class:active={activeTab === 'shared'} onclick={() => (activeTab = 'shared')} title="Shared CSS"><Layers size={12} /></button>
			<button class:active={activeTab === 'app'} onclick={() => (activeTab = 'app')} title="App CSS"><Globe size={12} /></button>
		</div>
		<button class="mode" onclick={cycleEditMode} title="Editor mode: {editMode}">{editMode}</button>
		<button class="close" onclick={onClose} title="Close"><X size={12} /></button>
	</div>
	<div class="editor-container">
		{#if mode === 'item'}
			<div class="editor" class:hidden={activeTab !== 'html'} bind:this={htmlContainer}></div>
			<div class="editor" class:hidden={activeTab !== 'css'} bind:this={cssContainer}></div>
		{/if}
		<div class="editor" class:hidden={activeTab !== 'shared'} bind:this={sharedContainer}></div>
		<div class="editor" class:hidden={activeTab !== 'app'} bind:this={appContainer}></div>
	</div>
</div>

<style>
	.panel {
		width: 100%;
		height: 100%;
		background: var(--surface, #282c34);
		display: flex;
		flex-direction: column;
		border-left: 1px solid var(--border, #333);
	}

	.header {
		display: flex;
		background: var(--surface, #282c34);
		border-bottom: 1px solid var(--border, #333);
		flex-shrink: 0;
	}

	.tabs {
		display: flex;
		flex: 1;
	}

	.tabs button {
		padding: 6px 10px;
		background: transparent;
		border: none;
		color: var(--text-dim, #666);
		cursor: pointer;
	}

	.tabs button:hover {
		color: var(--text, #fff);
	}

	.tabs button.active {
		color: var(--accent, #4a9eff);
	}

	.mode {
		padding: 6px 8px;
		background: transparent;
		border: none;
		color: var(--text-dim, #666);
		cursor: pointer;
		font-size: 10px;
		text-transform: uppercase;
	}

	.mode:hover {
		color: var(--text, #fff);
	}

	.close {
		padding: 6px 8px;
		background: transparent;
		border: none;
		color: var(--text-dim, #666);
		cursor: pointer;
	}

	.close:hover {
		color: var(--text, #fff);
	}

	.editor-container {
		flex: 1;
		position: relative;
		overflow: hidden;
	}

	.editor {
		position: absolute;
		inset: 0;
	}

	.editor.hidden {
		display: none;
	}
</style>
