<script setup>
import { computed, reactive, ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
import { fetchGuideContent, chatGuide, fetchBausteinLernstand } from '../api.js'
import { renderMarkdown } from '../markdown.js'
import { useChat } from '../composables/useChat.js'
import BausteinPanel from './BausteinPanel.vue'
import BausteinFokus from './BausteinFokus.vue'

const props = defineProps({
  previewGuide: { type: Object, default: null },
  dark: { type: Boolean, default: false },
  provider: { type: String, default: 'claude' },
  elementsOpen: { type: Boolean, default: false }, // Element-Sidebar offen → Chat nach links
  doneByFormat: { type: Object, default: () => ({}) }, // Format → fertiger Guide (Themen-bezogen)
  themaAbgeschlossen: { type: Boolean, default: false },
  ansichtModus: { type: String, default: 'kompakt' }, // kompakt | erklärend
})

const emit = defineEmits(['progressChanged', 'setAnsicht', 'openSidebar', 'fokusActive'])

// Rotierende Kapitel-Akzentfarben (ohne Rot)
const CH_COLORS = ['#3b82f6', '#8b5cf6', '#14b8a6', '#f59e0b', '#22c55e', '#6366f1']

// --- Inhalt laden ---
const content = ref(null)
const loadError = ref(null)
const scrollEl = ref(null)
const lernstand = ref({}) // Prüfungs-Stand pro Baustein-Titel — VOR dem immediate-Watch (loadContent nutzt es)

// --- Lazy-Render + Markdown-Cache: nur sichtbare Sections parsen, jede nur einmal.
// Behebt das Blockieren beim Öffnen (160× marked/highlight.js) und Re-Parse bei jedem Update. ---
const mdCache = new Map()       // `${modus}:${num}` → html
const sichtbar = reactive({})   // num → true (bleibt true, sobald je sichtbar)
let mdObserver = null

function htmlFor(s) {
  const key = `${props.ansichtModus}:${s.num}`
  let h = mdCache.get(key)
  if (h === undefined) {
    h = renderMarkdown(props.ansichtModus === 'kompakt' ? (s.kompakt || s.md) : s.md)
    mdCache.set(key, h)
  }
  return h
}

// Sections rendern erst, wenn sie (fast) im Viewport sind — Observer auf den Scroll-Container.
function setupLazy() {
  mdObserver?.disconnect()
  if (!scrollEl.value) return
  mdObserver = new IntersectionObserver((entries) => {
    for (const e of entries) {
      if (!e.isIntersecting) continue
      sichtbar[Number(e.target.dataset.num)] = true
      mdObserver.unobserve(e.target)
    }
  }, { root: scrollEl.value, rootMargin: '800px 0px' })
  for (const el of scrollEl.value.querySelectorAll('.section-card')) mdObserver.observe(el)
}

watch(content, () => nextTick(setupLazy))
onUnmounted(() => mdObserver?.disconnect())

watch(() => props.previewGuide?.id, loadContent, { immediate: true })

async function loadContent() {
  content.value = null
  loadError.value = null
  lernstand.value = {}
  mdCache.clear()
  for (const k in sichtbar) delete sichtbar[k]
  const g = props.previewGuide
  if (!g || g.status !== 'done') return
  try {
    content.value = await fetchGuideContent(g.id)
  } catch (e) {
    console.error('Fehler beim Laden des Guides:', e)
    loadError.value = 'Inhalt nicht verfügbar — die Datei fehlt. Guide neu generieren (▶).'
    return
  }
  try {
    lernstand.value = (await fetchBausteinLernstand(g.topic)).bausteine || {}
  } catch { /* offline → leer */ }
}

// --- Baustein-Lernen: Prüfungs-Stand pro Baustein-Titel (lernstand oben deklariert) ---
function onBausteinStatus(baustein, status) {
  const warAbsolviert = lernstand.value[baustein]?.absolviert
  lernstand.value = { ...lernstand.value, [baustein]: status }
  if (status.absolviert && !warAbsolviert) emit('progressChanged') // Locks/Stats neu laden
}

// --- Vollbild-Fokus: ein Baustein groß, Guide links + Prüfung rechts ---
const fokusIndex = ref(null)        // Index in der flachen Baustein-Liste; null = zu
const fokusTab = ref('pruefung')
const bausteine = computed(() =>
  !content.value ? [] : content.value.chapters.flatMap((ch) => ch.sections),
)
const fokusBaustein = computed(() => (fokusIndex.value === null ? null : bausteine.value[fokusIndex.value]))

// Fokus-Zustand an App melden (für die Sidebar über dem Overlay); Themenwechsel schließt den Fokus.
watch(fokusIndex, (v) => emit('fokusActive', v !== null))
watch(() => props.previewGuide?.id, () => { fokusIndex.value = null })

function openFokus(baustein, tab) {
  if (!istPruefbar(baustein)) return // reine Lese-Sections (Rest/Rand) öffnen keinen Prüfungs-Fokus
  const i = bausteine.value.findIndex((s) => s.title === baustein.title)
  if (i === -1) return
  fokusIndex.value = i
  fokusTab.value = tab || 'pruefung'
}
// Nur prüfbare Sections im Fokus anspringen (FullGuide hat dazwischen reine Lese-Sections).
function fokusPrev() {
  for (let i = fokusIndex.value - 1; i >= 0; i--) if (istPruefbar(bausteine.value[i])) { fokusIndex.value = i; return }
}
function fokusNext() {
  for (let i = fokusIndex.value + 1; i < bausteine.value.length; i++) if (istPruefbar(bausteine.value[i])) { fokusIndex.value = i; return }
}

// Erfahrungsleiste: Lernstand über alle Bausteine des Guides (sticky, absolviert ⊇ verstanden ⊇ gemeistert).
const fortschritt = computed(() => {
  let a = 0, v = 0, g = 0
  for (const s of bausteine.value) {
    const l = lernstand.value[s.title] || {}
    if (l.absolviert) a++
    if (l.verstanden) v++
    if (l.gemeistert) g++
  }
  return { total: bausteine.value.length, absolviert: a, verstanden: v, gemeistert: g }
})

// Prüfung läuft voll 0–30, sobald Guide ODER FullGuide fertig ist (gleicher Baustein-Score).
const cap = computed(() => ((props.doneByFormat?.Guide || props.doneByFormat?.FullGuide) ? 30 : 6))

// Section prüfbar? Rest nie. Sonst prüfbar, außer das Feld ist explizit false (FullGuide-Rand).
// Fehlt das Feld (alte Guides ohne `pruefbar`) → prüfbar, damit Bestands-Guides weiter funktionieren.
function istPruefbar(s) {
  return props.previewGuide?.format !== 'Rest' && s.pruefbar !== false
}

// --- Chat (Mechanik in useChat; Kontext-Extraktion bleibt hier) ---
const chat = useChat((msgs) => {
  const { section, outline } = extractContext()
  return chatGuide(props.previewGuide.id, {
    section, outline, messages: msgs, provider: props.provider,
  })
})
const { messages, input, loading, messagesEl, inputEl, onScroll, send } = chat
const autoGrow = () => chat.autoGrow()
const chatOpen = ref(false)
const panelEl = ref(null)

function openChat() {
  chatOpen.value = true
  nextTick(() => inputEl.value?.focus())
}

function closeChat() {
  chatOpen.value = false
  chat.reset()
}

// Mobil schließen sich Chat und Elemente-Sidebar gegenseitig aus —
// nebeneinander ist kein Platz, die Sidebar würde den Chat überdecken.
watch(() => props.elementsOpen, (open) => {
  if (open && chatOpen.value && window.matchMedia('(max-width: 768px)').matches) closeChat()
})

function onDocMouseDown(e) {
  if (!chatOpen.value) return
  if (panelEl.value && panelEl.value.contains(e.target)) return
  closeChat()
}

// Enter öffnet den Chat (wenn zu, nicht in Eingabefeld); ESC schließt ihn
function onDocKeyDown(e) {
  if (e.key === 'Escape' && chatOpen.value) {
    e.preventDefault()
    closeChat()
    return
  }
  if (e.key !== 'Enter' || chatOpen.value || !props.previewGuide) return
  const tag = document.activeElement?.tagName
  if (tag === 'INPUT' || tag === 'TEXTAREA') return
  e.preventDefault()
  openChat()
}

onMounted(() => {
  document.addEventListener('mousedown', onDocMouseDown, true)
  document.addEventListener('keydown', onDocKeyDown)
})
onUnmounted(() => {
  document.removeEventListener('mousedown', onDocMouseDown, true)
  document.removeEventListener('keydown', onDocKeyDown)
})

function extractContext() {
  if (!content.value) return { section: '', outline: '' }
  const outline = content.value.chapters
    .map((ch) => [ch.title, ...ch.sections.map((s) => '  ' + s.title)].join('\n'))
    .join('\n')
    .slice(0, 7000)

  // Aktuelle Section = letzte Karte, deren Oberkante oben im Viewport oder darüber liegt
  let section = ''
  const cards = Array.from(scrollEl.value?.querySelectorAll('.section-card') || [])
  let current = null
  for (const el of cards) {
    if (el.getBoundingClientRect().top <= 120) current = el
    else break
  }
  if (!current && cards.length) current = cards[0]
  if (current) section = current.innerText.trim().slice(0, 18000)
  return { section, outline }
}

</script>

<template>
  <div class="detail">
    <div v-if="previewGuide && content" ref="scrollEl" class="guide-scroll">
      <div class="guide-content">
        <header class="guide-head">
          <h1>{{ previewGuide.topic }}</h1>
          <span class="guide-format">{{ previewGuide.format }}</span>
          <span v-if="themaAbgeschlossen" class="thema-done" title="Alle Bausteine gemeistert (18/18)">✓ Thema abgeschlossen</span>
        </header>

        <section
          v-for="(ch, ci) in content.chapters"
          :key="ch.title"
          class="chapter"
          :style="{ '--ch-accent': CH_COLORS[ci % CH_COLORS.length] }"
        >
          <h2 class="chapter-title"><span class="ch-num">{{ ci + 1 }}</span>{{ ch.title }}</h2>
          <div class="sections">
            <article
              v-for="s in ch.sections"
              :key="s.num"
              :data-num="s.num"
              :class="['section-card', lernstand[s.title]?.gemeistert ? 'gemeistert' : (lernstand[s.title]?.verstanden ? 'verstanden' : (lernstand[s.title]?.absolviert ? 'absolviert' : ''))]"
            >
              <h3 :class="{ 'baustein-klick': istPruefbar(s) }" @click="istPruefbar(s) && openFokus(s, 'pruefung')">
                {{ s.title }}
                <template v-if="istPruefbar(s)">
                  <span v-if="lernstand[s.title]?.gemeistert" class="baustein-done gemeistert" title="Gemeistert (25/25)">✓✓✓ Gemeistert</span>
                  <span v-else-if="lernstand[s.title]?.verstanden" class="baustein-done verstanden" title="Vollständig verstanden (10/10)">✓✓ Verstanden</span>
                  <span v-else-if="lernstand[s.title]?.absolviert" class="baustein-done" title="Prüfung bestanden">✓ Absolviert</span>
                </template>
              </h3>
              <div v-if="sichtbar[s.num]" class="section-body markdown" v-html="htmlFor(s)"></div>
              <div v-else class="section-body skeleton"></div>
              <BausteinPanel
                v-if="istPruefbar(s)"
                mode="trigger"
                :baustein="s.title"
                :status="lernstand[s.title]"
                :cap="cap"
                :topic="previewGuide.topic"
                @open-fokus="(tab) => openFokus(s, tab)"
              />
            </article>
          </div>
        </section>
      </div>
    </div>

    <div v-else-if="previewGuide" class="empty-preview">
      <p>{{ loadError || 'Lade Inhalt…' }}</p>
    </div>

    <div class="empty-preview" v-else>
      <p>Guide-Format anklicken um zu generieren oder Vorschau zu öffnen.</p>
    </div>

    <BausteinFokus
      v-if="fokusBaustein"
      :baustein="fokusBaustein"
      :topic="previewGuide.topic"
      :provider="provider"
      :fortschritt="fortschritt"
      :status="lernstand[fokusBaustein.title]"
      :cap="cap"
      :tab="fokusTab"
      :ansicht="ansichtModus"
      :has-prev="fokusIndex > 0"
      :has-next="fokusIndex < bausteine.length - 1"
      @prev="fokusPrev"
      @next="fokusNext"
      @close="fokusIndex = null"
      @set-ansicht="$emit('setAnsicht', $event)"
      @status-changed="(st) => onBausteinStatus(st.baustein, st)"
      @open-sidebar="$emit('openSidebar')"
    />

    <button v-if="previewGuide && !chatOpen && fokusIndex === null" class="chat-fab" :class="{ shifted: elementsOpen }" title="Fragen zum Guide" @click="openChat">💬</button>

    <div v-if="previewGuide && chatOpen" ref="panelEl" class="chat-panel" :class="{ shifted: elementsOpen }">
      <header class="chat-header">
        <span>Fragen zum Guide</span>
        <button class="chat-close" title="Chat beenden" @click="closeChat">×</button>
      </header>
      <div ref="messagesEl" class="chat-messages" @scroll="onScroll">
        <p v-if="!messages.length" class="chat-hint">Stell eine Frage zum aktuellen Abschnitt.</p>
        <template v-for="(m, i) in messages" :key="i">
          <div v-if="m.role === 'assistant'" class="chat-msg assistant markdown" v-html="renderMarkdown(m.content)"></div>
          <div v-else class="chat-msg user">{{ m.content }}</div>
        </template>
        <div v-if="loading" class="chat-msg assistant chat-typing">Denkt…</div>
      </div>
      <div class="chat-input">
        <textarea
          ref="inputEl"
          v-model="input"
          rows="3"
          placeholder="Frage stellen…"
          @input="autoGrow"
          @keydown.enter.exact.prevent="send"
        ></textarea>
        <button
          :disabled="!input.trim() && !loading"
          :class="{ cancel: loading }"
          :title="loading ? 'Abbrechen' : 'Senden'"
          @click="send"
        >{{ loading ? '✕' : '➤' }}</button>
      </div>
    </div>
  </div>
</template>

<style scoped>
.detail {
  flex: 1;
  /* Flex-Item darf schmaler werden als seine Code-Blöcke — sonst sprengt
     deren Mindestbreite auf Mobile das Layout */
  min-width: 0;
  height: 100dvh;
  position: relative;
}

.guide-scroll {
  height: 100%;
  overflow-y: auto;
  /* Kein horizontales Pannen der ganzen Seite — Code-Blöcke scrollen intern */
  overflow-x: hidden;
  background: var(--bg-preview);
}

.guide-content {
  max-width: 880px;
  margin: 0 auto;
  padding: 2rem 2.5rem 5rem;
  /* Lese-Zoom nur für den Inhalt — Sidebar/Chat bleiben unverändert */
  zoom: 1;
}

@media (max-width: 600px) {
  .guide-content {
    padding: 1.25rem 0.9rem 4rem;
  }
}

.guide-head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin-bottom: 1.5rem;

  h1 {
    font-size: 1.7rem;
  }
}

.guide-format {
  color: var(--text-faint);
  font-size: 0.9rem;
  font-weight: 600;
}

.thema-done {
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  background: color-mix(in srgb, #d4af37 20%, var(--panel));
  border: 1px solid #d4af37;
  color: #8a6d12;
}

.chapter {
  margin-bottom: 2.5rem;
}

.chapter-title {
  font-size: 1.25rem;
  margin-bottom: 0.9rem;
  padding-bottom: 0.4rem;
  border-bottom: 2px solid color-mix(in srgb, var(--ch-accent, var(--accent)) 35%, transparent);
  display: flex;
  align-items: center;
  gap: 10px;
}

.ch-num {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--ch-accent, var(--accent));
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 700;
}

.section-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1rem 1.25rem;
  margin-bottom: 0.75rem;
}

.baustein-done {
  float: right;
  margin-left: 0.5rem;
  padding: 0.12rem 0.6rem;
  font-size: 0.68em;
  font-weight: 600;
  line-height: 1.5;
  border-radius: 999px;
  background: var(--success-soft);
  border: 1px solid var(--success-border);
  color: var(--success);
  white-space: nowrap;
}

/* Absolvierte Bausteine: Karte kippt sichtbar auf Grün */
.guide-content .section-card.absolviert {
  border-color: var(--success-border);
  border-top: 3px solid var(--success);
  background: color-mix(in srgb, var(--success) 5%, var(--panel));
}

/* Verstandene Bausteine (10/10): Lila */
.baustein-done.verstanden {
  background: color-mix(in srgb, #8b5cf6 16%, var(--panel));
  border-color: #8b5cf6;
  color: #6d28d9;
}
.guide-content .section-card.verstanden {
  border-color: #8b5cf6;
  border-top: 3px solid #8b5cf6;
  background: color-mix(in srgb, #8b5cf6 7%, var(--panel));
}

/* Gemeisterte Bausteine (Meisterpfad 25/25): Gold */
.baustein-done.gemeistert {
  background: color-mix(in srgb, #d4af37 20%, var(--panel));
  border-color: #d4af37;
  color: #8a6d12;
}
.guide-content .section-card.gemeistert {
  border-color: #d4af37;
  border-top: 3px solid #d4af37;
  background: color-mix(in srgb, #d4af37 8%, var(--panel));
}

/* Guides: Karten tragen die Kapitel-Akzentfarbe */
.guide-content .section-card {
  border-top: 3px solid color-mix(in srgb, var(--ch-accent, var(--accent)) 65%, transparent);
  background: color-mix(in srgb, var(--ch-accent, var(--accent)) 3%, var(--panel));
}

.section-card {

  h3 {
    font-size: 1.02rem;
    margin-bottom: 0.5rem;
  }
}

/* Titel-Klick öffnet die Vollansicht. */
.section-card h3.baustein-klick { cursor: pointer; width: fit-content; }
.section-card h3.baustein-klick:hover { color: var(--accent); }

/* Platzhalter für noch nicht gerenderte Sections (Lazy-Render). Stabile Höhe,
   damit der IntersectionObserver die folgenden Karten gestaffelt erkennt. */
.section-body.skeleton { min-height: 160px; }

.empty-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--text-muted);
}

/* --- Markdown: Basis global (assets/markdown.css), hier nur Lese-Ansicht-Overrides --- */

/* Breite Lese-Ansicht: Code scrollt horizontal statt umzubrechen */
.markdown :deep(pre) {
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
}

/* „Beispiel"-Überschriften in Karten als dezentes Uppercase-Label */
.section-card .markdown :deep(h3) {
  font-size: 0.74em;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
  margin: 0.9em 0 0.35em;
}

/* Lesbarkeit: ~17px Fließtext, Zeilenhöhe 1.6, Textspalte max. ~70 Zeichen —
   Code-Blöcke dürfen die volle Kartenbreite nutzen */
.section-body {
  font-size: 1.0625rem;
  line-height: 1.6;
}

.section-card .markdown :deep(p),
.section-card .markdown :deep(ul),
.section-card .markdown :deep(ol) {
  max-width: 70ch;
}

/* --- Chat --- */
.chat-fab {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  width: 52px;
  height: 52px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 1.4rem;
  cursor: pointer;
  box-shadow: 0 2px 12px var(--shadow);
  z-index: 20;
}

.chat-fab:hover {
  background: var(--accent-hover);
}

/* Element-Sidebar (320px) offen → Chat links daneben anzeigen */
.chat-fab.shifted {
  right: calc(1.5rem + 320px);
}

.chat-panel.shifted {
  right: calc(1.5rem + 320px);
}

/* Mobil liegt die Elemente-Sidebar als Overlay über dem Chat — FAB/Panel ausblenden */
@media (max-width: 768px) {
  .chat-fab.shifted,
  .chat-panel.shifted {
    display: none;
  }
}

.chat-panel {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  width: 360px;
  height: 500px;
  max-height: calc(100dvh - 3rem);
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 4px 24px var(--shadow);
  z-index: 20;
  overflow: hidden;
}

.chat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.6rem 0.9rem;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
  font-size: 0.9rem;
}

.chat-close {
  border: none;
  background: none;
  color: var(--on-accent);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.chat-hint {
  color: var(--text-faint);
  font-size: 0.82rem;
  text-align: center;
  margin-top: 1rem;
}

.chat-msg {
  max-width: 85%;
  padding: 7px 11px;
  border-radius: 12px;
  font-size: 0.85rem;
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
}

.chat-msg.user {
  align-self: flex-end;
  background: var(--accent);
  color: var(--on-accent);
  border-bottom-right-radius: 3px;
}

.chat-msg.assistant {
  align-self: flex-start;
  background: var(--panel-soft);
  color: var(--text);
  border-bottom-left-radius: 3px;
}

.chat-msg.markdown {
  white-space: normal;
}

.chat-typing {
  color: var(--text-faint);
  font-style: italic;
}

.chat-input {
  display: flex;
  align-items: stretch;
  gap: 6px;
  padding: 0.6rem;
  border-top: 1px solid var(--border);
}

.chat-input textarea {
  flex: 1;
  resize: none;
  min-height: 72px;
  max-height: 200px;
  overflow-y: auto;
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-size: 0.85rem;
  font-family: inherit;
  line-height: 1.4;
  outline: none;
}

.chat-input textarea:focus {
  border-color: var(--accent);
}

.chat-input button {
  width: 38px;
  flex-shrink: 0;
  border: none;
  border-radius: 8px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 1rem;
  cursor: pointer;
}

.chat-input button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.chat-input button.cancel {
  background: var(--danger);
}
</style>
