<script setup>
import { ref, computed, watch, onUnmounted } from 'vue'
import { fetchBlocksBoard, fetchRuns, runQa, runRepair, removeArtefacts } from '../api.js'
import { usePolling } from '../composables/usePolling.js'
import { useConfirm } from '../composables/useConfirm.js'
import { fmtRuntime, fmtTokens } from '../format.js'
import KanbanBoard from './KanbanBoard.vue'
import GuideBoardSection from './GuideBoardSection.vue'
import ProgressBar from './ProgressBar.vue'

const props = defineProps({
  topic: { type: String, required: true },
  generating: { type: Boolean, default: false },
  progress: { type: String, default: null },
  ready: { type: Boolean, default: false },
  partial: { type: Boolean, default: false },
  error: { type: String, default: null },
  guideFormat: { type: String, default: 'Guide' },
  autoInventory: { type: Boolean, default: true },
  autoArtefacts: { type: Boolean, default: true },
  autoGuide: { type: Boolean, default: true },
})
const emit = defineEmits(['close', 'restartAll', 'continueAll', 'addResearch', 'requeueDead',
  'removeAll', 'removeArtefacts', 'cancel', 'cancelGuide', 'startGuide', 'preview', 'removeFormat', 'setAuto'])

// ── Blocks-Pipeline (Poll 1.2s solange generiert; Visibility-Pause via usePolling) ──
const board = ref(null)
const pollError = ref(null)

async function pollBoard() {
  try {
    board.value = await fetchBlocksBoard(props.topic)
    pollError.value = null
  } catch (e) {
    if (e.status === 404) board.value = null
    else pollError.value = e.message
  }
}
const { start: startPoll } = usePolling(pollBoard, () => props.generating, 1200)

watch(() => props.topic, () => { board.value = null; pollBoard() }, { immediate: true })
watch(() => props.generating, (g) => {
  if (g) startPoll()
  else pollBoard()  // Endstand nachladen
}, { immediate: true })

const inventoryCols = computed(() => (board.value?.columns || []).filter((c) => c.board === 'inventory'))
const artefactCols = computed(() => (board.value?.columns || []).filter((c) => c.board === 'artefacts'))
const dead = computed(() => board.value?.dead || [])
const qa = computed(() => board.value?.qa || null)

// Ehrlicher Fortschritt aus den Spaltenzahlen: gewichteter Spaltenindex (Terminal = 1,0).
const TERMINAL = new Set(['done_block', 'done_artefact', 'rejected', 'grouped'])
function boardProgress(cols) {
  const total = cols.reduce((n, c) => n + c.total, 0)
  if (!total) return { value: 0, done: 0, total: 0 }
  const last = Math.max(1, cols.length - 1)
  let acc = 0
  cols.forEach((c, i) => { acc += (TERMINAL.has(c.key) ? 1 : i / last) * c.total })
  const done = cols.filter((c) => TERMINAL.has(c.key)).reduce((n, c) => n + c.total, 0)
  return { value: acc / total, done, total }
}
const inventoryProgress = computed(() => boardProgress(inventoryCols.value))
const artefactProgress = computed(() => boardProgress(artefactCols.value))
const scopeGrowing = computed(() => props.generating && (board.value?.agents?.length || 0) > 0)

// Score 0–10 → „100 %"
const pct = (note) => Math.round(note * 10) + ' %'

// ── Laufzeit + Tokens aus /api/runs (5s-Takt, unabhängig vom 1,2s-Board-Poll) ──────
const run = ref(null)
const now = ref(Date.now())
let clock = null
async function loadRun() {
  try {
    const { runs } = await fetchRuns(props.topic, 1)
    run.value = runs[0] || null
  } catch { run.value = null }
}
const { start: startRunPoll } = usePolling(loadRun, () => props.generating, 5000)
const runLaufzeit = computed(() => {
  if (!run.value?.start) return null
  const start = Date.parse(run.value.start)
  const ende = run.value.aktiv ? now.value : Date.parse(run.value.ende || run.value.start)
  return fmtRuntime((ende - start) / 1000)
})
const runTokens = computed(() => {
  const t = run.value?.tokens
  return t ? fmtTokens((t.input || 0) + (t.output || 0)) : null
})
const runTokenTitel = computed(() => {
  const t = run.value?.tokens || {}
  return `Input ${t.input || 0} · Output ${t.output || 0} · Cache ${(t.cache_read || 0) + (t.cache_write || 0)}`
})
watch(() => props.generating, (g) => {
  if (g) { startRunPoll(); if (!clock) clock = setInterval(() => { now.value = Date.now() }, 1000) }
  else { loadRun(); if (clock) { clearInterval(clock); clock = null } }  // Endstand
}, { immediate: true })
watch(() => props.topic, () => { run.value = null; loadRun() })
onUnmounted(() => { if (clock) clearInterval(clock) })

const { isArmed, armOrRun, reset: resetConfirm } = useConfirm()  // 2-Klick-Bestätigung
function later(fn) { fn(); setTimeout(pollBoard, 600) }

// ── QA + Befunde beheben (je Ebene) ──────────────────────────────────────────────
const qaBusy = ref(false)
const guideRefresh = ref(0)  // QA/Repair schreiben auch den Guide-Report → Badge neu laden
async function runQaClick() {
  if (qaBusy.value) return
  qaBusy.value = true
  try { await runQa(props.topic) }
  finally { qaBusy.value = false; pollBoard(); guideRefresh.value++ }
}

const repairBusy = ref('')            // '' | 'inventory' | 'artefacts'
const repairInfo = ref({})            // { inventory, artefacts }
function repairText(r) {
  const n = (r.hygiene || []).length + (r.merges || []).length + (r.sub_merges || []).length
    + (r.entfernt || []).length + (r.aufgeraeumt || 0) + (r.freigesprochen || []).length
  return n === 0 ? 'keine behebbaren Befunde'
    : `${(r.hygiene || []).length} Titel · ${(r.merges || []).length} Merges · ${(r.sub_merges || []).length} Sub-Merges · ${(r.entfernt || []).length} entfernt · ${r.aufgeraeumt || 0} aufgeräumt`
}
// Auto an → „Befunde beheben"-Loop bis 100 % / Stillstand / 10×; sonst genau ein Durchlauf.
async function repairClick(ebene) {
  if (repairBusy.value) return
  const auto = ebene === 'inventory' ? props.autoInventory : props.autoArtefacts
  const max = auto ? 10 : 1
  repairBusy.value = ebene
  repairInfo.value = { ...repairInfo.value, [ebene]: '' }
  let prev = null
  try {
    for (let i = 0; i < max; i++) {
      const r = await runRepair(props.topic, ebene)
      const note = ebene === 'artefacts' ? r.note_artefakte : r.note
      const suffix = max > 1 ? ` — Runde ${i + 1}${note != null ? ` (${Math.round(note * 10)} %)` : ''}` : ''
      repairInfo.value = { ...repairInfo.value, [ebene]: repairText(r) + suffix }
      if (note == null || note >= 10) break          // 100 % erreicht
      if (prev != null && note <= prev) break         // Stillstand: Repair bewegt nichts mehr
      prev = note
    }
  } catch (e) {
    repairInfo.value = { ...repairInfo.value, [ebene]: String(e.message || e) }
  } finally {
    repairBusy.value = ''; pollBoard()
  }
}

function removeArtefactsClick() {
  later(() => removeArtefacts(props.topic).then(pollBoard))
}
</script>

<template>
  <div class="gen-view">
    <header class="gen-head">
      <h1>{{ topic }}</h1>
      <span class="gen-sub">Generierung</span>
      <span class="gen-spacer"></span>
      <button class="gen-close" title="Schließen" @click="emit('close')">✕</button>
    </header>

    <div v-if="pollError" class="gen-poll-error">Board nicht erreichbar: {{ pollError }}</div>

    <section class="gen-section">
      <div class="gen-steps-top">
        <span class="gen-title">Bausteine</span>
        <div v-if="progress" class="gen-progress"><span class="gen-progress-dot"></span>{{ progress }}</div>
        <span v-if="runLaufzeit" class="gen-run" :title="runTokenTitel">⏱ {{ runLaufzeit }}<template v-if="runTokens"> · {{ runTokens }} Tokens</template></span>
        <span v-if="run?.fails?.length" class="gen-run-fail" :title="run.fails.map((f) => f.key + ': ' + f.error).join('\n')">⚠ {{ run.fails.length }} Fehler</span>
        <div v-if="generating" class="gen-actions">
          <button class="gen-act" @click="emit('addResearch')">+ Recherche</button>
          <button class="gen-act danger" @click="emit('cancel')">Abbrechen</button>
        </div>
        <button
          v-if="dead.length && generating"
          class="gen-act"
          :title="dead.map((d) => d.title + ': ' + d.error).join('\n')"
          @click="later(() => emit('requeueDead'))"
        >⟳ {{ dead.length }} dead</button>
      </div>

      <!-- Ebene Inventar -->
      <div class="gen-board-label">
        Inventar
        <span v-if="qa && qa.note != null" class="qa-note" :class="qa.note >= 10 ? 'ok' : 'bad'">QA {{ pct(qa.note) }}</span>
        <label class="auto-box" title="Nach der QA automatisch „Befunde beheben“, bis 100 % (max 10×)">
          <input type="checkbox" :checked="autoInventory" @change="emit('setAuto', { ebene: 'inventory', value: $event.target.checked })"> Auto
        </label>
      </div>
      <div v-if="!generating" class="gen-bar">
        <button class="gen-act" :disabled="qaBusy" @click="runQaClick">{{ qaBusy ? 'QA läuft…' : 'QA' }}</button>
        <button v-if="qa" class="gen-act" :disabled="!!repairBusy" @click="repairClick('inventory')">{{ repairBusy === 'inventory' ? 'Repariert…' : 'Befunde beheben' }}</button>
        <span v-if="repairInfo.inventory" class="repair-info">{{ repairInfo.inventory }}</span>
        <button class="gen-act play" @click="emit('restartAll')">{{ ready || partial ? '+ Recherche' : 'Generieren' }}</button>
        <button v-if="partial" class="gen-act" @click="emit('continueAll')">Fortsetzen</button>
        <button v-if="ready || partial || error" class="gen-act danger" :class="{ armed: isArmed('remove') }"
                title="Löscht das gesamte Topic-Inventar (Artefakte + Guide hängen daran)"
                @click="armOrRun('remove', () => later(() => emit('removeAll')))">{{ isArmed('remove') ? 'Sicher?' : 'Entfernen' }}</button>
      </div>
      <ProgressBar v-if="inventoryProgress.total" :value="inventoryProgress.value"
                   :label="`${inventoryProgress.done}/${inventoryProgress.total} Karten fertig · ${Math.round(inventoryProgress.value * 100)} %`"
                   :hint="scopeGrowing ? 'Umfang wächst noch' : ''" />
      <KanbanBoard :columns="inventoryCols" :agents="board?.agents || []" :generating="generating" />

      <!-- Ebene Artefakte -->
      <div class="gen-board-label">
        Artefakte
        <span v-if="qa && qa.note_artefakte != null" class="qa-note" :class="qa.note_artefakte >= 10 ? 'ok' : 'bad'">QA {{ pct(qa.note_artefakte) }}</span>
        <label class="auto-box" title="Nach der QA automatisch „Befunde beheben“, bis 100 % (max 10×)">
          <input type="checkbox" :checked="autoArtefacts" @change="emit('setAuto', { ebene: 'artefacts', value: $event.target.checked })"> Auto
        </label>
      </div>
      <div v-if="!generating" class="gen-bar">
        <button class="gen-act" :disabled="qaBusy" @click="runQaClick">{{ qaBusy ? 'QA läuft…' : 'QA' }}</button>
        <button v-if="qa" class="gen-act" :disabled="!!repairBusy" @click="repairClick('artefacts')">{{ repairBusy === 'artefacts' ? 'Repariert…' : 'Befunde beheben' }}</button>
        <span v-if="repairInfo.artefacts" class="repair-info">{{ repairInfo.artefacts }}</span>
        <button class="gen-act play" title="Board 2 auf dem fertigen Inventar bauen (Fortsetzen)" @click="emit('continueAll', { qaForce: true })">Generieren</button>
        <button v-if="ready || partial" class="gen-act danger" :class="{ armed: isArmed('remove-art') }"
                title="Nur die Artefakte leeren — Inventar bleibt"
                @click="armOrRun('remove-art', removeArtefactsClick)">{{ isArmed('remove-art') ? 'Sicher?' : 'Entfernen' }}</button>
      </div>
      <ProgressBar v-if="artefactProgress.total" :value="artefactProgress.value"
                   :label="`${artefactProgress.done}/${artefactProgress.total} Karten fertig · ${Math.round(artefactProgress.value * 100)} %`"
                   :hint="scopeGrowing ? 'Umfang wächst noch' : ''" />
      <KanbanBoard :columns="artefactCols" :generating="generating" />
    </section>

    <section class="gen-section">
      <GuideBoardSection
        :topic="topic"
        :format="guideFormat"
        :refresh="guideRefresh"
        :auto="autoGuide"
        @cancelGuide="(id) => emit('cancelGuide', id)"
        @startGuide="(p) => emit('startGuide', p)"
        @preview="emit('preview')"
        @removeFormat="(f) => emit('removeFormat', f)"
        @setAuto="(v) => emit('setAuto', { ebene: 'guide', value: v })"
      />
    </section>
  </div>
</template>

<style scoped>
.gen-view {
  flex: 1;
  min-width: 0;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  background: var(--bg-preview);
}
.gen-head {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  padding: 1.25rem 2rem;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
}
.gen-head h1 { font-size: 1.5rem; }
.gen-sub { color: var(--text-faint); font-size: 0.9rem; font-weight: 600; }
.gen-spacer { flex: 1; }
.gen-close {
  align-self: center;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: var(--panel);
  color: var(--text);
  width: 2rem;
  height: 2rem;
  cursor: pointer;
}
.gen-close:hover { border-color: var(--accent); }

.gen-section {
  padding: 0.85rem 2rem;
  border-bottom: 1px solid var(--border);
  background: var(--panel-soft);
}
.gen-title { font-size: 0.9rem; font-weight: 700; }
.gen-board-label {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.64rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
  margin: 0.7rem 0 0.3rem;
}
.gen-progress {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.84rem;
  color: var(--accent);
  font-weight: 600;
}
.gen-progress-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  animation: pulse-soft 1.2s ease-in-out infinite;
}

.gen-steps-top { display: flex; align-items: center; gap: 1rem; min-height: 1.9rem; margin-bottom: 0.4rem; }
.gen-actions { margin-left: auto; display: flex; gap: 0.4rem; }
.gen-bar { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin: 0.1rem 0 0.4rem; }

.auto-box {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
}
.auto-box input { cursor: pointer; margin: 0; }

.gen-act {
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: var(--panel);
  color: var(--text);
  font-size: 0.8rem;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
  font-weight: 600;
}
.gen-act:hover { border-color: var(--accent); }
.gen-act:disabled { opacity: 0.55; cursor: default; }
.gen-act.play { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.gen-act.play:hover { background: var(--accent-hover); }
.gen-act.danger { color: var(--danger); border-color: var(--danger); background: transparent; }
.gen-act.danger.armed { background: var(--danger); color: #fff; }
.qa-note {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  text-transform: none;
  letter-spacing: 0;
}
.qa-note.ok { background: color-mix(in srgb, #22c55e 18%, transparent); color: #16a34a; }
.qa-note.bad { background: color-mix(in srgb, #ef4444 18%, transparent); color: #dc2626; }
.repair-info { font-size: 0.78rem; color: var(--text-muted); }
.gen-poll-error { color: var(--danger); font-size: 0.82rem; padding: 0.3rem 0; }
.gen-run { font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.gen-run-fail { font-size: 0.78rem; color: var(--danger); white-space: nowrap; }
</style>
