<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import { page } from '$app/stores';
	import { goto } from '$app/navigation';
	import { flip } from 'svelte/animate';
	import { dndzone } from 'svelte-dnd-action';
	import { currentUser } from '$lib/stores/auth';
	import { collectiveMembers, currentCollective } from '$lib/stores/collective';
	import {
		loadTasks,
		addTask,
		renameTask,
		completeTask,
		deleteTask,
		reorderTasks,
		renameTaskList
	} from '$lib/stores/tasks';
	import { getSupabase } from '$lib/supabase';
	import { generateId } from '$lib/utils/id';
	import type { Task, TaskList } from '@colectivo/types';
	import { ArrowLeft, GripVertical, Plus, Trash2, Check } from 'lucide-svelte';
	import * as m from '$lib/paraglide/messages';

	const listId = $page.params.id!;

	let list = $state<TaskList | null>(null);
	let tasks = $state<Task[]>([]);
	let loading = $state(true);

	// Inline rename of the task list (same pattern as /lists/[id], /notes/[id]).
	let listName = $state('');
	let nameSaveTimer: ReturnType<typeof setTimeout> | null = null;
	const NAME_SAVE_DEBOUNCE_MS = 500;

	let newTitle = $state('');
	let titleInput: HTMLInputElement | undefined = $state();

	let editingId = $state<string | null>(null);
	let editTitle = $state('');

	const pendingTasks = $derived(tasks.filter((t) => !t.is_completed));
	const completedTasks = $derived(tasks.filter((t) => t.is_completed));

	let pollingId: ReturnType<typeof setInterval> | null = null;

	onMount(async () => {
		const [listRes, tasksRes] = await Promise.all([
			getSupabase().from('task_lists').select('*').eq('id', listId).single(),
			loadTasks(listId)
		]);

		if (listRes.error || !listRes.data) {
			goto('/tasks');
			return;
		}

		list = listRes.data as TaskList;
		listName = list.name ?? '';
		tasks = tasksRes;
		loading = false;

		// Lazily load collective members so we can attribute completed tasks to a name.
		if ($currentCollective && $collectiveMembers.length === 0) {
			const { data: members } = await getSupabase()
				.from('collective_members')
				.select('user_id, role, users:users(display_name, avatar_type, avatar_emoji, avatar_url)')
				.eq('collective_id', $currentCollective.id);
			if (members) {
				type Row = {
					user_id: string;
					role: 'admin' | 'member' | 'guest';
					users: {
						display_name: string;
						avatar_type: 'initials' | 'emoji' | 'upload';
						avatar_emoji: string | null;
						avatar_url: string | null;
					} | null;
				};
				const rows = members as unknown as Row[];
				collectiveMembers.set(
					rows
						.filter((r) => r.users)
						.map((r) => ({
							user_id: r.user_id,
							collective_id: $currentCollective!.id,
							role: r.role,
							display_name: r.users!.display_name,
							avatar_type: r.users!.avatar_type,
							avatar_emoji: r.users!.avatar_emoji,
							avatar_url: r.users!.avatar_url
						}))
				);
			}
		}

		// 5s polling per analysis §6
		pollingId = setInterval(async () => {
			tasks = await loadTasks(listId);
		}, 5_000);
	});

	onDestroy(async () => {
		if (pollingId) clearInterval(pollingId);
		if (nameSaveTimer) {
			clearTimeout(nameSaveTimer);
			await flushNameSave();
		}
	});

	// ── Inline rename ──────────────────────────────────────────────────────────

	function scheduleNameSave() {
		if (nameSaveTimer) clearTimeout(nameSaveTimer);
		nameSaveTimer = setTimeout(() => {
			nameSaveTimer = null;
			void flushNameSave();
		}, NAME_SAVE_DEBOUNCE_MS);
	}

	async function flushNameSave() {
		if (!list) return;
		const next = listName.trim();
		if (next === (list.name ?? '')) return;
		list = { ...list, name: next };
		await renameTaskList(list.id, next);
	}

	function nameFor(userId: string | null): string {
		if (!userId) return '';
		const member = $collectiveMembers.find((mm) => mm.user_id === userId);
		return member?.display_name ?? '';
	}

	async function handleAdd() {
		const title = newTitle.trim();
		if (!title || !$currentUser) return;
		const sortOrder = pendingTasks.length;
		const tempId = generateId();
		const optimistic: Task = {
			id: tempId,
			list_id: listId,
			title,
			is_completed: false,
			completed_by: null,
			completed_at: null,
			sort_order: sortOrder,
			created_by: $currentUser.id,
			created_at: new Date().toISOString()
		};
		tasks = [...tasks, optimistic];
		newTitle = '';

		const real = await addTask(listId, title, $currentUser.id, sortOrder);
		if (real) {
			tasks = tasks.map((t) => (t.id === tempId ? real : t));
		} else {
			tasks = tasks.filter((t) => t.id !== tempId);
		}
		titleInput?.focus();
	}

	function handleKeydown(e: KeyboardEvent) {
		if (e.key === 'Enter') handleAdd();
	}

	async function handleToggle(task: Task) {
		if (!$currentUser) return;
		const nowCompleted = !task.is_completed;
		tasks = tasks.map((t) =>
			t.id === task.id
				? {
						...t,
						is_completed: nowCompleted,
						completed_by: nowCompleted ? $currentUser!.id : null,
						completed_at: nowCompleted ? new Date().toISOString() : null
				  }
				: t
		);
		await completeTask(task.id, $currentUser.id, nowCompleted);
	}

	async function handleDelete(id: string) {
		tasks = tasks.filter((t) => t.id !== id);
		await deleteTask(id);
	}

	function startEdit(task: Task) {
		editingId = task.id;
		editTitle = task.title;
	}

	async function commitEdit() {
		if (!editingId) return;
		const id = editingId;
		const title = editTitle.trim();
		editingId = null;
		if (!title) return;
		tasks = tasks.map((t) => (t.id === id ? { ...t, title } : t));
		await renameTask(id, title);
	}

	function handleEditKeydown(e: KeyboardEvent) {
		if (e.key === 'Enter') commitEdit();
		if (e.key === 'Escape') {
			editingId = null;
		}
	}

	function handleDnd(e: CustomEvent<{ items: Task[] }>) {
		// Reorder is only valid among pending tasks (completed sink to the bottom).
		const reordered = e.detail.items;
		tasks = [...reordered, ...completedTasks];
	}

	async function handleDndFinalize(e: CustomEvent<{ items: Task[] }>) {
		const reordered = e.detail.items;
		tasks = [...reordered, ...completedTasks];
		await reorderTasks(reordered);
	}
</script>

<svelte:head><title>{list?.name ?? m.tasks_title()} — Colectivo</title></svelte:head>

<div class="flex h-full flex-col overflow-hidden">
	<header class="sticky top-0 z-30 flex h-14 items-center gap-2 border-b border-black/5 bg-surface/80 px-4 backdrop-blur-xl md:px-6 dark:border-white/5">
		<a href="/tasks" class="rounded p-1 text-text-secondary hover:bg-black/5 dark:hover:bg-white/5" aria-label={m.notes_back_to_board()}>
			<ArrowLeft size={18} strokeWidth={1.5} />
		</a>
		<h1 class="truncate text-[15px] font-medium text-text-secondary md:hidden">
			{list?.name || m.tasks_new_list_placeholder()}
		</h1>
	</header>

	<div class="flex-1 overflow-y-auto px-4 py-4 md:px-6">
		<!-- Inline rename title input (autosave on idle) -->
		<div class="pt-1 pb-4 md:pt-2 md:pb-6">
			<input
				bind:value={listName}
				oninput={scheduleNameSave}
				onblur={flushNameSave}
				placeholder={m.tasks_new_list_placeholder()}
				class="w-full bg-transparent text-[26px] md:text-[32px] font-semibold tracking-[-0.02em] text-text-primary placeholder:text-text-secondary focus:outline-none"
				aria-label={m.tasks_new_list_placeholder()}
			/>
		</div>

		{#if loading}
			<p class="text-sm text-text-secondary">{m.loading()}</p>
		{:else if tasks.length === 0}
			<div class="rounded-lg border border-dashed border-black/10 p-12 text-center dark:border-white/10">
				<p class="text-sm font-medium text-text-primary">{m.tasks_empty()}</p>
				<p class="mt-1 text-sm text-text-secondary">{m.tasks_empty_hint()}</p>
			</div>
			{@render addTaskForm()}
		{:else}
			<!-- Pending (drag-and-drop reorderable) -->
			{#if pendingTasks.length > 0}
				<section
					use:dndzone={{ items: pendingTasks, flipDurationMs: 200, dragDisabled: editingId !== null }}
					onconsider={handleDnd}
					onfinalize={handleDndFinalize}
					class="space-y-1"
				>
					{#each pendingTasks as task (task.id)}
						<div
							role="listitem"
							animate:flip={{ duration: 200 }}
							class="group flex items-center gap-2 rounded-md bg-surface-raised px-3 py-2"
						>
							<button
								type="button"
								class="cursor-grab text-text-secondary opacity-0 group-hover:opacity-100"
								aria-label="Drag handle"
								tabindex="-1"
							>
								<GripVertical size={14} strokeWidth={1.5} />
							</button>
							<button
								type="button"
								onclick={() => handleToggle(task)}
								aria-label={m.tasks_toggle()}
								class="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded border border-black/20 hover:border-black/40 dark:border-white/20"
							></button>
							{#if editingId === task.id}
								<input
									bind:value={editTitle}
									onkeydown={handleEditKeydown}
									onblur={commitEdit}
									class="flex-1 bg-transparent text-sm text-text-primary focus:outline-none"
								/>
							{:else}
								<button
									type="button"
									ondblclick={() => startEdit(task)}
									class="flex-1 truncate text-left text-sm text-text-primary"
								>
									{task.title}
								</button>
							{/if}
							<button
								type="button"
								onclick={() => handleDelete(task.id)}
								aria-label={m.tasks_delete()}
								class="rounded p-1 text-text-secondary opacity-0 hover:bg-black/5 group-hover:opacity-100 dark:hover:bg-white/5"
							>
								<Trash2 size={14} strokeWidth={1.5} />
							</button>
						</div>
					{/each}
				</section>
			{/if}

			<!-- Inline add-task form between pending and completed (option `a`). -->
			{@render addTaskForm()}

			<!-- Completed (sunk to bottom; not reorderable) -->
			{#if completedTasks.length > 0}
				<section class="mt-6">
					<h2 class="mb-2 text-xs font-semibold uppercase tracking-wide text-text-secondary">
						{m.tasks_completed_section()}
					</h2>
					<div class="space-y-1">
						{#each completedTasks as task (task.id)}
							<div
								role="listitem"
								animate:flip={{ duration: 200 }}
								class="group flex items-center gap-2 rounded-md bg-surface-raised px-3 py-2"
							>
								<button
									type="button"
									onclick={() => handleToggle(task)}
									aria-label={m.tasks_uncheck()}
									title={task.completed_by
										? m.tasks_completed_by({ name: nameFor(task.completed_by) })
										: undefined}
									class="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded border border-emerald-500 bg-emerald-500 text-white"
								>
									<Check size={12} strokeWidth={2.5} />
								</button>
								<span class="flex-1 truncate text-sm text-text-secondary line-through">
									{task.title}
								</span>
								<span class="text-xs text-text-secondary">
									{m.tasks_completed_by({ name: nameFor(task.completed_by) })}
								</span>
								<button
									type="button"
									onclick={() => handleDelete(task.id)}
									aria-label={m.tasks_delete()}
									class="rounded p-1 text-text-secondary opacity-0 hover:bg-black/5 group-hover:opacity-100 dark:hover:bg-white/5"
								>
									<Trash2 size={14} strokeWidth={1.5} />
								</button>
							</div>
						{/each}
					</div>
				</section>
			{/if}
		{/if}
	</div>

	</div>

	<!-- Inline add-task form: input row on top, no suggestions for tasks. -->
	{#snippet addTaskForm()}
		<div class="mt-3 pb-6" data-testid="add-task-form">
			<div class="flex items-center gap-2">
				<div class="flex flex-1 items-center gap-2 rounded-lg bg-surface px-3 py-2 shadow-[0px_2px_8px_rgba(15,23,42,0.06)]">
					<input
						bind:this={titleInput}
						bind:value={newTitle}
						onkeydown={handleKeydown}
						placeholder={m.tasks_new_task_placeholder()}
						class="min-w-0 flex-1 bg-transparent text-sm text-text-primary placeholder:text-text-secondary focus:outline-none"
					/>
				</div>
				<button
					type="button"
					onclick={handleAdd}
					disabled={!newTitle.trim()}
					aria-label={m.add()}
					class="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-slate-900 text-white hover:opacity-90 disabled:opacity-40 dark:bg-slate-100 dark:text-slate-900 transition-opacity"
				>
					<Plus size={18} strokeWidth={2} />
				</button>
			</div>
		</div>
	{/snippet}
