<script setup>
import { ref, computed, watch, onMounted, nextTick } from 'vue'
import { fetchGuides, fetchTopics, createTopic as apiCreateTopic, deleteTopic as apiDeleteTopic, createGuide as apiCreate, deleteGuide, cancelGuide as apiCancel, fetchBausteineStatus, fetchActiveBausteine, createBausteine as apiCreateBausteine, cancelBausteine as apiCancelBausteine, deleteBausteine as apiDeleteBausteine, fetchProviders, fetchStats, fetchTopicFortschritt, fetchGuideLocks, fetchGuideSteps, fetchFolders, updateQuelle as apiUpdateQuelle } from './api.js'
import { usePolling } from './composables/usePolling.js'
import TopicSidebar from './components/TopicSidebar.vue'
import TopicDetail from './components/TopicDetail.vue'
import BausteineUebersicht from './components/BausteineUebersicht.vue'
import ElementsSidebar from './components/elements/ElementsSidebar.vue'
import ElementsOverview from './components/ElementsOverview.vue'

const guides = ref([])
const backendTopics = ref([])
const selectedTopic = ref(null)
const previewGuide = ref(null)
const sidebarPinned = ref(localStorage.getItem('sidebarPinned') !== 'false')
const sidebarSticky = ref(false)
const fokusOffen = ref(false)   // Baustein-Vollbild aktiv → Sidebar als Overlay über das Fokus-Overlay heben
const darkMode = ref(
  localStorage.getItem('darkMode') === null
    ? window.matchMedia('(prefers-color-scheme: dark)').matches
    : localStorage.getItem('darkMode') === 'true',
)
const EMPTY_BAUSTEINE = { ready: false, generating: false, progress: null, error: null, partial: false, steps: [] }
const bausteine = ref({ ...EMPTY_BAUSTEINE })
const activeBausteine = ref([])
const provider = ref(localStorage.getItem('provider') || 'claude')
const providers = ref([])
const folders = ref({ projekt: [], uni: [] })   // Ordner für die Quellen-Auswahl
const bausteineView = ref(false)                 // Bausteine-Übersicht im Hauptbereich
const ansichtModus = ref('kompakt')              // kompakt | erklärend — je Thema, Default kompakt
const stats = ref(null)
const fortschritt = ref({})
const locks = ref({})       // Sperr-Gründe pro Format (Backend = einzige Regel-Quelle)
const guideStepsDone = ref({}) // höchster fertiger Schritt-Index je Format (artefakt-basiert)
const uiError = ref(null)   // abgewiesene Aktionen (409/400) sichtbar machen
const elementsOpen = ref(false)      // rechte Sidebar
const elementsView = ref(false)      // Übersicht im Hauptbereich
const elementsVersion = ref(0)       // Erhöhung = Übersicht neu laden
const elementOpenId = ref(null)      // Element aus Übersicht in Sidebar öffnen
const elementOpenTick = ref(0)

async function loadStats() {
  try {
    stats.value = await fetchStats()
  } catch (e) {
    console.error('Fehler beim Laden der Statistik:', e)
  }
}

function setProvider(id) {
  provider.value = id
  localStorage.setItem('provider', id)
}

async function loadFolders() {
  try {
    const [projekt, uni] = await Promise.all([fetchFolders('projekt'), fetchFolders('uni')])
    folders.value = { projekt, uni }
  } catch (e) {
    console.error('Fehler beim Laden der Ordner:', e)
  }
}

async function loadProviders() {
  try {
    providers.value = await fetchProviders()
    const current = providers.value.find((p) => p.id === provider.value)
    if (current && !current.available) {
      const fallback = providers.value.find((p) => p.available)
      if (fallback) setProvider(fallback.id)
    }
  } catch (e) {
    console.error('Fehler beim Laden der Provider:', e)
  }
}

function applyTheme() {
  document.documentElement.classList.toggle('dark', darkMode.value)
}

function toggleDark() {
  darkMode.value = !darkMode.value
  localStorage.setItem('darkMode', darkMode.value ? 'true' : 'false')
  applyTheme()
}

applyTheme()

function toggleSidebarPin() {
  sidebarPinned.value = !sidebarPinned.value
  localStorage.setItem('sidebarPinned', sidebarPinned.value ? 'true' : 'false')
  if (sidebarPinned.value) sidebarSticky.value = false
}

function clickHoverZone() {
  sidebarSticky.value = !sidebarSticky.value
}

function onSidebarLeave() {
  if (!sidebarPinned.value) sidebarSticky.value = false
}

const topics = computed(() => backendTopics.value)

const doneByFormat = computed(() => {
  const map = {}
  for (const g of guides.value) {
    if (g.topic !== selectedTopic.value) continue
    if (g.status !== 'done') continue
    if (!map[g.format] || g.created_at > map[g.format].created_at) {
      map[g.format] = g
    }
  }
  return map
})

const latestByFormat = computed(() => {
  const map = {}
  for (const g of guides.value) {
    if (g.topic !== selectedTopic.value) continue
    if (!map[g.format] || g.created_at > map[g.format].created_at) {
      map[g.format] = g
    }
  }
  return map
})

const hasActiveGuides = computed(() =>
  guides.value.some((g) => g.status === 'queued' || g.status === 'generating'),
)

async function loadTopics() {
  try {
    backendTopics.value = await fetchTopics()
  } catch (e) {
    console.error('Fehler beim Laden der Themen:', e)
  }
}

// Weggeklickte Fehler bleiben weggeklickt — auch über Reloads (localStorage).
// Nicht weggeklickte Fehler bleiben sichtbar, bis der Nutzer sie schließt.
const dismissedErrors = ref(new Set(JSON.parse(localStorage.getItem('dismissedErrors') || '[]')))

function persistDismissed() {
  localStorage.setItem('dismissedErrors', JSON.stringify([...dismissedErrors.value]))
}

function handleDismissError(guideId) {
  dismissedErrors.value = new Set([...dismissedErrors.value, guideId])
  persistDismissed()
}

async function loadGuides() {
  try {
    guides.value = await fetchGuides()
    // IDs prunen, deren Guide nicht mehr als Fehler existiert
    const errorIds = new Set(guides.value.filter((g) => g.status === 'error').map((g) => g.id))
    if ([...dismissedErrors.value].some((id) => !errorIds.has(id))) {
      dismissedErrors.value = new Set([...dismissedErrors.value].filter((id) => errorIds.has(id)))
      persistDismissed()
    }
    loadStats()
  } catch (e) {
    console.error('Fehler beim Laden:', e)
  }
}

async function loadBausteine() {
  try {
    activeBausteine.value = await fetchActiveBausteine()
    if (selectedTopic.value) {
      bausteine.value = await fetchBausteineStatus(selectedTopic.value)
      fortschritt.value = await fetchTopicFortschritt(selectedTopic.value)
      locks.value = await fetchGuideLocks(selectedTopic.value)
      guideStepsDone.value = await fetchGuideSteps(selectedTopic.value)
    } else {
      bausteine.value = { ...EMPTY_BAUSTEINE }
      fortschritt.value = {}
      locks.value = {}
      guideStepsDone.value = {}
    }
    if (activeBausteine.value.length && !polling.running()) startPolling()
  } catch (e) {
    console.error('Fehler beim Laden der Bausteine:', e)
  }
}

const FORMAT_ORDER = ['Guide', 'FullGuide', 'Rest']

function autoPreview() {
  const map = doneByFormat.value
  for (const f of FORMAT_ORDER) {
    if (map[f]) {
      previewGuide.value = map[f]
      return
    }
  }
  previewGuide.value = null
}

function selectTopic(topic) {
  selectedTopic.value = topic
  previewGuide.value = null
  sidebarSticky.value = false
  elementsOpen.value = false
  elementsView.value = false
  bausteineView.value = false
  elementOpenId.value = null
  ansichtModus.value = localStorage.getItem('ansicht_' + topic) === 'erklärend' ? 'erklärend' : 'kompakt'
  localStorage.setItem('lastTopic', topic)
  loadBausteine()
  nextTick(autoPreview)
}

// Beim Reload dort landen, wo man vorher war (Thema + Format)
watch(previewGuide, (g) => {
  if (g) localStorage.setItem('lastFormat', g.format)
})

async function createTopic(topic) {
  await apiCreateTopic(topic)
  await loadTopics()
  selectedTopic.value = topic
  previewGuide.value = null
  loadBausteine()
}

async function handleCancelBausteine() {
  if (!selectedTopic.value) return
  await apiCancelBausteine(selectedTopic.value)
  await loadBausteine()
}

async function handleResetBausteine() {
  if (!selectedTopic.value) return
  await apiDeleteBausteine(selectedTopic.value)
  await loadBausteine()
}

async function handleBausteineClick({ instructions, abPhase = null }) {
  if (!selectedTopic.value) return
  uiError.value = null
  try {
    // Quelle ist hier schon festgeschrieben; nur abPhase steuert den Re-Run-Umfang.
    await apiCreateBausteine(selectedTopic.value, instructions, provider.value, undefined, undefined, abPhase)
  } catch (e) {
    uiError.value = e.message
    return
  }
  await loadBausteine()
  startPolling()
}

async function handleCreateThema({ topic, instructions, sourceType, sourceOrt }) {
  uiError.value = null
  try {
    await apiCreateBausteine(topic, instructions, provider.value, sourceType, sourceOrt)
  } catch (e) {
    uiError.value = e.message
    return
  }
  await loadTopics()
  selectTopic(topic)
  startPolling()
}

async function handleUpdateQuelle({ topic, type, ort, spec }) {
  uiError.value = null
  try {
    await apiUpdateQuelle(topic, { type, ort, spec })
  } catch (e) {
    uiError.value = e.message
    return
  }
  await loadBausteine()  // Schritt-Anzeige folgt ggf. der neuen Quelle (z.B. Link → „Quelle laden")
}

function setAnsicht(modus) {
  ansichtModus.value = modus
  if (selectedTopic.value) localStorage.setItem('ansicht_' + selectedTopic.value, modus)
}

function handleOpenBausteineView() {
  if (!selectedTopic.value) return
  bausteineView.value = true
  elementsView.value = false
  previewGuide.value = null
}

async function handleFormatClick({ format, instructions, abStep = null }) {
  if (!selectedTopic.value) return
  // Kein Duplikat-Start: läuft für Thema+Format schon eine Generierung, ignorieren
  const running = guides.value.some(
    (g) => g.topic === selectedTopic.value && g.format === format
      && (g.status === 'generating' || g.status === 'queued'),
  )
  if (running) return
  uiError.value = null
  try {
    await apiCreate(selectedTopic.value, format, instructions, provider.value, abStep)
  } catch (e) {
    uiError.value = e.message
    return
  }
  await loadGuides()
  startPolling()
}

function handlePreview(guide) {
  previewGuide.value = guide
  elementsView.value = false
  bausteineView.value = false
}

function handleOpenElements() {
  if (!selectedTopic.value) return
  elementsView.value = true
  bausteineView.value = false
  // Rechte Sidebar bleibt zu — sie öffnet erst beim Klick auf ein Element.
}

function handleOpenElementDetail(el) {
  elementOpenId.value = el.id
  elementOpenTick.value++
  elementsOpen.value = true
}

async function handleDeleteGuide(guideId, slots = false) {
  await deleteGuide(guideId, slots)
  if (previewGuide.value?.id === guideId) {
    previewGuide.value = null
  }
  await loadGuides()
}

const polling = usePolling(
  () => Promise.all([loadGuides(), loadBausteine(), loadTopics()]),
  () => hasActiveGuides.value || activeBausteine.value.length > 0,
)
const startPolling = polling.start

async function handleCancel(guideId) {
  await apiCancel(guideId)
  await loadGuides()
}

async function handleDeleteTopic(topic) {
  const topicGuides = guides.value.filter((g) => g.topic === topic)
  for (const g of topicGuides) {
    await deleteGuide(g.id)
  }
  await apiDeleteBausteine(topic)
  await apiDeleteTopic(topic)
  await loadTopics()
  if (selectedTopic.value === topic) {
    selectedTopic.value = null
    previewGuide.value = null
  }
  await loadGuides()
}

onMounted(async () => {
  await Promise.all([loadGuides(), loadTopics(), loadProviders(), loadFolders()])
  const savedTopic = localStorage.getItem('lastTopic')
  const savedFormat = localStorage.getItem('lastFormat')
  if (savedTopic && topics.value.includes(savedTopic)) {
    selectTopic(savedTopic)
    await nextTick()
    const g = doneByFormat.value[savedFormat]
    if (g) previewGuide.value = g
  } else if (!selectedTopic.value && topics.value.length) {
    selectTopic(topics.value[0])
  }
})
</script>

<template>
  <div class="layout" :class="{ 'sidebar-floating': !sidebarPinned, 'sidebar-open': sidebarSticky, 'sidebar-over-fokus': fokusOffen && sidebarSticky }">
    <div v-if="!sidebarPinned" class="hover-zone" @click="clickHoverZone"></div>
    <div v-if="(!sidebarPinned && sidebarSticky) || (fokusOffen && sidebarSticky)" class="sidebar-backdrop" @click="sidebarSticky = false"></div>
    <TopicSidebar
      :topics="topics"
      :selectedTopic="selectedTopic"
      :stats="stats"
      :fortschritt="fortschritt"
      :locks="locks"
      :guideStepsDone="guideStepsDone"
      :uiError="uiError"
      :doneByFormat="doneByFormat"
      :latestByFormat="latestByFormat"
      :allGuides="guides"
      :dismissedErrors="dismissedErrors"
      :bausteine="bausteine"
      :activeBausteine="activeBausteine"
      :pinned="sidebarPinned"
      :dark="darkMode"
      :provider="provider"
      :providers="providers"
      :folders="folders"
      :ansichtModus="ansichtModus"
      @setProvider="setProvider"
      @toggleDark="toggleDark"
      @setAnsicht="setAnsicht"
      @select="selectTopic"
      @create="createTopic"
      @createThema="handleCreateThema"
      @updateQuelle="handleUpdateQuelle"
      @openBausteineView="handleOpenBausteineView"
      @formatClick="handleFormatClick"
      @bausteineClick="handleBausteineClick"
      @cancelBausteine="handleCancelBausteine"
      @resetBausteine="handleResetBausteine"
      @deleteTopic="handleDeleteTopic"
      @cancelGuide="handleCancel"
      @deleteGuide="handleDeleteGuide"
      @dismissError="handleDismissError"
      @dismissUiError="uiError = null"
      @preview="handlePreview"
      @openElements="handleOpenElements"
      @togglePin="toggleSidebarPin"
      @sidebarLeave="onSidebarLeave"
    />
    <BausteineUebersicht
      v-if="selectedTopic && bausteineView"
      :topic="selectedTopic"
      @close="bausteineView = false"
    />
    <ElementsOverview
      v-else-if="selectedTopic && elementsView"
      :topic="selectedTopic"
      :version="elementsVersion"
      @open="handleOpenElementDetail"
    />
    <TopicDetail
      v-else-if="selectedTopic"
      :previewGuide="previewGuide"
      :dark="darkMode"
      :provider="provider"
      :elementsOpen="elementsOpen"
      :doneByFormat="doneByFormat"
      :themaAbgeschlossen="!!fortschritt.abgeschlossen"
      :ansichtModus="ansichtModus"
      @progressChanged="loadStats(); loadBausteine()"
      @setAnsicht="setAnsicht"
      @open-sidebar="sidebarSticky = true"
      @fokus-active="fokusOffen = $event"
    />
    <div v-else class="empty-main">
      <p>Thema in der Sidebar anlegen oder auswählen.</p>
    </div>
    <div
      v-if="elementsOpen && selectedTopic"
      class="elements-backdrop"
      @click="elementsOpen = false"
    ></div>
    <ElementsSidebar
      v-if="elementsOpen && selectedTopic"
      :topic="selectedTopic"
      :provider="provider"
      :openId="elementOpenId"
      :openTick="elementOpenTick"
      @close="elementsOpen = false"
      @changed="elementsVersion++"
    />
  </div>
</template>

<style>
:root {
  --bg: #f8f9fb;
  --bg-preview: #f0f1f4;
  --panel: #ffffff;
  --panel-soft: #f4f5f7;
  --border: #e2e5e9;
  --border-strong: #d8dde3;
  --text: #1a1a1a;
  --text-muted: #4b5563;
  --text-faint: #9ca3af;
  --accent: #6366f1;
  --accent-hover: #4f46e5;
  --accent-soft: #ede9fe;
  --accent-border: #a5b4fc;
  --on-accent: #ffffff;
  --success: #065f46;
  --success-soft: #d1fae5;
  --success-soft-hover: #a7f3d0;
  --success-border: #34d399;
  --warning: #92400e;
  --warning-soft: #fef3c7;
  --warning-border: #fbbf24;
  --danger: #991b1b;
  --code-bg: #1e2a3a;
  --code-fg: #e6e6e6;
  --shadow: rgba(0, 0, 0, 0.12);
}

html.dark {
  --bg: #15171c;
  --bg-preview: #0e1014;
  --panel: #1c1f26;
  --panel-soft: #23262e;
  --border: #2c3038;
  --border-strong: #3a3f4a;
  --text: #e6e8ee;
  --text-muted: #9aa3b2;
  --text-faint: #6b7280;
  --accent: #6366f1;
  --accent-hover: #818cf8;
  --accent-soft: #2a2350;
  --accent-border: #4f46e5;
  --on-accent: #ffffff;
  --success: #34d399;
  --success-soft: #0f3a2e;
  --success-soft-hover: #155e45;
  --success-border: #0f805e;
  --warning: #fbbf24;
  --warning-soft: #3a2c0a;
  --warning-border: #a06a12;
  --danger: #f87171;
  --shadow: rgba(0, 0, 0, 0.5);
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: -apple-system, 'Segoe UI', Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
}

input,
textarea {
  background: var(--panel);
  color: var(--text);
}

input::placeholder,
textarea::placeholder {
  color: var(--text-faint);
}

.layout {
  display: flex;
  height: 100dvh;
  overflow: hidden;
  position: relative;
}

.hover-zone {
  position: fixed;
  left: 0;
  top: 0;
  width: 50px;
  height: 100dvh;
  z-index: 5;
  cursor: pointer;
}

/* Unsichtbare Fläche hinter der offenen Floating-Sidebar.
   Tipp/Klick daneben schließt sie — ohne sie gibt es auf Touch keinen Ausweg. */
.sidebar-backdrop {
  position: fixed;
  inset: 0;
  z-index: 9;
  background: transparent;
}

.layout.sidebar-floating > .sidebar {
  position: fixed;
  left: 0;
  top: 0;
  height: 100dvh;
  transform: translateX(-100%);
  transition: transform 0.2s ease;
  z-index: 10;
  box-shadow: 0 0 16px var(--shadow);
}

.layout.sidebar-floating .hover-zone:hover ~ .sidebar,
.layout.sidebar-floating > .sidebar:hover,
.layout.sidebar-floating.sidebar-open > .sidebar {
  transform: translateX(0);
}

/* Baustein-Vollbild: Sidebar als Overlay ÜBER das Fokus-Overlay (z-index 40) heben.
   Greift für gepinnt UND floating. */
.layout.sidebar-over-fokus > .sidebar {
  position: fixed;
  left: 0;
  top: 0;
  height: 100dvh;
  transform: translateX(0);
  z-index: 50;
  box-shadow: 0 0 16px var(--shadow);
}
.layout.sidebar-over-fokus .sidebar-backdrop {
  z-index: 49;
}

.empty-main {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 1rem;
}

/* Nur sichtbar, wenn die Elemente-Sidebar mobil als Overlay liegt.
   Tipp daneben schließt sie. */
.elements-backdrop {
  display: none;
}

@media (max-width: 768px) {
  .elements-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 29;
    background: var(--shadow);
  }
}
</style>
