<template>
  <div class="rounded border border-accent bg-surface-raised p-6 sm:p-8 max-w-2xl mx-auto">

    <!-- Step indicator -->
    <div class="flex items-center gap-2 mb-6">
      <span v-for="(label, i) in stepLabels" :key="i" class="flex items-center gap-2">
        <span
          class="w-6 h-6 rounded-full flex items-center justify-center text-xs font-semibold border"
          :class="i + 1 === step
            ? 'bg-accent text-bg border-accent'
            : i + 1 < step
              ? 'bg-accent/20 text-accent border-accent/40'
              : 'bg-surface text-text-dim border-surface-border'"
        >{{ i + 1 }}</span>
        <span class="text-xs hidden sm:inline" :class="i + 1 === step ? 'text-text-primary' : 'text-text-dim'">{{ label }}</span>
        <span v-if="i < stepLabels.length - 1" class="text-text-dim text-xs">›</span>
      </span>
    </div>

    <!-- ── Step 1: Detect ── -->
    <div v-if="step === 1">
      <h2 class="text-text-primary text-base font-semibold mb-1">Detecting log sources…</h2>
      <p class="text-text-dim text-sm mb-5">Turnstone is scanning for available log sources on this host.</p>

      <div v-if="discovering" class="flex items-center gap-2 text-text-dim text-sm py-4">
        <svg class="animate-spin w-4 h-4 text-accent" viewBox="0 0 24 24" fill="none">
          <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
          <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"/>
        </svg>
        Scanning…
      </div>

      <div v-else-if="discoverError" class="text-sev-error text-sm py-4">
        {{ discoverError }}
        <button @click="runDiscover" class="ml-2 underline text-accent text-xs">Retry</button>
      </div>

      <div v-else>
        <div v-if="candidates.length === 0" class="text-text-dim text-sm py-3 mb-4">
          No sources auto-detected. You can add sources manually in the next step.
        </div>
        <div v-else class="space-y-1 text-sm mb-4">
          <div v-for="g in groups" :key="g.type" class="flex items-center gap-2 text-text-muted">
            <span class="font-mono text-xs text-text-dim px-1.5 py-0.5 rounded border border-surface-border">{{ g.type }}</span>
            <span><strong class="text-text-primary">{{ g.items.length }}</strong> {{ g.label }}</span>
          </div>
        </div>

        <div class="flex justify-between items-center mt-6">
          <a @click.prevent="$emit('skip')" href="#" class="text-text-dim text-xs hover:text-text-muted">
            Skip — I'll edit sources.yaml manually
          </a>
          <button @click="step = 2" class="btn-primary text-sm">Continue →</button>
        </div>
      </div>
    </div>

    <!-- ── Step 2: Select ── -->
    <div v-if="step === 2">
      <h2 class="text-text-primary text-base font-semibold mb-1">Select log sources</h2>
      <p class="text-text-dim text-sm mb-4">Choose which sources to monitor. You can add more later.</p>

      <!-- Grouped source list -->
      <div class="space-y-3 mb-4">
        <div v-for="g in groups" :key="g.type" class="rounded border border-surface-border overflow-hidden">

          <!-- Group header -->
          <div class="flex items-center gap-3 px-3 py-2 bg-surface border-b border-surface-border">
            <button @click="toggleGroupOpen(g.type)" class="flex items-center gap-2 flex-1 min-w-0 text-left">
              <span class="text-text-dim text-xs">{{ groupOpen[g.type] ? '▾' : '▸' }}</span>
              <span class="text-text-primary text-sm font-medium">{{ g.label }}</span>
              <span class="text-text-dim text-xs">({{ g.items.length }})</span>
              <span v-if="groupSelectedCount(g.type) > 0" class="text-accent text-xs ml-1">
                {{ groupSelectedCount(g.type) }} selected
              </span>
            </button>
            <div class="flex items-center gap-2 shrink-0">
              <button
                @click="selectGroup(g.type)"
                class="text-xs px-2 py-0.5 rounded border border-surface-border text-text-dim hover:text-accent hover:border-accent transition-colors"
              >All</button>
              <button
                @click="deselectGroup(g.type)"
                class="text-xs px-2 py-0.5 rounded border border-surface-border text-text-dim hover:text-sev-error hover:border-sev-error transition-colors"
              >None</button>
            </div>
          </div>

          <!-- Group items -->
          <div v-if="groupOpen[g.type]" class="divide-y divide-surface-border max-h-64 overflow-y-auto">
            <label
              v-for="c in g.items"
              :key="c.id"
              class="flex items-start gap-3 px-3 py-2.5 cursor-pointer transition-colors"
              :class="isSelected(c) ? 'bg-accent/5' : 'hover:bg-surface'"
            >
              <input
                type="checkbox"
                :checked="isSelected(c)"
                @change="toggleCandidate(c)"
                class="mt-0.5 accent-accent shrink-0"
              />
              <div class="min-w-0 flex-1">
                <div class="text-text-primary text-sm">{{ c.label }}</div>
                <div v-if="c.path" class="font-mono text-xs text-text-dim mt-0.5 truncate">{{ c.path }}</div>
                <div v-else-if="c.container" class="font-mono text-xs text-text-dim mt-0.5">{{ c.container }}</div>
              </div>
            </label>
          </div>
        </div>
      </div>

      <!-- NL / manual add -->
      <div class="border border-surface-border rounded p-4 mb-4">
        <p class="text-text-muted text-xs font-medium mb-2">Add a source by description</p>
        <div class="flex gap-2">
          <input
            v-model="nlDescription"
            type="text"
            placeholder="e.g. nginx access log, qbittorrent, sonarr"
            class="flex-1 bg-surface border border-surface-border rounded px-3 py-1.5 text-sm text-text-primary placeholder-text-dim focus:outline-none focus:border-accent"
            @keydown.enter="interpretNL"
          />
          <button
            @click="interpretNL"
            :disabled="!nlDescription.trim() || interpreting"
            class="btn-secondary text-xs px-3 disabled:opacity-40 disabled:cursor-not-allowed"
          >{{ interpreting ? '…' : 'Add' }}</button>
        </div>
        <div v-if="nlError" class="text-sev-error text-xs mt-2">{{ nlError }}</div>
        <div v-if="showManualForm" class="mt-3 space-y-2">
          <p class="text-text-dim text-xs">Couldn't interpret that — fill in manually:</p>
          <div class="flex gap-2">
            <input v-model="manualId"   placeholder="id (e.g. nginx)" class="flex-1 input-sm" />
            <input v-model="manualPath" placeholder="/path/to/log.txt" class="flex-1 input-sm" />
          </div>
          <button @click="addManual" class="btn-secondary text-xs mt-1">Add manually</button>
        </div>
      </div>

      <div class="flex justify-between items-center">
        <button @click="step = 1" class="text-text-dim text-xs hover:text-text-muted">← Back</button>
        <div class="flex items-center gap-3">
          <span class="text-text-dim text-xs">
            {{ selected.length }} source{{ selected.length === 1 ? '' : 's' }} selected
          </span>
          <button
            @click="step = 3"
            :disabled="selected.length === 0"
            class="btn-primary text-sm disabled:opacity-40 disabled:cursor-not-allowed"
          >Review →</button>
        </div>
      </div>
    </div>

    <!-- ── Step 3: Confirm ── -->
    <div v-if="step === 3">
      <h2 class="text-text-primary text-base font-semibold mb-1">Confirm and write</h2>
      <p class="text-text-dim text-sm mb-4">Review the <code class="bg-surface px-1 rounded">sources.yaml</code> that will be written.</p>

      <pre class="bg-surface border border-surface-border rounded p-3 text-xs font-mono text-text-primary overflow-x-auto max-h-64 mb-5 whitespace-pre">{{ previewYaml }}</pre>

      <div v-if="writeError" class="text-sev-error text-sm mb-4">{{ writeError }}</div>
      <div v-if="writeSuccess" class="text-green-400 text-sm mb-4">{{ writeSuccess }}</div>

      <div class="flex justify-between items-center">
        <button @click="step = 2" class="text-text-dim text-xs hover:text-text-muted">← Back</button>
        <button
          @click="writeAndFinish"
          :disabled="writing"
          class="btn-primary text-sm disabled:opacity-40 disabled:cursor-not-allowed"
        >{{ writing ? 'Writing…' : 'Write sources.yaml' }}</button>
      </div>
    </div>

  </div>
</template>

<script setup lang="ts">
import { ref, computed, reactive, onMounted } from 'vue'

const BASE = import.meta.env.BASE_URL.replace(/\/$/, '')

const emit = defineEmits<{ done: []; skip: [] }>()

interface Candidate {
  type: string
  id: string
  label: string
  description: string
  path?: string
  container?: string
  runtime?: string
  unit?: string
  available: boolean
}

interface Group {
  type: string
  label: string
  items: Candidate[]
}

const GROUP_META: Record<string, { label: string; order: number; defaultOpen: boolean; preselect: boolean }> = {
  journald: { label: 'System journal',      order: 0, defaultOpen: true,  preselect: true  },
  file:     { label: 'Log files',           order: 1, defaultOpen: true,  preselect: true  },
  docker:   { label: 'Docker containers',   order: 2, defaultOpen: false, preselect: false },
}

const stepLabels = ['Detect', 'Select', 'Confirm']
const step        = ref(1)
const discovering = ref(false)
const discoverError = ref<string | null>(null)
const candidates  = ref<Candidate[]>([])
const selected    = ref<Candidate[]>([])

// Track which groups are expanded
const groupOpen = reactive<Record<string, boolean>>({})

const groups = computed<Group[]>(() => {
  const map: Record<string, Candidate[]> = {}
  for (const c of candidates.value) {
    ;(map[c.type] ??= []).push(c)
  }
  return Object.entries(map)
    .map(([type, items]) => ({
      type,
      label: GROUP_META[type]?.label ?? type,
      items,
    }))
    .sort((a, b) => (GROUP_META[a.type]?.order ?? 99) - (GROUP_META[b.type]?.order ?? 99))
})

function groupSelectedCount(type: string): number {
  const group = groups.value.find(g => g.type === type)
  if (!group) return 0
  return group.items.filter(c => isSelected(c)).length
}

function toggleGroupOpen(type: string) {
  groupOpen[type] = !groupOpen[type]
}

function selectGroup(type: string) {
  const group = groups.value.find(g => g.type === type)
  if (!group) return
  const newIds = new Set(selected.value.map(s => s.id))
  const additions = group.items.filter(c => !newIds.has(c.id))
  selected.value = [...selected.value, ...additions]
  groupOpen[type] = true
}

function deselectGroup(type: string) {
  const group = groups.value.find(g => g.type === type)
  if (!group) return
  const removeIds = new Set(group.items.map(c => c.id))
  selected.value = selected.value.filter(s => !removeIds.has(s.id))
}

// NL / manual add
const nlDescription  = ref('')
const interpreting   = ref(false)
const nlError        = ref<string | null>(null)
const showManualForm = ref(false)
const manualId       = ref('')
const manualPath     = ref('')

// Write
const writing      = ref(false)
const writeError   = ref<string | null>(null)
const writeSuccess = ref<string | null>(null)

const previewYaml = computed(() => {
  if (!selected.value.length) return '# No sources selected'
  const lines = ['sources:']
  for (const src of selected.value) {
    if (src.type === 'journald') {
      lines.push(`  - id: ${src.id}`)
      lines.push(`    type: journald`)
      if (src.unit) lines.push(`    unit: ${src.unit}`)
    } else if (src.type === 'docker') {
      lines.push(`  - id: ${src.id}`)
      lines.push(`    type: docker`)
      lines.push(`    runtime: ${src.runtime ?? 'docker'}`)
      lines.push(`    container: ${src.container ?? src.id.split(':').pop()}`)
    } else {
      lines.push(`  - id: ${src.id}`)
      lines.push(`    path: ${src.path}`)
    }
  }
  return lines.join('\n')
})

function isSelected(c: Candidate): boolean {
  return selected.value.some(s => s.id === c.id)
}

function toggleCandidate(c: Candidate) {
  if (isSelected(c)) {
    selected.value = selected.value.filter(s => s.id !== c.id)
  } else {
    selected.value = [...selected.value, c]
  }
}

async function runDiscover() {
  discovering.value   = true
  discoverError.value = null
  try {
    const res = await fetch(`${BASE}/api/setup/discover`)
    if (!res.ok) throw new Error(`HTTP ${res.status}`)
    const data = await res.json()
    candidates.value = data.candidates ?? []

    // Initialise group open state and pre-selection per group meta
    for (const [type, meta] of Object.entries(GROUP_META)) {
      groupOpen[type] = meta.defaultOpen
    }
    // Any type not in GROUP_META gets collapsed by default
    for (const c of candidates.value) {
      if (!(c.type in groupOpen)) groupOpen[c.type] = false
    }

    // Pre-select only groups where preselect = true
    selected.value = candidates.value.filter(c => GROUP_META[c.type]?.preselect ?? false)
  } catch (e: any) {
    discoverError.value = e.message ?? 'Discovery failed'
  } finally {
    discovering.value = false
  }
}

async function interpretNL() {
  if (!nlDescription.value.trim()) return
  interpreting.value   = true
  nlError.value        = null
  showManualForm.value = false
  try {
    const res = await fetch(`${BASE}/api/setup/interpret`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ description: nlDescription.value }),
    })
    const data = await res.json()
    if (data.source) {
      const candidate: Candidate = { available: true, ...data.source }
      // Add to candidates so it appears in the correct group
      if (!candidates.value.some(c => c.id === candidate.id)) {
        candidates.value = [...candidates.value, candidate]
        if (!(candidate.type in groupOpen)) groupOpen[candidate.type] = true
      }
      if (!isSelected(candidate)) selected.value = [...selected.value, candidate]
      nlDescription.value = ''
    } else {
      showManualForm.value = true
      nlError.value = data.validation_error
        ? `Validation: ${data.validation_error}`
        : 'Could not interpret — fill in manually below.'
    }
  } catch {
    showManualForm.value = true
    nlError.value = 'Interpretation failed — fill in manually below.'
  } finally {
    interpreting.value = false
  }
}

function addManual() {
  if (!manualId.value.trim() || !manualPath.value.trim()) return
  const candidate: Candidate = {
    type: 'file',
    id: manualId.value.trim(),
    path: manualPath.value.trim(),
    label: manualId.value.trim(),
    description: `Read from ${manualPath.value.trim()}`,
    available: true,
  }
  if (!candidates.value.some(c => c.id === candidate.id)) {
    candidates.value = [...candidates.value, candidate]
    groupOpen['file'] = true
  }
  if (!isSelected(candidate)) selected.value = [...selected.value, candidate]
  manualId.value       = ''
  manualPath.value     = ''
  showManualForm.value = false
  nlDescription.value  = ''
  nlError.value        = null
}

async function writeAndFinish() {
  writing.value    = true
  writeError.value = null
  try {
    const res = await fetch(`${BASE}/api/setup/write`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ sources: selected.value }),
    })
    if (!res.ok) {
      const err = await res.json().catch(() => ({ detail: res.statusText }))
      writeError.value = err.detail ?? 'Write failed'
      return
    }
    const data = await res.json()
    writeSuccess.value = `Wrote ${data.written} source${data.written === 1 ? '' : 's'} to sources.yaml.`
    setTimeout(() => emit('done'), 1200)
  } catch (e: any) {
    writeError.value = e.message ?? 'Network error'
  } finally {
    writing.value = false
  }
}

onMounted(runDiscover)
</script>

<style scoped>
.input-sm {
  @apply bg-surface border border-surface-border rounded px-2 py-1 text-xs text-text-primary focus:outline-none focus:border-accent;
}
</style>
