<script lang="ts">
	import { base } from '$app/paths';
	import { api, ApiError, type Group } from '$lib/api';
	import { slugify, SLUG_MAX } from '$lib/slugify';
	import { canCreateApp } from '$lib/capabilities';
	import { currentUser } from '$lib/auth';

	const me = $derived($currentUser);
	// Group creation mirrors app creation's gate — owner/admin only.
	const canCreate = $derived(canCreateApp(me));

	let groups = $state<Group[] | null>(null);
	let listError = $state<string | null>(null);
	let loading = $state(true);

	// Tree assembly: index children by parent_id so we can render the
	// flat list as a nested tree. Roots have parent_id === null.
	interface TreeNode {
		group: Group;
		children: TreeNode[];
	}

	const tree = $derived.by<TreeNode[]>(() => {
		if (!groups) return [];
		const byParent = new Map<string | null, Group[]>();
		for (const g of groups) {
			const key = g.parent_id;
			const bucket = byParent.get(key) ?? [];
			bucket.push(g);
			byParent.set(key, bucket);
		}
		const build = (parentId: string | null): TreeNode[] =>
			(byParent.get(parentId) ?? [])
				.slice()
				.sort((a, b) => a.name.localeCompare(b.name))
				.map((group) => ({ group, children: build(group.id) }));
		return build(null);
	});

	// Collapse state, keyed by group id. Default: expanded.
	let collapsed = $state<Record<string, boolean>>({});
	function toggle(id: string) {
		collapsed = { ...collapsed, [id]: !collapsed[id] };
	}

	// Create form
	let showCreate = $state(false);
	let createSlug = $state('');
	let createName = $state('');
	let createDescription = $state('');
	let createParent = $state('');
	let slugTouched = $state(false);
	let creating = $state(false);
	let createError = $state<string | null>(null);

	function onNameInput(event: Event) {
		const value = (event.target as HTMLInputElement).value;
		createName = value;
		if (!slugTouched) {
			createSlug = slugify(value);
		}
	}

	function onSlugInput(event: Event) {
		const raw = (event.target as HTMLInputElement).value;
		const normalized = slugify(raw);
		createSlug = normalized;
		if (raw !== normalized) {
			(event.target as HTMLInputElement).value = normalized;
		}
		slugTouched = normalized.length > 0;
	}

	async function load() {
		loading = true;
		listError = null;
		try {
			groups = await api.groups.list();
		} catch (e) {
			listError = e instanceof Error ? e.message : String(e);
			groups = null;
		} finally {
			loading = false;
		}
	}

	function resetCreate() {
		createSlug = '';
		createName = '';
		createDescription = '';
		createParent = '';
		createError = null;
		slugTouched = false;
	}

	async function submitCreate(event: Event) {
		event.preventDefault();
		creating = true;
		createError = null;
		try {
			await api.groups.create({
				slug: createSlug.trim(),
				name: createName.trim(),
				description: createDescription.trim() || null,
				parent: createParent.trim() || undefined
			});
			showCreate = false;
			resetCreate();
			await load();
		} catch (e) {
			createError =
				e instanceof ApiError ? e.message : e instanceof Error ? e.message : String(e);
		} finally {
			creating = false;
		}
	}

	$effect(() => {
		void load();
	});
</script>

<section>
	<header class="page-header">
		<h1>Groups</h1>
		{#if canCreate}
			<button
				type="button"
				onclick={() => {
					showCreate = !showCreate;
					if (!showCreate) resetCreate();
				}}
			>
				{showCreate ? 'Cancel' : 'New group'}
			</button>
		{/if}
	</header>

	{#if showCreate && canCreate}
		<form class="create-form" onsubmit={submitCreate}>
			<div class="row">
				<label>
					<span>Name</span>
					<input value={createName} oninput={onNameInput} required placeholder="My Group" />
				</label>
				<label>
					<span>Slug</span>
					<input
						value={createSlug}
						oninput={onSlugInput}
						required
						pattern="[a-z0-9][a-z0-9-]*"
						maxlength={SLUG_MAX}
						placeholder="my-group"
						autocomplete="off"
						autocapitalize="off"
						autocorrect="off"
						spellcheck="false"
					/>
				</label>
			</div>
			<label>
				<span>Parent group (optional)</span>
				<select bind:value={createParent}>
					<option value="">— root —</option>
					{#each groups ?? [] as g (g.id)}
						<option value={g.slug}>{g.name} (/{g.slug})</option>
					{/each}
				</select>
			</label>
			<label>
				<span>Description</span>
				<input bind:value={createDescription} placeholder="optional" />
			</label>
			{#if createError}
				<div class="error">{createError}</div>
			{/if}
			<div class="actions">
				<button type="submit" disabled={creating}>
					{creating ? 'Creating…' : 'Create group'}
				</button>
			</div>
		</form>
	{/if}

	{#if loading}
		<p class="muted">Loading…</p>
	{:else if listError}
		<div class="error">
			<strong>Could not load groups.</strong>
			<p>{listError}</p>
			<button type="button" onclick={() => void load()}>Retry</button>
		</div>
	{:else if groups && groups.length === 0}
		<p class="muted">No groups yet. Create one above to get started.</p>
	{:else if groups}
		<ul class="tree">
			{#each tree as node (node.group.id)}
				{@render branch(node, 0)}
			{/each}
		</ul>
	{/if}
</section>

{#snippet branch(node: TreeNode, depth: number)}
	<li>
		<div class="node" style="padding-left: {depth * 1.25}rem">
			{#if node.children.length > 0}
				<button
					type="button"
					class="twisty"
					aria-label={collapsed[node.group.id] ? 'Expand' : 'Collapse'}
					onclick={() => toggle(node.group.id)}
				>
					{collapsed[node.group.id] ? '▸' : '▾'}
				</button>
			{:else}
				<span class="twisty-spacer"></span>
			{/if}
			<a href="{base}/groups/{node.group.slug}">
				<strong>{node.group.name}</strong>
				<span class="muted">/{node.group.slug}</span>
			</a>
		</div>
		{#if node.children.length > 0 && !collapsed[node.group.id]}
			<ul>
				{#each node.children as child (child.group.id)}
					{@render branch(child, depth + 1)}
				{/each}
			</ul>
		{/if}
	</li>
{/snippet}

<style>
	.page-header {
		display: flex;
		justify-content: space-between;
		align-items: center;
		margin-bottom: 1.5rem;
	}

	h1 {
		margin: 0;
		font-size: 1.5rem;
	}

	button {
		background: #38bdf8;
		color: #0b1220;
		border: none;
		padding: 0.5rem 1rem;
		border-radius: 0.375rem;
		font-weight: 600;
		cursor: pointer;
	}

	button:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}

	.muted {
		color: #64748b;
	}

	.error {
		border: 1px solid #b91c1c;
		background: #450a0a;
		color: #fecaca;
		padding: 1rem;
		border-radius: 0.5rem;
		margin: 1rem 0;
	}

	.create-form {
		background: #1e293b;
		border-radius: 0.5rem;
		padding: 1.25rem;
		margin-bottom: 1.5rem;
		display: flex;
		flex-direction: column;
		gap: 0.75rem;
	}

	.create-form .row {
		display: grid;
		grid-template-columns: 1fr 2fr;
		gap: 0.75rem;
	}

	.create-form label {
		display: flex;
		flex-direction: column;
		gap: 0.25rem;
		font-size: 0.85rem;
		color: #cbd5e1;
	}

	.create-form input {
		background: #0b1220;
		color: #e2e8f0;
		border: 1px solid #334155;
		border-radius: 0.375rem;
		padding: 0.5rem 0.75rem;
		font: inherit;
	}

	.actions {
		display: flex;
		justify-content: flex-end;
		gap: 0.5rem;
	}

	.tree {
		list-style: none;
		padding: 0;
		margin: 0;
	}

	.tree ul {
		list-style: none;
		padding: 0;
		margin: 0;
	}

	.node {
		display: flex;
		align-items: center;
		gap: 0.4rem;
		margin-bottom: 0.25rem;
	}

	.node a {
		flex: 1;
		display: flex;
		gap: 0.5rem;
		align-items: baseline;
		padding: 0.55rem 0.75rem;
		background: #1e293b;
		border-radius: 0.375rem;
		text-decoration: none;
		color: inherit;
	}

	.node a:hover {
		background: #283549;
	}

	.twisty {
		background: transparent;
		color: #94a3b8;
		border: none;
		padding: 0;
		width: 1.25rem;
		font-size: 0.85rem;
		font-weight: 400;
		cursor: pointer;
		flex: none;
	}

	.twisty-spacer {
		width: 1.25rem;
		flex: none;
	}
</style>
