<template>
  <div class="flex flex-col h-full min-h-0">

    <!-- Conversation history -->
    <div
      ref="scrollEl"
      class="flex-1 overflow-y-auto space-y-6 pb-4 pr-1"
      aria-live="polite"
      aria-label="Conversation history"
    >
      <!-- Empty state -->
      <div v-if="!turns.length" class="flex flex-col items-center justify-center py-16 text-center px-4">
        <div class="text-4xl mb-3" aria-hidden="true">🪵</div>
        <p class="text-text-primary text-base font-medium mb-1">Describe your issue</p>
        <p class="text-text-dim text-sm max-w-md">
          Write what you're seeing — however you'd say it. Turnstone will search
          your logs and explain what it finds. Mention a service name to focus
          the search ("meshtasticd keeps disconnecting after 4.10 update").
        </p>
      </div>

      <!-- Turn history -->
      <template v-for="(turn, idx) in turns" :key="idx">
        <!-- User bubble -->
        <div class="flex justify-end">
          <div class="max-w-[80%] rounded-2xl rounded-tr-sm bg-accent text-white px-4 py-2.5 text-sm whitespace-pre-wrap leading-relaxed">
            {{ turn.query }}
          </div>
        </div>

        <!-- Assistant response -->
        <div class="flex flex-col gap-3">
          <!-- Source chips -->
          <div v-if="turn.sources?.length" class="flex flex-wrap gap-1.5 items-center">
            <span class="text-xs text-text-dim">Searched:</span>
            <span
              v-for="s in turn.sources"
              :key="s"
              class="font-mono text-xs bg-surface-raised border border-surface-border rounded px-2 py-0.5 text-text-muted"
            >{{ s }}</span>
          </div>

          <!-- Loading spinner -->
          <div v-if="turn.loading" class="flex items-center gap-2 text-xs text-text-dim py-2">
            <span class="inline-block w-3 h-3 rounded-full border-2 border-accent border-t-transparent animate-spin motion-reduce:animate-none" aria-hidden="true" />
            <span>{{ turn.status ?? 'Searching logs…' }}</span>
          </div>

          <!-- LLM reasoning -->
          <div
            v-if="turn.reasoning"
            class="rounded-r border-l-4 border-accent bg-surface-raised px-4 py-3"
          >
            <div class="flex items-center gap-2 mb-2 text-xs text-accent font-semibold uppercase tracking-wide">
              <span aria-hidden="true">⚡</span>
              <span>Diagnosis</span>
            </div>
            <p class="text-sm text-text-primary leading-relaxed whitespace-pre-wrap">{{ turn.reasoning }}</p>
          </div>

          <!-- Summary bar -->
          <div v-if="turn.summary" class="flex flex-wrap gap-x-5 gap-y-1 text-xs text-text-dim px-1">
            <span class="font-medium text-text-muted">{{ turn.summary.total }} entr{{ turn.summary.total !== 1 ? 'ies' : 'y' }}</span>
            <span v-if="turn.summary.window_start">
              {{ fmtTs(turn.summary.window_start) }} → {{ fmtTs(turn.summary.window_end) }}
            </span>
            <button
              v-if="turn.entries?.length && !turn.showEntries"
              @click="turn.showEntries = true"
              class="text-accent hover:underline"
            >show {{ turn.entries.length }} log lines</button>
            <button
              v-if="turn.showEntries"
              @click="turn.showEntries = false"
              class="text-text-dim hover:text-text-primary"
            >hide entries</button>
          </div>

          <!-- Log entries (collapsible) -->
          <div
            v-if="turn.showEntries && turn.entries?.length"
            class="rounded border border-surface-border overflow-hidden"
          >
            <LogEntryRow
              v-for="entry in turn.entries"
              :key="entry.entry_id"
              :entry="entry"
            />
          </div>

          <!-- No results -->
          <p
            v-if="!turn.loading && turn.summary?.total === 0"
            class="text-sm text-text-dim px-1"
          >
            No log evidence found for that query. Check Sources to confirm data is
            gleaned, or try different wording.
          </p>

          <!-- Save as incident -->
          <div v-if="!turn.loading && (turn.entries?.length ?? 0) > 0 && !turn.saved" class="flex gap-3 mt-1">
            <button
              @click="saveIncident(turn)"
              :disabled="turn.saving"
              class="px-3 py-1.5 bg-surface-raised border border-surface-border rounded text-xs text-text-muted hover:text-text-primary hover:border-accent transition-colors disabled:opacity-40"
            >
              {{ turn.saving ? 'Saving…' : 'Save as incident' }}
            </button>
          </div>
          <p v-if="turn.saved" class="text-xs text-green-400 px-1">
            Saved —
            <RouterLink to="/incidents" class="underline underline-offset-2 hover:text-green-300">view in Incidents</RouterLink>
          </p>
        </div>
      </template>
    </div>

    <!-- Source suggestion pre-flight -->
    <div
      v-if="suggestedSources.length && !activeTurn"
      class="mb-3 p-3 rounded border border-surface-border bg-surface-raised"
    >
      <p class="text-xs text-text-dim mb-2">Detected sources — deselect to exclude:</p>
      <div class="flex flex-wrap gap-2">
        <button
          v-for="s in suggestedSources"
          :key="s.source_id"
          @click="toggleSource(s.source_id)"
          :aria-pressed="!excludedSources.has(s.source_id)"
          :class="[
            'font-mono text-xs rounded px-2 py-1 border transition-colors',
            excludedSources.has(s.source_id)
              ? 'bg-surface border-surface-border text-text-dim line-through'
              : 'bg-accent/10 border-accent/40 text-accent'
          ]"
        >{{ s.source_id }}</button>
      </div>
    </div>

    <!-- Input row -->
    <div class="border-t border-surface-border pt-3">
      <div class="flex gap-2 items-end">
        <div class="flex-1">
          <label :for="inputId" class="sr-only">Describe your issue</label>
          <textarea
            :id="inputId"
            ref="textareaEl"
            v-model="draft"
            :disabled="!!activeTurn"
            :placeholder="turns.length
              ? 'Follow up, or ask about something else…'
              : 'Paste or type your issue — as much detail as you want…'"
            rows="3"
            class="w-full bg-surface-raised border border-surface-border rounded-xl px-4 py-2.5 text-sm text-text-primary placeholder-text-dim focus:outline-none focus:border-accent transition-colors resize-none leading-relaxed disabled:opacity-50"
            @input="onInput"
            @keydown.enter.exact.prevent="submit"
            @keydown.enter.shift.exact.stop
          />
          <p class="text-right text-xs text-text-dim mt-1">Enter to search · Shift+Enter for new line</p>
        </div>
        <button
          :disabled="!draft.trim() || !!activeTurn"
          @click="submit"
          class="shrink-0 px-4 py-2.5 rounded-xl bg-accent text-white text-sm font-semibold hover:bg-blue-400 transition-colors disabled:opacity-40 self-end mb-6"
          aria-label="Search logs"
        >
          <span v-if="activeTurn">…</span>
          <span v-else>Search</span>
        </button>
      </div>
    </div>

  </div>
</template>

<script setup lang="ts">
import { ref, nextTick, onMounted } from 'vue'
import { RouterLink } from 'vue-router'
import LogEntryRow from '@/components/LogEntryRow.vue'
import type { LogEntry } from '@/stores/search'

const BASE    = import.meta.env.BASE_URL.replace(/\/$/, '')
const inputId = `chat-input-${Math.random().toString(36).slice(2, 7)}`

interface Summary {
  total: number
  window_start: string | null
  window_end: string | null
  time_detected: boolean
  by_severity: Record<string, number>
  by_source: Record<string, number>
}

interface SuggestedSource {
  source_id: string
  score: number
  matched_tokens: string[]
}

interface Turn {
  query:       string
  loading:     boolean
  status:      string | null
  reasoning:   string | null
  summary:     Summary | null
  entries:     LogEntry[]
  sources:     string[]
  showEntries: boolean
  saved:       boolean
  saving:      boolean
  since:       string | null
  until:       string | null
}

const turns            = ref<Turn[]>([])
const draft            = ref('')
const suggestedSources = ref<SuggestedSource[]>([])
const excludedSources  = ref(new Set<string>())
const activeTurn       = ref<Turn | null>(null)
const scrollEl         = ref<HTMLElement | null>(null)
const textareaEl       = ref<HTMLTextAreaElement | null>(null)

let suggestTimer: ReturnType<typeof setTimeout> | null = null

onMounted(() => textareaEl.value?.focus())

function onInput() {
  // Auto-grow textarea
  const el = textareaEl.value
  if (el) {
    el.style.height = 'auto'
    el.style.height = `${Math.min(el.scrollHeight, 240)}px`
  }
  // Debounce source suggestion
  if (suggestTimer) clearTimeout(suggestTimer)
  if (draft.value.trim().length > 8) {
    suggestTimer = setTimeout(fetchSuggestions, 400)
  } else {
    suggestedSources.value = []
  }
}

async function fetchSuggestions() {
  try {
    const res = await fetch(`${BASE}/api/sources/suggest`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ query: draft.value }),
    })
    if (!res.ok) return
    const data = await res.json()
    suggestedSources.value = (data.suggested ?? []).slice(0, 6)
    // Reset exclusions when suggestions change
    excludedSources.value = new Set()
  } catch { /* non-critical */ }
}

function toggleSource(id: string) {
  const next = new Set(excludedSources.value)
  if (next.has(id)) next.delete(id)
  else next.add(id)
  excludedSources.value = next
}

async function submit() {
  const text = draft.value.trim()
  if (!text || activeTurn.value) return

  draft.value = ''
  suggestedSources.value = []
  if (textareaEl.value) textareaEl.value.style.height = 'auto'

  // Determine source scope from non-excluded suggestions
  const sources = suggestedSources.value
    .filter(s => !excludedSources.value.has(s.source_id))
    .map(s => s.source_id)
  excludedSources.value = new Set()

  const turn: Turn = {
    query:       text,
    loading:     true,
    status:      'Searching…',
    reasoning:   null,
    summary:     null,
    entries:     [],
    sources,
    showEntries: false,
    saved:       false,
    saving:      false,
    since:       null,
    until:       null,
  }
  turns.value.push(turn)
  activeTurn.value = turn
  await nextTick()
  scrollEl.value?.scrollTo({ top: scrollEl.value.scrollHeight, behavior: 'smooth' })

  try {
    const res = await fetch(`${BASE}/api/diagnose/stream`, {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body:    JSON.stringify({
        query:  text,
        source: sources.length === 1 ? sources[0] : null,
      }),
    })
    if (!res.ok || !res.body) throw new Error(`API ${res.status}`)

    const reader  = res.body.getReader()
    const decoder = new TextDecoder()
    let   buf     = ''

    while (true) {
      const { done, value } = await reader.read()
      if (done) break
      buf += decoder.decode(value, { stream: true })
      const parts = buf.split('\n\n')
      buf = parts.pop() ?? ''
      for (const part of parts) {
        const line = part.trim()
        if (!line.startsWith('data: ')) continue
        const evt = JSON.parse(line.slice(6))
        if      (evt.type === 'status')    { turn.status    = evt.message }
        else if (evt.type === 'summary')   { turn.summary   = evt.data; turn.since = evt.data.window_start; turn.until = evt.data.window_end }
        else if (evt.type === 'entries')   { turn.entries   = evt.data; turn.showEntries = evt.data.length > 0 && evt.data.length <= 10 }
        else if (evt.type === 'reasoning') { turn.reasoning = evt.text; await nextTick(); scrollEl.value?.scrollTo({ top: scrollEl.value.scrollHeight, behavior: 'smooth' }) }
        else if (evt.type === 'done')      { turn.status    = null }
      }
    }
  } catch (e) {
    turn.reasoning = `Error: ${e instanceof Error ? e.message : String(e)}`
  } finally {
    turn.loading     = false
    turn.status      = null
    activeTurn.value = null
    await nextTick()
    scrollEl.value?.scrollTo({ top: scrollEl.value.scrollHeight, behavior: 'smooth' })
    textareaEl.value?.focus()
  }
}

async function saveIncident(turn: Turn) {
  turn.saving = true
  try {
    const res = await fetch(`${BASE}/api/incidents`, {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body:    JSON.stringify({
        label:      turn.query.slice(0, 120),
        started_at: turn.since,
        ended_at:   turn.until,
        severity:   'medium',
        notes:      turn.reasoning ?? '',
      }),
    })
    if (!res.ok) throw new Error(await res.text())
    turn.saved = true
  } catch { /* surface silently — not worth crashing the chat */ }
  finally   { turn.saving = false }
}

function fmtTs(iso: string | null): string {
  if (!iso) return '—'
  try {
    return new Date(iso).toLocaleString(undefined, {
      month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit',
    })
  } catch { return iso }
}
</script>
