<script lang="ts">
	import type { FolderNode } from "./types";
	import { createEventDispatcher } from "svelte";
	import Arrow from "./ArrowIcon.svelte";
	import FolderIcon from "../icons/yellow-folder.svg";

	export let name: string;
	export let path: string[];
	export let is_root = false;
	export let level = 0;
	export let selected_path: string[] | null;
	export let interactive: boolean;
	export let ls_fn: (path: string[]) => Promise<FolderNode[]>;
	
	let children: FolderNode[] = [];
	let is_open = is_root;
	let has_loaded_children = false;
	let is_loading = false; 

	const dispatch = createEventDispatcher<{
		select: { path: string[] };
	}>();

	const current_path = is_root ? [] : [...path, name];

	async function load_children() {
		if (has_loaded_children) return;
		is_loading = true;
		children = await ls_fn(current_path);
		has_loaded_children = true;
		is_loading = false;
	}
	
	async function toggle_folder() {
		if (is_open) {
			is_open = false;
			return;
		}
		await load_children();
		if (children.length > 0 || is_root) {
			is_open = true;
		}
	}

	const paths_equal = (p1: string[] | null, p2: string[] | null): boolean => {
		if (p1 === null || p2 === null) return p1 === p2;
		return p1.join("/") === p2.join("/");
	};

	// Load children on initial render if the root is open.
	if (is_open) {
		load_children();
	}

	// Automatically expand parent directories when a child path is selected.
	$: if (!is_root && selected_path && selected_path.length > current_path.length && selected_path.join("/").startsWith(current_path.join("/"))) {
		if (!is_open) {
			is_open = true;
			load_children();
		}
	}
</script>

<div class="node-container" style="--level: {level}">
	<button
		class="folder-item"
		class:selected={paths_equal(current_path, selected_path)}
		disabled={!interactive}
		on:click={() => {
			dispatch('select', { path: current_path });
			toggle_folder();
		}}
	>
		<button
			type="button"
			class="icon"
			class:open={is_open}
			class:loading={is_loading} 
			class:disabled={has_loaded_children && children.length === 0 && !is_root}
			aria-label={is_open ? "Collapse folder" : "Expand folder"}
			disabled={has_loaded_children && children.length === 0 && !is_root}
			on:click|stopPropagation={toggle_folder}
		>
			<Arrow />
		</button>

		<img src={FolderIcon} alt="folder icon" class="folder-icon" />
		<span class="folder-name">{name}</span>
	</button>
	
	{#if is_open && children.length > 0}
		<div class="children-container">
			{#each children as child_node}
				<!-- Recursively render child folders -->
				<svelte:self
					name={child_node.name}
					path={current_path} 
					level={level + 1}
					{selected_path}
					{interactive}
					{ls_fn}
					on:select
				/>
			{/each}
		</div>
	{/if}
</div>

<style>
    .node-container {
		padding-left: calc(var(--level) * 20px);
	}
	.folder-item {
		display: flex;
		gap: 8px;
		align-items: center;
		width: 100%;
		background: none;
		border: 1px solid transparent;
		padding: 4px;
		border-radius: var(--radius-md);
		cursor: pointer;
		text-align: left;
		font-family: var(--font-mono);
		font-size: var(--scale-00);
	}
	.folder-item:hover { background: var(--background-fill-secondary); }
	.folder-item.selected {
		background: var(--color-accent-soft);
		border-color: var(--color-accent);
	}
	.folder-icon { width: 20px; height: 20px; }
	.folder-name { overflow-wrap: anywhere; word-break: break-word; }

	.icon {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: 20px;
		height: 20px;
		padding: 2px;
		border-radius: var(--radius-sm);
		color: var(--body-text-color-subdued);
		transition: transform 0.2s, color 0.2s, background-color 0.2s;
		cursor: pointer;
		position: relative;
	}
	.icon.disabled {
		opacity: 0.3;
		cursor: default;
	}
	.icon.disabled:hover {
		background-color: transparent;
	}
	.folder-item:hover .icon {
		background-color: var(--background-fill-secondary);
	}
	.icon:hover {
		background-color: var(--background-fill-secondary-hover);
	}

	/* Controls the rotation of the arrow icon */
	.icon :global(> *) {
		transform: rotate(0deg);
		transition: transform 0.2s;
	}
	.icon.open :global(> *) {
		transform: rotate(90deg);
		color: var(--color-accent);
	}

	/* Loading spinner styles */
	.icon.loading :global(> *) {
		opacity: 0;
	}
	.icon.loading::after {
		content: "";
		position: absolute;
		width: 12px;
		height: 12px;
		border: 2px solid var(--body-text-color-subdued);
		border-top-color: var(--color-accent);
		border-radius: 50%;
		animation: spin 0.8s linear infinite;
	}
	@keyframes spin {
		to {
			transform: rotate(360deg);
		}
	}
</style>