<script setup>
import { ref, computed, watch, onMounted, onUnmounted, 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, fetchProjects, deleteProject as apiDeleteProject, fetchProviders, fetchStats } from './api.js'
import TopicSidebar from './components/TopicSidebar.vue'
import TopicDetail from './components/TopicDetail.vue'

const guides = ref([])
const projects = ref([])
const backendTopics = ref([])
const selectedTopic = ref(null)
const previewGuide = ref(null)
const sidebarPinned = ref(localStorage.getItem('sidebarPinned') !== 'false')
const sidebarSticky = ref(false)
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 stats = ref(null)

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 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)
  }
}
let pollTimer = null

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 projectNames = computed(() => projects.value.map((p) => p.name))

const topics = computed(() => {
  const isProject = new Set(projectNames.value)
  return backendTopics.value.filter((t) => !isProject.has(t))
})

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)
  }
}

// Fehlermeldungen verhalten sich wie Flash-Messages: × blendet aus,
// beim Reload sind Alt-Fehler von vornherein ausgeblendet.
const dismissedErrors = ref(new Set())
let errorsInitialized = false

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

async function loadGuides() {
  try {
    guides.value = await fetchGuides()
    if (!errorsInitialized) {
      errorsInitialized = true
      dismissedErrors.value = new Set(guides.value.filter((g) => g.status === 'error').map((g) => g.id))
    }
    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)
    } else {
      bausteine.value = { ...EMPTY_BAUSTEINE }
    }
    if (activeBausteine.value.length && !pollTimer) startPolling()
  } catch (e) {
    console.error('Fehler beim Laden der Bausteine:', e)
  }
}

async function loadProjects() {
  try {
    projects.value = await fetchProjects()
  } catch (e) {
    console.error('Fehler beim Laden der Projekte:', e)
  }
}

const FORMAT_ORDER = ['OnePager', 'MiniGuide', 'Guide', 'FullGuide']

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
  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 }) {
  if (!selectedTopic.value) return
  await apiCreateBausteine(selectedTopic.value, instructions, provider.value)
  await loadBausteine()
  startPolling()
}

async function handleFormatClick({ format, instructions }) {
  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
  await apiCreate(selectedTopic.value, format, instructions, provider.value)
  await loadGuides()
  startPolling()
}

async function handleDeleteProject(name) {
  await apiDeleteProject(name)
  if (selectedTopic.value === name) {
    selectedTopic.value = null
    previewGuide.value = null
  }
  await loadProjects()
}

function handlePreview(guide) {
  previewGuide.value = guide
}

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

function startPolling() {
  stopPolling()
  pollTimer = setInterval(async () => {
    await Promise.all([loadGuides(), loadBausteine(), loadTopics()])
    if (!hasActiveGuides.value && !activeBausteine.value.length) stopPolling()
  }, 3000)
}

function stopPolling() {
  if (pollTimer) {
    clearInterval(pollTimer)
    pollTimer = null
  }
}

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()
}

function onVisibility() {
  if (document.hidden) {
    stopPolling()
  } else {
    loadGuides()
    loadBausteine()
    if (hasActiveGuides.value || activeBausteine.value.length) startPolling()
  }
}

onMounted(async () => {
  await Promise.all([loadGuides(), loadTopics(), loadProjects(), loadProviders()])
  const savedTopic = localStorage.getItem('lastTopic')
  const savedFormat = localStorage.getItem('lastFormat')
  if (savedTopic && [...topics.value, ...projectNames.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])
  }
  document.addEventListener('visibilitychange', onVisibility)
})

onUnmounted(() => {
  stopPolling()
  document.removeEventListener('visibilitychange', onVisibility)
})
</script>

<template>
  <div class="layout" :class="{ 'sidebar-floating': !sidebarPinned, 'sidebar-open': sidebarSticky }">
    <div v-if="!sidebarPinned" class="hover-zone" @click="clickHoverZone"></div>
    <div v-if="!sidebarPinned && sidebarSticky" class="sidebar-backdrop" @click="sidebarSticky = false"></div>
    <TopicSidebar
      :topics="topics"
      :projects="projectNames"
      :selectedTopic="selectedTopic"
      :stats="stats"
      :doneByFormat="doneByFormat"
      :latestByFormat="latestByFormat"
      :allGuides="guides"
      :dismissedErrors="dismissedErrors"
      :bausteine="bausteine"
      :activeBausteine="activeBausteine"
      :pinned="sidebarPinned"
      :dark="darkMode"
      :provider="provider"
      :providers="providers"
      @setProvider="setProvider"
      @toggleDark="toggleDark"
      @select="selectTopic"
      @create="createTopic"
      @formatClick="handleFormatClick"
      @bausteineClick="handleBausteineClick"
      @cancelBausteine="handleCancelBausteine"
      @resetBausteine="handleResetBausteine"
      @deleteTopic="handleDeleteTopic"
      @deleteProject="handleDeleteProject"
      @cancelGuide="handleCancel"
      @deleteGuide="handleDeleteGuide"
      @dismissError="handleDismissError"
      @preview="handlePreview"
      @togglePin="toggleSidebarPin"
      @sidebarLeave="onSidebarLeave"
    />
    <TopicDetail
      v-if="selectedTopic"
      :previewGuide="previewGuide"
      :dark="darkMode"
      :provider="provider"
      @progressChanged="loadStats"
    />
    <div v-else class="empty-main">
      <p>Thema in der Sidebar anlegen oder auswählen.</p>
    </div>
  </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: 100vh;
  overflow: hidden;
  position: relative;
}

.hover-zone {
  position: fixed;
  left: 0;
  top: 0;
  width: 50px;
  height: 100vh;
  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: 100vh;
  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);
}

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