<script lang="ts">
  import { sessions, loadSessions, loadSessionMessages, deleteSession, newChat } from '$lib/stores/chat'
  import { liveEvents, subscribeEvents } from '$lib/stores/events'
  import type { Session } from '$lib/api'
  import { relativeTime } from '$lib/utils'
  import { Badge } from '$lib/components/ui/badge'
  import { Button } from '$lib/components/ui/button'
  import * as Card from '$lib/components/ui/card'
  import PlusIcon from '@lucide/svelte/icons/plus'
  import Trash2Icon from '@lucide/svelte/icons/trash-2'
  import { onMount } from 'svelte'

  // Live board: refetch when a task's lifecycle changes anywhere (the agent set
  // a goal, advanced status, raised/answered a question, finished). We subscribe
  // to the event store directly rather than via $effect so delivery is
  // deterministic. We must scan ALL events newer than the last we saw, not just
  // liveEvents[0]: entity.touched fires on every tool call, so a task.status
  // event is usually buried below several touches by the time we're notified. A
  // short debounce coalesces one task's goal.set + plan.proposed + task.status
  // burst into a single refetch.
  const TASK_EVENTS = new Set(['task.status', 'goal.set', 'question.raised', 'question.answered'])

  onMount(() => {
    loadSessions()
    const unsubStream = subscribeEvents() // keep the global stream open while the board is up
    let lastSeenId = 0
    let refreshTimer: ReturnType<typeof setTimeout> | null = null
    const unsub = liveEvents.subscribe((evs) => {
      if (evs.length === 0) return
      const maxId = evs[0].id // newest-first
      if (maxId <= lastSeenId) return
      const relevant = evs.some((e) => e.id > lastSeenId && TASK_EVENTS.has(e.type))
      lastSeenId = maxId
      if (relevant) {
        if (refreshTimer) clearTimeout(refreshTimer)
        refreshTimer = setTimeout(() => loadSessions(), 400)
      }
    })
    return () => {
      unsub()
      unsubStream()
    }
  })

  // ── Status → display ────────────────────────────────────────────────
  type Bucket = 'running' | 'input' | 'done' | 'failed'
  function bucket(s: Session): Bucket {
    switch (s.status) {
      case 'awaiting_input':
        return 'input'
      case 'done':
        return s.outcome === 'failure' ? 'failed' : 'done'
      case 'failed':
        return 'failed'
      default:
        return 'running' // active | planning | executing | undefined
    }
  }

  interface StatusStyle {
    label: string
    dot: string
    pulse: boolean
    variant: 'default' | 'secondary' | 'destructive' | 'outline'
  }
  function statusStyle(s: Session): StatusStyle {
    switch (bucket(s)) {
      case 'input':
        return { label: 'Needs input', dot: 'bg-warning', pulse: true, variant: 'secondary' }
      case 'done':
        return { label: s.outcome === 'partial' ? 'Done · partial' : 'Done', dot: 'bg-success', pulse: false, variant: 'default' }
      case 'failed':
        return { label: 'Failed', dot: 'bg-destructive', pulse: false, variant: 'destructive' }
      default:
        return { label: 'Running', dot: 'bg-primary', pulse: true, variant: 'secondary' }
    }
  }

  const FILTERS: { id: 'all' | Bucket; label: string }[] = [
    { id: 'all', label: 'All' },
    { id: 'running', label: 'Running' },
    { id: 'input', label: 'Needs input' },
    { id: 'done', label: 'Done' },
    { id: 'failed', label: 'Failed' }
  ]
  let filter = $state<'all' | Bucket>('all')

  const counts = $derived.by(() => {
    const c: Record<string, number> = { all: $sessions.length, running: 0, input: 0, done: 0, failed: 0 }
    for (const s of $sessions) c[bucket(s)]++
    return c
  })

  const visible = $derived(
    filter === 'all' ? $sessions : $sessions.filter((s) => bucket(s) === filter)
  )

  function heading(s: Session): string {
    return s.goal || s.title || 'Untitled task'
  }

  function openTask(id: string) {
    loadSessionMessages(id)
    location.hash = '#/chat'
  }

  function startTask() {
    newChat()
    location.hash = '#/chat'
  }

  let confirmDelete = $state<string | null>(null)
  function handleDelete(e: MouseEvent, id: string) {
    e.stopPropagation()
    if (confirmDelete === id) {
      deleteSession(id)
      confirmDelete = null
    } else {
      confirmDelete = id
      setTimeout(() => { if (confirmDelete === id) confirmDelete = null }, 3000)
    }
  }
</script>

<div class="mx-auto flex h-full min-h-0 max-w-6xl flex-col p-4 sm:p-6">
  <div class="mb-4 flex items-center justify-between gap-3">
    <div>
      <h2 class="text-lg font-semibold">Tasks</h2>
      <p class="text-sm text-muted-foreground">Every task is a goal Nomos works to completion.</p>
    </div>
    <Button onclick={startTask} class="gap-1.5">
      <PlusIcon class="size-4" />
      New task
    </Button>
  </div>

  <!-- Filter chips -->
  <div class="mb-4 flex flex-wrap gap-1.5">
    {#each FILTERS as f}
      <button
        type="button"
        onclick={() => (filter = f.id)}
        class="rounded-full border px-2.5 py-1 text-xs transition-colors {filter === f.id
          ? 'border-primary bg-primary/10 text-foreground'
          : 'border-border text-muted-foreground hover:bg-muted/50'}"
      >
        {f.label}
        <span class="ml-1 opacity-60">{counts[f.id] ?? 0}</span>
      </button>
    {/each}
  </div>

  <div class="min-h-0 flex-1 overflow-y-auto">
    {#if visible.length === 0}
      <div class="flex flex-col items-center gap-4 pt-20 text-center">
        <p class="max-w-sm text-sm text-muted-foreground">
          {filter === 'all'
            ? 'No tasks yet. Start one and Nomos will plan it, execute it, and report the outcome.'
            : `No ${FILTERS.find((f) => f.id === filter)?.label.toLowerCase()} tasks.`}
        </p>
        {#if filter === 'all'}
          <Button onclick={startTask} variant="outline" class="gap-1.5">
            <PlusIcon class="size-4" /> Start your first task
          </Button>
        {/if}
      </div>
    {:else}
      <div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
        {#each visible as s (s.id)}
          {@const st = statusStyle(s)}
          <div class="group relative">
            <button type="button" class="block w-full text-left" onclick={() => openTask(s.id)}>
              <Card.Root class="h-full transition-colors hover:border-primary/50">
                <Card.Header class="pb-2">
                  <div class="flex items-center justify-between gap-2">
                    <span class="flex items-center gap-1.5 text-xs font-medium text-muted-foreground">
                      <span class="size-2 rounded-full {st.dot} {st.pulse ? 'animate-pulse' : ''}"></span>
                      {st.label}
                    </span>
                    <span class="text-[11px] text-muted-foreground">{relativeTime(s.last_active_at)}</span>
                  </div>
                  <Card.Title class="line-clamp-2 text-sm leading-snug">{heading(s)}</Card.Title>
                </Card.Header>
                <Card.Content class="pt-0">
                  {#if s.summary}
                    <p class="line-clamp-3 text-xs text-muted-foreground">{s.summary}</p>
                  {:else if s.goal && s.title && s.goal !== s.title}
                    <p class="line-clamp-2 text-xs text-muted-foreground">{s.title}</p>
                  {:else}
                    <p class="text-xs italic text-muted-foreground/60">No summary yet.</p>
                  {/if}
                </Card.Content>
              </Card.Root>
            </button>
            <button
              type="button"
              class="absolute right-2 top-2 flex size-7 items-center justify-center rounded-md text-muted-foreground opacity-0 transition-opacity hover:bg-destructive/10 hover:text-destructive group-hover:opacity-100"
              onclick={(e) => handleDelete(e, s.id)}
              title={confirmDelete === s.id ? 'Click again to confirm' : 'Delete task'}
              aria-label="Delete task"
            >
              {#if confirmDelete === s.id}
                <span class="text-[10px] font-bold text-destructive">Del?</span>
              {:else}
                <Trash2Icon class="size-4" />
              {/if}
            </button>
          </div>
        {/each}
      </div>
    {/if}
  </div>
</div>
