<script lang="ts">
  import { messages, streaming, connectionState, currentSession, sendMessage, cancelStream, reconnect, error, chatErrors, dismissError } from '$lib/stores/chat'
  import { activityLog } from '$lib/stores/activity'
  import SessionRail from '$lib/components/SessionRail.svelte'
  import TaskContextPanel from '$lib/components/TaskContextPanel.svelte'
  import InlineApproval from '$lib/components/InlineApproval.svelte'
  import AgentIndicator from '$lib/components/AgentIndicator.svelte'
  import { Button } from '$lib/components/ui/button'
  import { Textarea } from '$lib/components/ui/textarea'
  import ArrowUpIcon from '@lucide/svelte/icons/arrow-up'
  import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw'
  import SquareIcon from '@lucide/svelte/icons/square'
  import { marked } from 'marked'
  import DOMPurify from 'dompurify'

  let { showRail = true }: { showRail?: boolean } = $props()

  let input = $state('')
  let messagesEnd = $state<HTMLDivElement | null>(null)
  let scrolledUp = $state(false)
  let container = $state<HTMLDivElement | null>(null)

  function isNearBottom(): boolean {
    if (!container) return true
    const { scrollTop, scrollHeight, clientHeight } = container
    return scrollHeight - scrollTop - clientHeight < 80
  }

  function onScroll() {
    scrolledUp = !isNearBottom()
  }

  // Auto-scroll to bottom on new messages — unless user scrolled up to read.
  $effect(() => {
    void $messages
    if ($streaming || !scrolledUp) {
      setTimeout(() => messagesEnd?.scrollIntoView({ behavior: 'smooth' }), 50)
    }
  })

  // Reset scroll lock when user sends a message.
  function submitFollows() {
    scrolledUp = false
  }

  // Resizable right rail (session graph). Persisted so it survives reloads.
  const RAIL_MIN = 260
  const RAIL_MAX = 620
  function loadRailWidth(): number {
    if (typeof localStorage === 'undefined') return 320
    const v = Number(localStorage.getItem('oikos-rail-width'))
    return v >= RAIL_MIN && v <= RAIL_MAX ? v : 320
  }
  let railWidth = $state(loadRailWidth())
  let resizing = $state(false)

  function startResize(e: PointerEvent) {
    e.preventDefault()
    resizing = true
    const startX = e.clientX
    const startW = railWidth
    function move(ev: PointerEvent) {
      railWidth = Math.min(RAIL_MAX, Math.max(RAIL_MIN, startW + (startX - ev.clientX)))
    }
    function up() {
      resizing = false
      localStorage.setItem('oikos-rail-width', String(railWidth))
      window.removeEventListener('pointermove', move)
      window.removeEventListener('pointerup', up)
    }
    window.addEventListener('pointermove', move)
    window.addEventListener('pointerup', up)
  }

  function render(text: string): string {
    return DOMPurify.sanitize(marked.parse(text, { async: false }) as string)
  }

  function submit() {
    const text = input.trim()
    if (!text || $streaming) return
    input = ''
    scrolledUp = false
    sendMessage(text)
  }

  function handleKeydown(e: KeyboardEvent) {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault()
      submit()
    }
  }

  const suggestions = [
    'What needs my attention right now?',
    'Summarize fleet health',
    'Any pending approvals or open signals?',
    'What changed in the last hour?'
  ]

  function ask(q: string) {
    if ($streaming) return
    sendMessage(q)
  }
</script>

<div class="flex h-full min-h-0">
  {#if showRail}
    <div class="hidden md:block">
      <SessionRail />
    </div>
  {/if}
  <div class="flex min-w-0 flex-1 flex-col">
    <div class="min-h-0 flex-1 overflow-y-auto" bind:this={container} onscroll={onScroll}>
      <div class="mx-auto flex max-w-3xl flex-col gap-5 p-4">
        {#if $messages.length === 0}
          <div class="flex flex-col items-center gap-6 pt-24 text-center">
            <div>
              <h2 class="text-xl font-semibold">Nomos</h2>
              <p class="mt-1 text-sm text-muted-foreground">Your resident operator. Ask about the fleet, or tell it to act.</p>
            </div>
            <div class="grid w-full max-w-md grid-cols-1 gap-2 sm:grid-cols-2">
              {#each suggestions as q}
                <Button variant="outline" size="sm" class="h-auto justify-start whitespace-normal py-2 text-left text-xs" onclick={() => ask(q)}>
                  {q}
                </Button>
              {/each}
            </div>
          </div>
        {/if}

        {#each $messages as msg, i (msg.id)}
          <div class="flex flex-col gap-1.5 {msg.role === 'user' ? 'items-end' : 'items-start'}">
            {#if msg.role === 'user'}
              <div class="max-w-[85%] rounded-2xl rounded-br-sm bg-primary px-4 py-2.5 text-sm text-primary-foreground whitespace-pre-wrap user-msg">{msg.text}</div>
            {:else}
              <div class="flex w-full flex-col gap-2">
                {#if msg.text}
                  <div class="prose-chat max-w-none text-sm leading-relaxed assistant-msg">
                    <!-- eslint-disable-next-line svelte/no-at-html-tags — sanitized via DOMPurify -->
                    {@html render(msg.text)}
                  </div>
                {/if}
                {#if msg.pendingApprovals.length > 0}
                  <InlineApproval approvals={msg.pendingApprovals} />
                {/if}
              </div>
            {/if}
          </div>
        {/each}
        <AgentIndicator
          active={$streaming || $activityLog.some((e) => e.status === 'running')}
          lastActivity={$activityLog.find((e) => e.status === 'running') ?? null}
          error={$error}
        />
        <div bind:this={messagesEnd}></div>
      </div>
    </div>

    {#if $connectionState === 'disconnected'}
      <div class="mx-auto w-full max-w-3xl px-4">
        <div class="mb-2 flex items-center gap-2 rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-xs">
          <RefreshCwIcon class="size-3 shrink-0" aria-hidden="true" />
          <span class="text-warning-foreground flex-1">Agent connection lost. The task may still be running.</span>
          <Button size="xs" variant="outline" class="h-6 text-[11px]" onclick={reconnect}>Reconnect</Button>
        </div>
      </div>
    {:else if $connectionState === 'reconnecting'}
      <div class="mx-auto w-full max-w-3xl px-4">
        <div class="mb-2 flex items-center gap-2 rounded-md border bg-muted/50 px-3 py-2 text-xs">
          <RefreshCwIcon class="size-3 shrink-0 animate-spin text-muted-foreground" aria-hidden="true" />
          <span class="text-muted-foreground flex-1">Reconnecting to agent…</span>
        </div>
      </div>
    {/if}

    {#if $error}
      <div class="mx-auto w-full max-w-3xl px-4">
        <div class="mb-2 rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-xs text-destructive">
          {$error}
        </div>
      </div>
    {/if}

    {#each $chatErrors as err (err.id)}
      <div class="mx-auto w-full max-w-3xl px-4">
        <div class="mb-2 flex items-center gap-2 rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive">
          <span class="flex-1">{err.message}</span>
          {#if err.action}
            <Button size="xs" variant="ghost" class="h-6 text-[11px]" onclick={() => dismissError(err.id)}>{err.action}</Button>
          {/if}
          <button class="ml-1 text-muted-foreground hover:text-foreground" onclick={() => dismissError(err.id)} aria-label="Dismiss">×</button>
        </div>
      </div>
    {/each}

    <div class="border-t bg-card/50 p-3 input-ornament relative">
      <form
        class="mx-auto flex max-w-3xl items-end gap-2"
        onsubmit={(e) => {
          e.preventDefault()
          submit()
        }}
      >
        <Textarea
          bind:value={input}
          onkeydown={handleKeydown}
          placeholder="Ask Nomos anything…"
          rows={2}
          class="max-h-40 min-h-0 resize-none"
          disabled={$streaming}
        />
        {#if $streaming}
          <Button type="button" size="icon" variant="destructive" onclick={cancelStream} aria-label="Stop">
            <SquareIcon />
          </Button>
        {:else}
          <Button type="submit" size="icon" disabled={!input.trim()} aria-label="Send">
            <ArrowUpIcon />
          </Button>
        {/if}
      </form>
    </div>
  </div>

  {#if showRail}
    <div class="hidden shrink-0 xl:flex" style="width: {railWidth}px">
      <button
        type="button"
        class="group/rz relative w-1.5 shrink-0 cursor-col-resize touch-none"
        onpointerdown={startResize}
        aria-label="Resize task panel"
      >
        <span
          class="absolute inset-y-0 left-1/2 w-px -translate-x-1/2 transition-colors {resizing
            ? 'bg-primary/60'
            : 'bg-border group-hover/rz:bg-primary/50'}"
        ></span>
      </button>
      <div class="flex min-w-0 flex-1 flex-col">
        <TaskContextPanel />
      </div>
    </div>
  {/if}
</div>

<style>
  /* ── Art Nouveau chat styling ── */

  /* Assistant message wrapper — ornamental frame */
  .assistant-msg {
    position: relative;
    border-left: 2px solid var(--primary);
    border-image: linear-gradient(to bottom, var(--primary), transparent 80%) 1;
    padding-left: 1rem;
  }

  /* User message — softened bubble with inner glow */
  .user-msg {
    box-shadow: 0 0 12px -4px var(--primary);
    border: 1px solid var(--primary);
  }

  /* Prose overrides */
  .prose-chat :global(p) {
    margin: 0 0 0.5rem;
  }
  .prose-chat :global(p:last-child) {
    margin-bottom: 0;
  }

  .prose-chat :global(ul),
  .prose-chat :global(ol) {
    margin: 0 0 0.5rem;
    padding-left: 1.25rem;
  }
  .prose-chat :global(ul) {
    list-style-type: disc;
  }
  .prose-chat :global(ol) {
    list-style-type: decimal;
  }
  .prose-chat :global(li) {
    margin-bottom: 0.125rem;
    padding-left: 0.25rem;
  }
  .prose-chat :global(li::marker) {
    color: var(--primary);
  }

  .prose-chat :global(code) {
    background: var(--muted);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.15em 0.4em;
    font-family: var(--font-mono);
    font-size: 0.85em;
    color: var(--primary);
  }

  .prose-chat :global(pre) {
    background: var(--muted);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.75rem 0.875rem;
    overflow-x: auto;
    margin: 0 0 0.5rem;
    position: relative;
  }
  .prose-chat :global(pre)::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 1px;
    background: linear-gradient(to right, transparent, var(--primary), transparent);
    opacity: 0.4;
  }
  .prose-chat :global(pre code) {
    background: none;
    padding: 0;
    font-size: 0.8125rem;
    color: inherit;
    border: none;
  }

  .prose-chat :global(h1),
  .prose-chat :global(h2),
  .prose-chat :global(h3) {
    font-weight: 600;
    margin: 0.75rem 0 0.375rem;
    font-size: 1em;
    position: relative;
    display: inline-block;
  }
  .prose-chat :global(h1)::after,
  .prose-chat :global(h2)::after,
  .prose-chat :global(h3)::after {
    content: '';
    display: block;
    height: 1px;
    margin-top: 2px;
    background: linear-gradient(to right, var(--primary), transparent);
    opacity: 0.5;
  }

  .prose-chat :global(table) {
    border-collapse: collapse;
    margin: 0 0 0.5rem;
    font-size: 0.8125rem;
  }
  .prose-chat :global(th) {
    background: var(--muted);
    font-weight: 600;
  }
  .prose-chat :global(th),
  .prose-chat :global(td) {
    border: 1px solid var(--border);
    padding: 0.3rem 0.6rem;
    text-align: left;
  }

  .prose-chat :global(blockquote) {
    border-left: 3px solid var(--primary);
    padding-left: 0.75rem;
    color: var(--muted-foreground);
    margin: 0 0 0.5rem;
    font-style: italic;
    position: relative;
  }
  .prose-chat :global(blockquote)::before {
    content: '“';
    position: absolute;
    left: -0.15rem;
    top: -0.35rem;
    font-size: 1.5rem;
    color: var(--primary);
    opacity: 0.6;
    font-style: normal;
    line-height: 1;
  }

  .prose-chat :global(hr) {
    border: none;
    height: 1px;
    margin: 0.75rem 0;
    background: linear-gradient(to right, transparent, var(--border) 20%, var(--border) 80%, transparent);
  }

  .prose-chat :global(strong) {
    color: var(--primary);
  }

  .prose-chat :global(a) {
    color: var(--primary);
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
  }

  /* Input area ornament */
  .input-ornament::before {
    content: '';
    position: absolute;
    top: 0;
    left: 2rem;
    right: 2rem;
    height: 1px;
    background: linear-gradient(to right, transparent, var(--primary), transparent);
    opacity: 0.3;
  }
</style>
