<script module lang="ts">
	interface TreeDir {
		type: "dir";
		name: string;
		children: TreeNode[];
	}

	interface TreeFile {
		type: "file";
		name: string;
		path: string;
	}

	type TreeNode = TreeDir | TreeFile;

	// first-seen order (the diff's file order), no re-sorting
	const buildTree = (paths: string[]): TreeNode[] => {
		const roots: TreeNode[] = [];
		const dirs: Record<string, TreeDir> = Object.create(null);
		for (const path of paths) {
			const parts = path.split("/");
			let siblings = roots;
			let prefix = "";
			for (const dir of parts.slice(0, -1)) {
				prefix = prefix ? `${prefix}/${dir}` : dir;
				let node = dirs[prefix];
				if (!node) {
					node = { type: "dir", name: dir, children: [] };
					dirs[prefix] = node;
					siblings.push(node);
				}
				siblings = node.children;
			}
			siblings.push({ type: "file", name: parts.at(-1) ?? path, path });
		}
		return roots;
	};
</script>

<script lang="ts">
	import File from "$icon/file";
	import Folder from "$icon/folder";
	import FolderOpen from "$icon/folder-open";

	interface Props {
		// full slash-separated paths. anchors point at `#file-<path>`
		files: { name: string }[];
	}

	let { files }: Props = $props();

	const tree = $derived(buildTree(files.map((file) => file.name)));
</script>

{#snippet nodes(list: TreeNode[])}
	{#each list as node (`${node.type}:${node.name}`)}
		{#if node.type === "dir"}
			<details open class="group">
				<summary class="cursor-pointer list-none">
					<span class="inline-flex items-center gap-2">
						<Folder class="size-4 shrink-0 group-open:hidden" />
						<FolderOpen class="hidden size-4 shrink-0 group-open:block" />
						<span class="truncate text-foreground-default">{node.name}</span>
					</span>
				</summary>
				<div class="ml-1 border-l border-border-default pl-2">
					{@render nodes(node.children)}
				</div>
			</details>
		{:else}
			<div class="flex items-center gap-2">
				<File class="size-4 shrink-0" />
				<a
					href="#file-{node.path}"
					class="truncate text-foreground-default no-underline hover:underline"
				>
					{node.name}
				</a>
			</div>
		{/if}
	{/each}
{/snippet}

{@render nodes(tree)}
