<script lang="ts">
	import {
		notebooks,
		notes,
		tags,
		viewMode,
		activeNotebook,
		activeNotePath,
		activeNote,
		activeTag,
		showSearch,
		sidebarCollapsed,
		showSettings,
		showInfo,
		notebookIcons,
		appConfig,
		quickAccessPaths,
		collapsedNotebooks,
		rootNoteCount,
		notebookSortMode,
		notebookOrder
	} from '$lib/stores/app';
	import { getNotebooks, getAllTags, createNotebook, deleteNotebook, renameNotebook, moveNotebook, getNotebookIcons, setNotebookIcon, saveAttachment, getQuickAccess, addQuickAccess, removeQuickAccess, emptyTrash, moveNote, readNote, getNotes, countRootNotes } from '$lib/api';
	import { open as openDialog } from '@tauri-apps/plugin-dialog';
	import { readFile } from '@tauri-apps/plugin-fs';
	import { convertFileSrc } from '@tauri-apps/api/core';
	import type { NotebookEntry } from '$lib/types';

	let { onViewChanged = () => {} }: {
		onViewChanged?: () => void;
	} = $props();

	const modKey = navigator.platform.startsWith('Mac') ? '⌘' : 'Ctrl';
	const isMobile = /android|ios/i.test(navigator.userAgent);

	let editingNotebook = $state<string | null>(null);
	let editValue = $state('');
	let newNotebookName = $state('');
	let showNewNotebook = $state(false);
	let dropTargetPath = $state<string | null>(null);
	let dropPosition = $state<'above' | 'into' | 'below' | null>(null);
	let draggedNotebookPath = $state<string | null>(null);

	// Apply manual ordering recursively when in 'manual' sort mode.
	// In 'alphabetical' mode the Rust scanner already sorts, so we pass through unchanged.
	function sortNotebooksTree(tree: NotebookEntry[]): NotebookEntry[] {
		if ($notebookSortMode !== 'manual') return tree;
		const order = $notebookOrder;
		const sorted = [...tree].sort((a, b) => {
			const oa = order[a.path] ?? Number.MAX_SAFE_INTEGER;
			const ob = order[b.path] ?? Number.MAX_SAFE_INTEGER;
			if (oa !== ob) return oa - ob;
			return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
		});
		return sorted.map(nb => ({ ...nb, children: sortNotebooksTree(nb.children) }));
	}
	let sortedNotebooks = $derived(sortNotebooksTree($notebooks));
	let contextMenu = $state<{ x: number; y: number; notebook: NotebookEntry } | null>(null);
	let trashContextMenu = $state<{ x: number; y: number } | null>(null);
	let tagsCollapsed = $state(true);
	let deleteConfirm = $state<NotebookEntry | null>(null);
	function toggleCollapse(path: string, e: MouseEvent) {
		e.stopPropagation();
		if ($collapsedNotebooks.includes(path)) {
			$collapsedNotebooks = $collapsedNotebooks.filter(p => p !== path);
		} else {
			$collapsedNotebooks = [...$collapsedNotebooks, path];
		}
	}


	export async function refresh() {
		try {
			if (isMobile) {
				// On mobile, parallelize and skip getAllTags (derive from $notes instead)
				const [nbs, icons, qaNotes, rootCount] = await Promise.all([
					getNotebooks(),
					getNotebookIcons(),
					getQuickAccess(),
					countRootNotes(),
				]);
				$notebooks = nbs;
				$notebookIcons = icons;
				$quickAccessPaths = qaNotes.map(n => n.relative_path);
				$rootNoteCount = rootCount;
			} else {
				const [nbs, rootCount] = await Promise.all([getNotebooks(), countRootNotes()]);
				$notebooks = nbs;
				$rootNoteCount = rootCount;
				$tags = await getAllTags();
				$notebookIcons = await getNotebookIcons();
				const qaNotes = await getQuickAccess();
				$quickAccessPaths = qaNotes.map(n => n.relative_path);
			}
		} catch (e) {
			console.error('Failed to refresh sidebar:', e);
		}
	}

	function selectAllNotes() {
		$viewMode = 'all';
		$activeNotebook = null;
		$activeTag = null;
		onViewChanged();
	}

	function selectUnfiled() {
		const vault = $appConfig?.active_vault;
		if (!vault) return;
		if ($viewMode === 'notebook' && $activeNotebook?.relative_path === '') {
			selectAllNotes();
			return;
		}
		$viewMode = 'notebook';
		$activeNotebook = { name: 'Unfiled Notes', path: vault, relative_path: '', children: [], note_count: $rootNoteCount };
		$activeTag = null;
		onViewChanged();
	}

	function selectNotebook(nb: NotebookEntry) {
		// Deselect if clicking the already-active notebook
		if ($viewMode === 'notebook' && $activeNotebook?.path === nb.path) {
			selectAllNotes();
			return;
		}
		$viewMode = 'notebook';
		$activeNotebook = nb;
		$activeTag = null;
		onViewChanged();
	}

	function selectTag(tag: string) {
		$viewMode = 'tag';
		$activeTag = tag;
		$activeNotebook = null;
		onViewChanged();
	}

	function selectQuickAccess() {
		$viewMode = 'quickaccess';
		$activeNotebook = null;
		$activeTag = null;
		onViewChanged();
	}

	function selectDaily() {
		$viewMode = 'daily';
		$activeNotebook = null;
		$activeTag = null;
		onViewChanged();
	}

	function selectTrash() {
		$viewMode = 'trash';
		$activeNotebook = null;
		$activeTag = null;
		onViewChanged();
	}

	let newNotebookParent = $state<NotebookEntry | null>(null);

	async function handleCreateNotebook() {
		if (!newNotebookName.trim()) return;
		try {
			const parentRel = newNotebookParent?.relative_path ?? null;
			await createNotebook(parentRel, newNotebookName.trim());
			newNotebookName = '';
			showNewNotebook = false;
			newNotebookParent = null;
			await refresh();
		} catch (e) {
			console.error('Failed to create notebook:', e);
		}
	}

	function startNewSubNotebook(nb: NotebookEntry) {
		contextMenu = null;
		newNotebookParent = nb;
		newNotebookName = '';
		showNewNotebook = true;
		// Expand parent so the new child is visible
		if ($collapsedNotebooks.includes(nb.path)) {
			$collapsedNotebooks = $collapsedNotebooks.filter(p => p !== nb.path);
		}
	}

	async function handleRename(nb: NotebookEntry) {
		if (!editValue.trim() || editValue.trim() === nb.name) {
			editingNotebook = null;
			return;
		}
		try {
			await renameNotebook(nb.path, editValue.trim());
			editingNotebook = null;
			await refresh();
		} catch (e) {
			console.error('Failed to rename:', e);
		}
	}

	function countNotesRecursive(nb: NotebookEntry): number {
		return nb.note_count + nb.children.reduce((sum, c) => sum + countNotesRecursive(c), 0);
	}

	function handleDelete(nb: NotebookEntry) {
		contextMenu = null;
		const total = countNotesRecursive(nb);
		if (total > 0) {
			deleteConfirm = nb;
		} else {
			confirmDelete(nb);
		}
	}

	async function confirmDelete(nb: NotebookEntry) {
		deleteConfirm = null;
		try {
			await deleteNotebook(nb.path);
			if ($activeNotebook?.path === nb.path) {
				selectAllNotes();
			}
			await refresh();
		} catch (e) {
			console.error('Failed to delete:', e);
		}
	}

	async function handleNoteDrop(e: DragEvent, nb: NotebookEntry) {
		e.preventDefault();
		dropTargetPath = null;
		const notePath = e.dataTransfer?.getData('text/plain');
		if (!notePath) return;
		// Don't move if already in this notebook
		const noteDir = notePath.substring(0, notePath.lastIndexOf('/'));
		if (noteDir === nb.path) return;
		try {
			const newPath = await moveNote(notePath, nb.path);
			$notes = $notes.filter(n => n.path !== notePath);
			if ($activeNotePath === notePath) {
				$activeNotePath = newPath;
				$activeNote = await readNote(newPath);
			}
			await refresh();
		} catch (e) {
			console.error('Failed to move note:', e);
		}
	}

	async function handleNotebookDrop(e: DragEvent, destPath: string) {
		e.preventDefault();
		dropTargetPath = null;
		const srcPath = e.dataTransfer?.getData('text/plain');
		if (!srcPath) return;
		draggedNotebookPath = null;
		// Don't move onto self or descendant
		if (destPath === srcPath || destPath.startsWith(srcPath + '/')) return;
		// Don't move if already in that parent
		const parentDir = srcPath.substring(0, srcPath.lastIndexOf('/'));
		if (parentDir === destPath) return;
		try {
			const oldName = srcPath.split('/').pop() || '';
			const newBasePath = destPath + '/' + oldName;
			await moveNotebook(srcPath, destPath);
			// Update collapsedNotebooks paths
			$collapsedNotebooks = $collapsedNotebooks.map(p => {
				if (p === srcPath) return newBasePath;
				if (p.startsWith(srcPath + '/')) return newBasePath + p.slice(srcPath.length);
				return p;
			});
			// Update active notebook/note if inside the moved notebook
			if ($activeNotebook?.path === srcPath || $activeNotebook?.path.startsWith(srcPath + '/')) {
				selectAllNotes();
			}
			if ($activeNotePath && $activeNotePath.startsWith(srcPath + '/')) {
				const newNotePath = newBasePath + $activeNotePath.slice(srcPath.length);
				$activeNotePath = newNotePath;
				$activeNote = await readNote(newNotePath);
			}
			$notebookIcons = await getNotebookIcons();
			await refresh();
		} catch (e) {
			console.error('Failed to move notebook:', e);
		}
	}

	function findSiblingsOfPath(tree: NotebookEntry[], parentPath: string, vaultRoot: string): NotebookEntry[] {
		if (parentPath === vaultRoot) return tree;
		for (const nb of tree) {
			if (nb.path === parentPath) return nb.children;
			if (nb.children.length > 0) {
				const found = findSiblingsOfPath(nb.children, parentPath, vaultRoot);
				if (found.length > 0) return found;
			}
		}
		return [];
	}

	async function handleNotebookReorder(srcPath: string, targetPath: string, position: 'above' | 'below') {
		dropTargetPath = null;
		dropPosition = null;
		draggedNotebookPath = null;
		if (srcPath === targetPath) return;
		if (targetPath.startsWith(srcPath + '/')) return; // can't move into descendant
		const vaultRoot = $appConfig?.active_vault;
		if (!vaultRoot) return;

		const srcParent = srcPath.substring(0, srcPath.lastIndexOf('/'));
		const targetParent = targetPath.substring(0, targetPath.lastIndexOf('/'));
		let actualSrcPath = srcPath;

		try {
			// Step 1: if different parents, move src into target's parent first
			if (srcParent !== targetParent) {
				const oldName = srcPath.split('/').pop() || '';
				const newPath = targetParent === vaultRoot
					? vaultRoot + '/' + oldName
					: targetParent + '/' + oldName;
				await moveNotebook(srcPath, targetParent);
				// Migrate paths in collapsedNotebooks
				$collapsedNotebooks = $collapsedNotebooks.map(p => {
					if (p === srcPath) return newPath;
					if (p.startsWith(srcPath + '/')) return newPath + p.slice(srcPath.length);
					return p;
				});
				// Migrate paths in notebookOrder
				const migrated: Record<string, number> = {};
				for (const [p, v] of Object.entries($notebookOrder)) {
					if (p === srcPath) migrated[newPath] = v;
					else if (p.startsWith(srcPath + '/')) migrated[newPath + p.slice(srcPath.length)] = v;
					else migrated[p] = v;
				}
				$notebookOrder = migrated;
				// Update active notebook/note
				if ($activeNotebook?.path === srcPath || $activeNotebook?.path.startsWith(srcPath + '/')) {
					selectAllNotes();
				}
				if ($activeNotePath && $activeNotePath.startsWith(srcPath + '/')) {
					const newNotePath = newPath + $activeNotePath.slice(srcPath.length);
					$activeNotePath = newNotePath;
					$activeNote = await readNote(newNotePath);
				}
				$notebookIcons = await getNotebookIcons();
				await refresh();
				actualSrcPath = newPath;
			}

			// Step 2: reorder within target's parent
			const siblings = findSiblingsOfPath($notebooks, targetParent, vaultRoot);
			const sortedSiblings = sortNotebooksTree(siblings);
			const without = sortedSiblings.filter(s => s.path !== actualSrcPath);
			const targetIdx = without.findIndex(s => s.path === targetPath);
			if (targetIdx === -1) return;
			const insertIdx = position === 'above' ? targetIdx : targetIdx + 1;
			const newSequence = [
				...without.slice(0, insertIdx).map(s => s.path),
				actualSrcPath,
				...without.slice(insertIdx).map(s => s.path),
			];

			// Assign sequential positions to all siblings (only within this parent)
			const newOrder = { ...$notebookOrder };
			newSequence.forEach((path, idx) => {
				newOrder[path] = idx;
			});
			$notebookOrder = newOrder;

			// Auto-enable manual mode (the act of reordering implies user wants this)
			if ($notebookSortMode !== 'manual') $notebookSortMode = 'manual';
		} catch (e) {
			console.error('Failed to reorder notebook:', e);
		}
	}

	function startRename(nb: NotebookEntry) {
		contextMenu = null;
		editingNotebook = nb.path;
		editValue = nb.name;
	}

	async function handleSetIcon(nb: NotebookEntry) {
		contextMenu = null;
		try {
			const selected = await openDialog({
				multiple: false,
				filters: [{ name: 'Images', extensions: ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico'] }]
			});
			if (!selected) return;
			const filePath = typeof selected === 'string' ? selected : selected;
			// Read the file and save as attachment
			const data = await readFile(filePath as string);
			const fileName = (filePath as string).split('/').pop() || 'icon.png';
			const iconRelative = await saveAttachment(`notebook-icon-${fileName}`, Array.from(data));
			await setNotebookIcon(nb.relative_path, iconRelative);
			$notebookIcons = await getNotebookIcons();
		} catch (e) {
			console.error('Failed to set notebook icon:', e);
		}
	}

	async function handleRemoveIcon(nb: NotebookEntry) {
		contextMenu = null;
		try {
			await setNotebookIcon(nb.relative_path, null);
			$notebookIcons = await getNotebookIcons();
		} catch (e) {
			console.error('Failed to remove notebook icon:', e);
		}
	}

	function getNotebookIconSrc(nb: NotebookEntry): string | null {
		const iconPath = $notebookIcons[nb.relative_path];
		if (!iconPath) return null;
		const vaultRoot = $appConfig?.active_vault;
		if (!vaultRoot) return null;
		return convertFileSrc(`${vaultRoot}/${iconPath}`);
	}

	function clampMenu(x: number, y: number, menuWidth = 220, menuHeight = 300): { x: number; y: number } {
		if (x + menuWidth > window.innerWidth) x = window.innerWidth - menuWidth - 8;
		if (y + menuHeight > window.innerHeight) y = window.innerHeight - menuHeight - 8;
		if (x < 4) x = 4;
		if (y < 4) y = 4;
		return { x, y };
	}

	function onContextMenu(e: MouseEvent, nb: NotebookEntry) {
		e.preventDefault();
		const { x, y } = clampMenu(e.clientX, e.clientY);
		contextMenu = { x, y, notebook: nb };
	}

	function closeContextMenu() {
		contextMenu = null;
	}

	function onTrashContextMenu(e: MouseEvent) {
		e.preventDefault();
		e.stopPropagation();
		const { x, y } = clampMenu(e.clientX, e.clientY, 180, 60);
		trashContextMenu = { x, y };
	}

	async function handleEmptyTrash() {
		trashContextMenu = null;
		try {
			await emptyTrash();
			// Clear selection and refresh the list (with cache invalidation)
			$activeNote = null;
			$activeNotePath = '';
			onViewChanged();
		} catch (e) {
			console.error('Failed to empty trash:', e);
		}
	}


	// Close context menu on click outside
	function handleWindowClick() {
		if (contextMenu) contextMenu = null;
		if (trashContextMenu) trashContextMenu = null;
	}
</script>

<svelte:window onclick={handleWindowClick} />

<aside class="sidebar" class:collapsed={$sidebarCollapsed} class:mobile={isMobile}>
	{#if !isMobile}
	<div class="sidebar-header">
		<button class="collapse-btn" onclick={() => ($sidebarCollapsed = !$sidebarCollapsed)} title="Toggle sidebar">
			<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
				{#if $sidebarCollapsed}
					<polyline points="9 6 15 12 9 18" />
				{:else}
					<polyline points="15 6 9 12 15 18" />
				{/if}
			</svg>
		</button>
		{#if !$sidebarCollapsed}
			<button class="icon-btn" onclick={() => ($showSearch = true)} title={`Search (${modKey}+F)`}>
				<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
					<circle cx="11" cy="11" r="8" />
					<line x1="21" y1="21" x2="16.65" y2="16.65" />
				</svg>
			</button>
		{/if}
	</div>
	{/if}

	{#if !$sidebarCollapsed}
		<nav class="sidebar-nav">
			<button
				class="nav-item"
				class:active={$viewMode === 'all'}
				onclick={selectAllNotes}
			>
				<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
					<path d="M16 3H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V8z" />
					<polyline points="14 3 14 8 21 8" />
				</svg>
				<span>All Notes</span>
			</button>

			<button
				class="nav-item"
				class:active={$viewMode === 'quickaccess'}
				onclick={selectQuickAccess}
			>
				<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
					<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
				</svg>
				<span>Quick Access</span>
			</button>

			<button
				class="nav-item"
				class:active={$viewMode === 'daily'}
				onclick={selectDaily}
			>
				<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
					<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
					<line x1="16" y1="2" x2="16" y2="6" />
					<line x1="8" y1="2" x2="8" y2="6" />
					<line x1="3" y1="10" x2="21" y2="10" />
				</svg>
				<span>Daily Notes</span>
			</button>

			<button
				class="nav-item"
				class:active={$viewMode === 'trash'}
				onclick={selectTrash}
				oncontextmenu={onTrashContextMenu}
			>
				<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
					<polyline points="3 6 5 6 21 6" /><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2" /><line x1="10" y1="11" x2="10" y2="17" /><line x1="14" y1="11" x2="14" y2="17" />
				</svg>
				<span>Trash</span>
			</button>
		</nav>

		<div class="section">
			<!-- svelte-ignore a11y_no_static_element_interactions -->
			<div
				class="section-header"
				class:drop-target={dropTargetPath === '__root__'}
				ondragover={(e) => {
					if (draggedNotebookPath) {
						e.preventDefault();
						e.dataTransfer!.dropEffect = 'move';
						dropTargetPath = '__root__';
					}
				}}
				ondragleave={() => { if (dropTargetPath === '__root__') dropTargetPath = null; }}
				ondrop={(e) => {
					if (draggedNotebookPath) {
						const vaultRoot = $appConfig?.active_vault;
						if (vaultRoot) handleNotebookDrop(e, vaultRoot);
					}
				}}
			>
				<span class="section-title">Notebooks</span>
				<button class="icon-btn-sm" onclick={() => {
					if (showNewNotebook) {
						showNewNotebook = false; newNotebookName = ''; newNotebookParent = null;
					} else {
						newNotebookParent = ($viewMode === 'notebook' && $activeNotebook) ? $activeNotebook : null;
						showNewNotebook = true;
					}
				}} title={$viewMode === 'notebook' && $activeNotebook ? `New notebook inside ${$activeNotebook.name}` : 'New notebook'}>
					<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
						<line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
					</svg>
				</button>
			</div>

			{#if showNewNotebook}
				<div class="new-notebook-input">
					{#if newNotebookParent}
						<span class="new-notebook-parent">{newNotebookParent.name}/</span>
					{/if}
					<input
						type="text"
						bind:value={newNotebookName}
						placeholder={newNotebookParent ? 'Sub-notebook name...' : 'Notebook name...'}
						onkeydown={(e) => {
							if (e.key === 'Enter') handleCreateNotebook();
							if (e.key === 'Escape') { showNewNotebook = false; newNotebookName = ''; newNotebookParent = null; }
						}}
					/>
				</div>
			{/if}

			<div class="notebook-list">
				{#if $rootNoteCount > 0}
					<button
						class="notebook-item"
						class:active={$viewMode === 'notebook' && $activeNotebook?.relative_path === ''}
						onclick={selectUnfiled}
					>
						<span style="width:13px;flex-shrink:0"></span>
						<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;opacity:0.6">
							<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" />
							<polyline points="14 2 14 8 20 8" />
						</svg>
						<span class="notebook-name">Unfiled Notes <span class="notebook-count">{$rootNoteCount}</span></span>
					</button>
				{/if}
				{#each sortedNotebooks as nb (nb.path)}
					{@render notebookItem(nb, 0)}
				{/each}
			</div>
		{#if isMobile && $tags.length > 0}
				<div class="tags-section-inline">
					<button class="section-header" onclick={() => tagsCollapsed = !tagsCollapsed}>
						<span class="section-title">Tags</span>
						<span class="tag-count" style="margin-left: auto">{$tags.length}</span>
					</button>
					{#if !tagsCollapsed}
						<div class="tag-list">
							{#each $tags as [tag, count]}
								<button
									class="tag-item"
									class:active={$viewMode === 'tag' && $activeTag === tag}
									onclick={() => selectTag(tag)}
								>
									<span class="tag-hash">#</span>
									<span class="tag-name">{tag}</span>
									<span class="tag-count">{count}</span>
								</button>
							{/each}
						</div>
					{/if}
				</div>
			{/if}
		</div>

		{#if !isMobile && $tags.length > 0}
			<div class="tags-section">
				<button class="section-header" onclick={() => tagsCollapsed = !tagsCollapsed}>
					<span class="section-title">Tags</span>
					<span class="tag-count" style="margin-left: auto">{$tags.length}</span>
				</button>
				{#if !tagsCollapsed}
					<div class="tag-list">
						{#each $tags as [tag, count]}
							<button
								class="tag-item"
								class:active={$viewMode === 'tag' && $activeTag === tag}
								onclick={() => selectTag(tag)}
							>
								<span class="tag-hash">#</span>
								<span class="tag-name">{tag}</span>
								<span class="tag-count">{count}</span>
							</button>
						{/each}
					</div>
				{/if}
			</div>
		{/if}

		<div class="sidebar-footer">
			<button class="icon-btn" onclick={() => ($showInfo = true)} title="Info">
				<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
					<circle cx="12" cy="12" r="10" />
					<line x1="12" y1="16" x2="12" y2="12" />
					<line x1="12" y1="8" x2="12.01" y2="8" />
				</svg>
			</button>
			<div class="sidebar-footer-right">
			<button class="icon-btn" onclick={() => ($showSettings = true)} title="Settings">
				<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
					<circle cx="12" cy="12" r="3" />
					<path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z" />
				</svg>
			</button>

			</div>
		</div>
	{/if}
</aside>

{#if contextMenu}
	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div class="context-menu" style="left: {contextMenu.x}px; top: {contextMenu.y}px" onmousedown={(e) => e.stopPropagation()}>
		<button onclick={() => startNewSubNotebook(contextMenu!.notebook)}>
			<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z" /><line x1="12" y1="11" x2="12" y2="17" /><line x1="9" y1="14" x2="15" y2="14" /></svg>
			New Sub-notebook
		</button>
		<button onclick={() => startRename(contextMenu!.notebook)}>
			<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" /><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" /></svg>
			Rename
		</button>
		<button onclick={() => handleSetIcon(contextMenu!.notebook)}>
			<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /><path d="M8 14s1.5 2 4 2 4-2 4-2" /><line x1="9" y1="9" x2="9.01" y2="9" /><line x1="15" y1="9" x2="15.01" y2="9" /></svg>
			Set Icon
		</button>
		{#if $notebookIcons[contextMenu.notebook.relative_path]}
			<button onclick={() => handleRemoveIcon(contextMenu!.notebook)}>
				<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /><line x1="15" y1="9" x2="9" y2="15" /><line x1="9" y1="9" x2="15" y2="15" /></svg>
				Remove Icon
			</button>
		{/if}
		<button class="danger" onclick={() => handleDelete(contextMenu!.notebook)}>
			<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6" /><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6" /><path d="M10 11v6" /><path d="M14 11v6" /><path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2" /></svg>
			Delete
		</button>
	</div>
{/if}

{#if trashContextMenu}
	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div class="context-menu" style="left: {trashContextMenu.x}px; top: {trashContextMenu.y}px" onmousedown={(e) => e.stopPropagation()}>
		<button class="danger" onclick={handleEmptyTrash}>
			<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
				<polyline points="3 6 5 6 21 6" /><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2" /><line x1="10" y1="11" x2="10" y2="17" /><line x1="14" y1="11" x2="14" y2="17" />
			</svg>
			Empty Trash
		</button>
	</div>
{/if}

{#if deleteConfirm}
	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div class="delete-confirm-overlay" onclick={() => deleteConfirm = null}>
		<!-- svelte-ignore a11y_no_static_element_interactions -->
		<div class="delete-confirm" onclick={(e) => e.stopPropagation()}>
			<h4>Delete "{deleteConfirm.name}"?</h4>
			<p>This notebook contains {countNotesRecursive(deleteConfirm)} note{countNotesRecursive(deleteConfirm) === 1 ? '' : 's'} that will be permanently deleted.</p>
			<div class="delete-confirm-actions">
				<button class="delete-confirm-cancel" onclick={() => deleteConfirm = null}>Cancel</button>
				<button class="delete-confirm-btn" onclick={() => confirmDelete(deleteConfirm!)}>Delete</button>
			</div>
		</div>
	</div>
{/if}

{#snippet notebookItem(nb: NotebookEntry, depth: number)}
	{@const hasChildren = nb.children.length > 0}
	{@const isCollapsed = $collapsedNotebooks.includes(nb.path)}
	{@const iconSrc = getNotebookIconSrc(nb)}
	{#if editingNotebook === nb.path}
		<div class="notebook-item" style="padding-left: {8 + depth * 16}px">
			<input
				type="text"
				class="rename-input"
				bind:value={editValue}
				onkeydown={(e) => {
					if (e.key === 'Enter') handleRename(nb);
					if (e.key === 'Escape') editingNotebook = null;
				}}
				onblur={() => handleRename(nb)}
			/>
		</div>
	{:else}
		<button
			class="notebook-item"
			class:active={$viewMode === 'notebook' && $activeNotebook?.path === nb.path}
			class:drop-target={dropTargetPath === nb.path && dropPosition === 'into'}
			class:drop-above={dropTargetPath === nb.path && dropPosition === 'above'}
			class:drop-below={dropTargetPath === nb.path && dropPosition === 'below'}
			style="padding-left: {8 + depth * 16}px"
			draggable="true"
			onclick={() => selectNotebook(nb)}
			oncontextmenu={(e) => onContextMenu(e, nb)}
			ondragstart={(e) => { draggedNotebookPath = nb.path; e.dataTransfer!.setData('text/plain', nb.path); e.dataTransfer!.effectAllowed = 'move'; }}
			ondragend={() => { draggedNotebookPath = null; dropPosition = null; }}
			ondragover={(e) => {
				e.preventDefault();
				if (draggedNotebookPath) {
					// Prevent drop on self or descendant
					if (nb.path === draggedNotebookPath || nb.path.startsWith(draggedNotebookPath + '/')) {
						e.dataTransfer!.dropEffect = 'none';
						return;
					}
				}
				// Decide drop position from cursor Y within the row: top 30% = above, bottom 30% = below, middle = into
				const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
				const relY = (e.clientY - rect.top) / rect.height;
				let pos: 'above' | 'into' | 'below';
				if (draggedNotebookPath && relY < 0.3) pos = 'above';
				else if (draggedNotebookPath && relY > 0.7) pos = 'below';
				else pos = 'into';
				// For 'into', reject if already in that parent (no-op)
				if (pos === 'into' && draggedNotebookPath) {
					const parentDir = draggedNotebookPath.substring(0, draggedNotebookPath.lastIndexOf('/'));
					if (parentDir === nb.path) { e.dataTransfer!.dropEffect = 'none'; return; }
				}
				e.dataTransfer!.dropEffect = 'move';
				dropTargetPath = nb.path;
				dropPosition = pos;
			}}
			ondragleave={() => { if (dropTargetPath === nb.path) { dropTargetPath = null; dropPosition = null; } }}
			ondrop={(e) => {
				e.preventDefault();
				const pos = dropPosition;
				if (draggedNotebookPath) {
					if (pos === 'above' || pos === 'below') {
						handleNotebookReorder(draggedNotebookPath, nb.path, pos);
					} else {
						handleNotebookDrop(e, nb.path);
					}
				} else {
					handleNoteDrop(e, nb);
				}
			}}
		>
			{#if hasChildren}
				<!-- svelte-ignore a11y_no_static_element_interactions -->
				<span class="chevron" class:collapsed={isCollapsed} onclick={(e) => toggleCollapse(nb.path, e)} onkeydown={(e) => { if (e.key === 'Enter') toggleCollapse(nb.path, e); }}>
					<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
						<polyline points="6 9 12 15 18 9" />
					</svg>
				</span>
			{:else}
				<span class="chevron-spacer"></span>
			{/if}
			{#if iconSrc}
				<img class="notebook-icon" src={iconSrc} alt="" />
			{:else}
				<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity="0.6">
					<path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z" />
				</svg>
			{/if}
			<span class="notebook-name">{nb.name} <span class="notebook-count">{nb.note_count}</span></span>
		</button>
	{/if}
	{#if hasChildren && !isCollapsed}
		{#each nb.children as child (child.path)}
			{@render notebookItem(child, depth + 1)}
		{/each}
	{/if}
{/snippet}

<style>
	.sidebar {
		display: flex;
		flex-direction: column;
		height: 100%;
		background: var(--bg-secondary);
		border-right: 1px solid var(--border-color);
		overflow: hidden;
		min-width: 44px;
		user-select: none;
	}

	.sidebar.collapsed {
		width: 44px !important;
		min-width: 44px;
		max-width: 44px;
	}

	.sidebar-header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 10px 8px;
		border-bottom: 1px solid var(--border-light);
		flex-shrink: 0;
	}

	.sidebar-nav {
		padding: 8px 6px;
		flex-shrink: 0;
	}

	.nav-item {
		display: flex;
		align-items: center;
		gap: 8px;
		width: 100%;
		padding: 6px 10px;
		border: none;
		background: none;
		border-radius: 6px;
		color: var(--text-secondary);
		font-size: inherit;
		cursor: pointer;
		transition: all 0.1s;
	}

	.nav-item:hover {
		background: var(--bg-hover);
		color: var(--text-primary);
	}

	.nav-item.active {
		background: var(--accent-light);
		color: var(--text-accent);
	}

	.section {
		flex: 1;
		overflow-y: auto;
		padding: 4px 0;
	}

	.tags-section {
		flex-shrink: 0;
		border-top: 1px solid var(--border-light);
		padding: 4px 0;
		max-height: 30%;
		overflow-y: auto;
	}

	.section-header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 8px 12px 4px;
		width: 100%;
		background: none;
		border: none;
		cursor: pointer;
		color: inherit;
		font: inherit;
		text-align: left;
	}

	.section-title {
		font-size: 11px;
		font-weight: 600;
		text-transform: uppercase;
		letter-spacing: 0.05em;
		color: var(--text-tertiary);
	}

	.icon-btn, .icon-btn-sm {
		background: none;
		border: none;
		color: var(--text-tertiary);
		cursor: pointer;
		padding: 4px;
		border-radius: 4px;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.icon-btn:hover, .icon-btn-sm:hover {
		background: var(--bg-hover);
		color: var(--text-primary);
	}

	.collapse-btn {
		background: none;
		border: none;
		color: var(--text-tertiary);
		cursor: pointer;
		padding: 4px;
		border-radius: 4px;
		display: flex;
		align-items: center;
	}

	.collapse-btn:hover {
		background: var(--bg-hover);
		color: var(--text-primary);
	}

	.new-notebook-input {
		padding: 2px 8px;
		display: flex;
		align-items: center;
		gap: 0;
		border: 1px solid var(--accent);
		border-radius: 4px;
		background: var(--bg-primary);
		overflow: hidden;
	}

	.new-notebook-parent {
		padding: 4px 0 4px 8px;
		color: var(--text-tertiary);
		font-size: inherit;
		white-space: nowrap;
		flex-shrink: 0;
	}

	.new-notebook-input input {
		flex: 1;
		min-width: 0;
		padding: 4px 8px;
		border: none;
		background: transparent;
		color: var(--text-primary);
		font-size: inherit;
		outline: none;
	}

	.rename-input {
		width: 100%;
		padding: 4px 8px;
		border: 1px solid var(--accent);
		border-radius: 4px;
		background: var(--bg-primary);
		color: var(--text-primary);
		font-size: inherit;
		outline: none;
	}

	.notebook-list {
		padding: 0 2px;
	}

	.notebook-item {
		display: flex;
		align-items: center;
		gap: 6px;
		width: 100%;
		padding: 5px 8px;
		border: none;
		background: none;
		border-radius: 6px;
		color: var(--text-secondary);
		font-size: inherit;
		cursor: pointer;
		transition: all 0.1s;
	}

	.notebook-item:hover {
		background: var(--bg-hover);
	}

	.notebook-item.active {
		background: var(--accent-light);
		color: var(--text-accent);
	}

	.notebook-item.drop-target,
	.section-header.drop-target {
		background: color-mix(in srgb, var(--accent) 20%, transparent);
		outline: 2px dashed var(--accent);
		outline-offset: -2px;
		border-radius: 6px;
	}
	.notebook-item.drop-above {
		box-shadow: 0 -2px 0 0 var(--accent);
	}
	.notebook-item.drop-below {
		box-shadow: 0 2px 0 0 var(--accent);
	}

	.chevron {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 14px;
		height: 14px;
		flex-shrink: 0;
		color: var(--text-tertiary);
		transition: transform 0.15s ease;
		cursor: pointer;
		border-radius: 3px;
	}

	.chevron:hover {
		color: var(--text-primary);
	}

	.chevron.collapsed {
		transform: rotate(-90deg);
	}

	.chevron-spacer {
		width: 14px;
		flex-shrink: 0;
	}

	.notebook-icon {
		width: 20px;
		height: 20px;
		border-radius: 3px;
		object-fit: cover;
		flex-shrink: 0;
	}

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

	.notebook-count {
		font-size: 11px;
		color: var(--text-tertiary);
		margin-left: 2px;
	}

	.tag-list {
		padding: 0 4px;
	}

	.tag-item {
		display: flex;
		align-items: center;
		gap: 4px;
		width: 100%;
		padding: 4px 12px;
		border: none;
		background: none;
		border-radius: 6px;
		color: var(--text-secondary);
		font-size: inherit;
		cursor: pointer;
		transition: all 0.1s;
	}

	.tag-item:hover {
		background: var(--bg-hover);
	}

	.tag-item.active {
		background: var(--accent-light);
		color: var(--text-accent);
	}

	.tag-hash {
		color: var(--text-tertiary);
		font-weight: 600;
	}

	.tag-name {
		flex: 1;
		text-align: left;
	}

	.tag-count {
		font-size: 11px;
		color: var(--text-tertiary);
	}

	.sidebar-footer {
		padding: 8px;
		border-top: 1px solid var(--border-light);
		display: flex;
		justify-content: space-between;
		align-items: center;
		flex-shrink: 0;
	}

	.sidebar-footer-right {
		display: flex;
		gap: 4px;
	}

	.context-menu {
		position: fixed;
		background: var(--bg-primary);
		border: 1px solid var(--border-color);
		border-radius: 8px;
		box-shadow: var(--shadow-md);
		padding: 4px;
		z-index: 1000;
		min-width: 140px;
	}

	.context-menu button {
		display: flex;
		align-items: center;
		gap: 8px;
		width: 100%;
		padding: 6px 12px;
		border: none;
		background: none;
		color: var(--text-primary);
		font-size: 13px;
		cursor: pointer;
		border-radius: 4px;
		text-align: left;
	}

	.context-menu button:hover {
		background: var(--bg-hover);
	}

	.context-menu button.danger {
		color: var(--danger);
	}

	.context-menu button.danger:hover {
		background: color-mix(in srgb, var(--danger) 10%, transparent);
	}

	.delete-confirm-overlay {
		position: fixed;
		inset: 0;
		background: rgba(0, 0, 0, 0.4);
		display: flex;
		align-items: center;
		justify-content: center;
		z-index: 1000;
	}

	.delete-confirm {
		background: var(--bg-primary);
		border: 1px solid var(--border-color);
		border-radius: 10px;
		padding: 20px 24px;
		max-width: 340px;
		box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
	}

	.delete-confirm h4 {
		margin: 0 0 8px;
		font-size: 14px;
		color: var(--text-primary);
	}

	.delete-confirm p {
		margin: 0 0 16px;
		font-size: 12.5px;
		color: var(--text-secondary);
		line-height: 1.4;
	}

	.delete-confirm-actions {
		display: flex;
		gap: 8px;
		justify-content: flex-end;
	}

	.delete-confirm-cancel {
		padding: 6px 14px;
		border: 1px solid var(--border-color);
		border-radius: 6px;
		background: var(--bg-secondary);
		color: var(--text-primary);
		font-size: 12px;
		cursor: pointer;
	}

	.delete-confirm-cancel:hover {
		background: var(--bg-hover);
	}

	.delete-confirm-btn {
		padding: 6px 14px;
		border: none;
		border-radius: 6px;
		background: var(--danger, #e53e3e);
		color: white;
		font-size: 12px;
		font-weight: 600;
		cursor: pointer;
	}

	.delete-confirm-btn:hover {
		opacity: 0.9;
	}

	/* ═══ MOBILE (class-based for Android high-DPI) ═══ */
	.sidebar.mobile {
		width: 100% !important;
		min-width: 100%;
		border-right: none;
	}

	.sidebar.mobile.collapsed {
		width: 100% !important;
		min-width: 100%;
		max-width: 100%;
	}

	.sidebar.mobile .sidebar-header {
		padding: 10px 12px;
	}

	.sidebar.mobile .collapse-btn {
		display: none;
	}

	.sidebar.mobile .nav-item {
		padding: 12px 14px;
		font-size: 15px;
		min-height: 48px;
		gap: 12px;
	}

	.sidebar.mobile .nav-item svg {
		width: 20px;
		height: 20px;
	}

	.sidebar.mobile .section {
		padding-bottom: 180px;
	}

	.sidebar.mobile .tags-section-inline {
		border-top: 1px solid var(--border-light);
		padding: 4px 0;
		margin-top: 8px;
	}

	.sidebar.mobile .section-header {
		padding: 12px 16px 6px;
		min-height: 44px;
	}

	.sidebar.mobile .section-title {
		font-size: 12px;
	}

	.sidebar.mobile .icon-btn,
	.sidebar.mobile .icon-btn-sm {
		min-width: 44px;
		min-height: 44px;
		padding: 10px;
	}

	.sidebar.mobile .notebook-item {
		padding: 12px 12px;
		min-height: 48px;
		gap: 10px;
		font-size: 15px;
	}

	.sidebar.mobile .notebook-icon {
		width: 24px;
		height: 24px;
	}

	.sidebar.mobile .new-notebook-input {
		padding: 4px 12px;
	}

	.sidebar.mobile .new-notebook-parent {
		padding: 10px 0 10px 12px;
		font-size: 15px;
	}

	.sidebar.mobile .new-notebook-input input {
		padding: 10px 12px;
		font-size: 15px;
	}

	.sidebar.mobile .rename-input {
		padding: 10px 12px;
		font-size: 15px;
	}

	.sidebar.mobile .tag-item {
		padding: 10px 16px;
		min-height: 44px;
		font-size: 14px;
	}

	.sidebar.mobile .sidebar-footer {
		padding: 8px 12px;
		display: none;
	}

	.sidebar.mobile .context-menu {
		min-width: 200px;
		border-radius: 12px;
		padding: 6px;
	}

	.sidebar.mobile .context-menu button {
		padding: 12px 16px;
		font-size: 15px;
		min-height: 44px;
		border-radius: 8px;
	}

	.sidebar.mobile .delete-confirm {
		max-width: calc(100vw - 40px);
		padding: 24px;
	}

	.sidebar.mobile .delete-confirm h4 {
		font-size: 16px;
	}

	.sidebar.mobile .delete-confirm p {
		font-size: 14px;
	}

	.sidebar.mobile .delete-confirm-cancel,
	.sidebar.mobile .delete-confirm-btn {
		padding: 10px 20px;
		font-size: 14px;
		min-height: 44px;
	}
</style>
