<template>
  <!-- 分离窗口：纯内容，不渲染主布局 -->
  <div v-if="isDetached" class="df-detached-root">
    <router-view v-slot="{ Component }">
      <component :is="Component" />
    </router-view>
  </div>
  <!-- 主窗口：完整布局 -->
  <div v-else class="df-shell">
    <!-- ═══ Sidebar ═══ -->
    <aside class="df-sidebar">
      <!-- Logo -->
      <div class="sidebar-brand">
        <div class="brand-mark">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none">
            <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" fill="url(#brand-grad)" />
            <defs><linearGradient id="brand-grad" x1="3" y1="2" x2="21" y2="22">
              <stop stop-color="#7b6ff0"/><stop offset="1" stop-color="#5eaff0"/>
            </linearGradient></defs>
          </svg>
        </div>
        <span class="brand-text">DevFlow</span>
        <span class="brand-version">v0.1</span>
      </div>

      <!-- Navigation -->
      <nav class="sidebar-nav">
        <div class="nav-group">
          <span class="nav-group-label">{{ $t('nav.workspace') }}</span>
          <router-link
            v-for="item in primaryNav" :key="item.path"
            :to="item.path"
            class="nav-link"
            active-class="nav-link--active"
          >
            <span class="nav-link-icon" v-html="item.svg"></span>
            <span class="nav-link-text">{{ $t(item.label) }}</span>
            <span v-if="item.badge" class="nav-link-badge">{{ item.badge }}</span>
          </router-link>
        </div>

        <div class="nav-divider"></div>

        <div class="nav-group">
          <span class="nav-group-label">{{ $t('nav.traceability') }}</span>
          <router-link
            v-for="item in secondaryNav" :key="item.path"
            :to="item.path"
            class="nav-link"
            active-class="nav-link--active"
          >
            <span class="nav-link-icon" v-html="item.svg"></span>
            <span class="nav-link-text">{{ $t(item.label) }}</span>
            <span v-if="item.path === '/knowledge' && candidateCount > 0" class="nav-link-badge">{{ candidateCount }}</span>
          </router-link>
        </div>
      </nav>

      <!-- Footer -->
      <div class="sidebar-footer">
        <div class="sidebar-status">
          <span class="status-dot"></span>
          <span class="status-text">{{ $t('nav.systemReady') }}</span>
        </div>
        <!-- AI 面板切换按钮 -->
        <button class="nav-link ai-toggle-btn" :class="{ 'ai-toggle-btn--active': aiStore.state.panelOpen }" @click="aiStore.togglePanel()">
          <span class="nav-link-icon">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a7 7 0 014 12.74V17a1 1 0 01-1 1H9a1 1 0 01-1-1v-2.26A7 7 0 0112 2z"/><line x1="9" y1="21" x2="15" y2="21"/></svg>
          </span>
          <span class="nav-link-text">{{ $t('nav.aiPanel') }}</span>
          <span class="ai-toggle-shortcut">⌘I</span>
        </button>
        <router-link to="/settings" class="nav-link" active-class="nav-link--active">
          <span class="nav-link-icon">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
          </span>
          <span class="nav-link-text">{{ $t('nav.settings') }}</span>
        </router-link>
      </div>
    </aside>

    <!-- ═══ Main (页面内容) ═══ -->
    <!-- 仅在 AI 面板可见且最大化时隐藏主内容；面板关闭时无论 maximized 状态如何都显示 -->
    <main class="df-main" v-show="!(aiStore.state.maximized && aiStore.state.panelOpen)">
      <router-view v-slot="{ Component }">
        <transition name="page" mode="out-in">
          <component :is="Component" />
        </transition>
      </router-view>
    </main>

    <!-- ═══ AI Chat Panel ═══ -->
    <transition name="ai-slide">
      <div v-if="aiStore.state.panelOpen && !aiStore.state.detached" class="df-ai-panel" :style="panelStyle">
        <div v-show="!aiStore.state.maximized" class="ai-resize-handle" :class="{ active: resizing }" @mousedown="startResize"></div>
        <AiChat />
      </div>
    </transition>
  </div>

  <!-- 全局错误 toast(根级,主窗口与分离窗口共用)-->
  <transition name="toast">
    <div v-if="errorMsg" class="df-toast">⚠ {{ errorMsg }}</div>
  </transition>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { useAiStore } from './stores/ai'
import { useKnowledgeStore } from './stores/knowledge'
import { useProjectStore } from './stores/project'
import { useAppSettingsStore } from './stores/appSettings'
import i18n from './i18n'
import AiChat from './components/AiChat.vue'
import { aiApi } from '@/api'

const route = useRoute()
const isDetached = computed(() => route.path === '/ai-detached')
const aiStore = useAiStore()
const appSettings = useAppSettingsStore()

// ── 全局错误 toast(消费 projectStore.error,所有 action 失败统一反馈)──
const projectStore = useProjectStore()
const errorMsg = ref('')
let errorTimer: ReturnType<typeof setTimeout> | null = null
watch(() => projectStore.error, (err) => {
  if (!err) return
  errorMsg.value = err
  projectStore.clearError() // 重置,允许连续同值错误再次触发
  if (errorTimer) clearTimeout(errorTimer)
  errorTimer = setTimeout(() => { errorMsg.value = '' }, 3500)
})

// 点击菜单自动退出最大化 → 切为分栏模式
watch(() => route.path, (path) => {
  if (!['/', '/ai-home', '/ai-detached'].includes(path) && aiStore.state.maximized) {
    aiStore.state.maximized = false
  }
})

// ── AI 面板拖拽调宽 ──
// useSetting 监听 appSettings 缓存:loadAll 异步填充后会自动刷新 ref
const panelWidth = appSettings.useSetting<number>('df-ai-width', 500)
const resizing = ref(false)

// maximized=全宽(flex:1填充) | sidebar=固定宽
const panelStyle = computed(() => {
  if (aiStore.state.maximized) return { flex: '1', minWidth: '0' }
  return {
    width: panelWidth.value + 'px',
    minWidth: panelWidth.value + 'px',
    maxWidth: panelWidth.value + 'px',
  }
})

function startResize(e: MouseEvent) {
  e.preventDefault()
  resizing.value = true
  const startX = e.clientX
  const startWidth = panelWidth.value

  document.body.style.cursor = 'col-resize'
  document.body.style.userSelect = 'none'

  function onMouseMove(ev: MouseEvent) {
    const delta = startX - ev.clientX // 向左拖 = 增宽
    const newWidth = Math.max(320, Math.min(800, startWidth + delta))
    panelWidth.value = newWidth
  }

  function onMouseUp() {
    resizing.value = false
    document.body.style.cursor = ''
    document.body.style.userSelect = ''
    void appSettings.set('df-ai-width', panelWidth.value)
    document.removeEventListener('mousemove', onMouseMove)
    document.removeEventListener('mouseup', onMouseUp)
  }

  document.addEventListener('mousemove', onMouseMove)
  document.addEventListener('mouseup', onMouseUp)
}

// 主题初始化
function initTheme() {
  const saved = appSettings.get<string>('df-theme', 'dark')
  if (saved === 'light') {
    document.documentElement.setAttribute('data-theme', 'light')
  } else if (saved === 'system') {
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
    if (!prefersDark) document.documentElement.setAttribute('data-theme', 'light')
  }
}

// Ctrl+I / Cmd+I 切换 AI 面板
function handleKeydown(e: KeyboardEvent) {
  if ((e.ctrlKey || e.metaKey) && e.key === 'i') {
    e.preventDefault()
    aiStore.togglePanel()
  }
}

onMounted(async () => {
  // 1. 先把 SQLite 全表 KV 填进 appSettings 缓存(后续 get/useSetting 才能读到真实值)
  await appSettings.loadAll()
  // 2. 一次性迁移:把旧 localStorage 的 11 个 key 导入 SQLite(仅首次跑,之后 localStorage 清净)
  await migrateLegacyLocalStorage()
  // 3. 缓存已就绪,初始化主题/locale
  initTheme()
  // F-260618-23: 根 onMounted 恢复 Agentic 轮次/重试到后端(AtomicUsize 纯内存,重启回默认)。
  // GeneralPanel.vue onMounted 仅 Settings 页挂载,用户直接进 AI Chat 不同步 → 后端默认 10 与前端显示持久值不一致。
  // clamp 与 GeneralPanel.syncAgentMaxIterations/syncAgentMaxRetries 逐字对齐(双保险,IPC 幂等值相同无害)
  // 包 try/catch 防 IPC 失败 reject onMounted(对齐 dataChangedListener 模式)
  try {
    const iter = appSettings.get<number>('df-ai-agent-max-iterations', 10)
    const iterClamped = Math.min(50, Math.max(1, iter || 10))
    const retry = appSettings.get<number>('df-ai-agent-max-retries', 3)
    const retryClamped = Math.min(10, Math.max(0, retry ?? 3))
    await aiApi.setAgentMaxIterations(iterClamped)
    await aiApi.setAgentMaxRetries(retryClamped)
  } catch (e) {
    console.error('恢复 Agentic 轮次/重试配置失败:', e)
  }
  const savedLang = appSettings.get<string | null>('df-language', null)
  if (savedLang) {
    i18n.global.locale.value = savedLang as 'zh-CN' | 'en'
  }
  window.addEventListener('keydown', handleKeydown)
  // 加载知识库候选数(供侧栏 knowledge badge 显示)
  knowledgeStore.loadCandidates()
  // AR-11:数据变更联动监听(AI 工具 create/update/delete 后端 emit df-data-changed → store listen 刷新对应列表,全局挂载因用户可在任意页)
  // CR-260615-21:包 try/catch 防 Tauri listen 失败 reject onMounted 致 AR-11 静默失效
  try {
    await projectStore.startDataChangedListener()
  } catch (e) {
    console.error('启动数据变更监听失败:', e)
  }
})

/// 一次性迁移:遍历遗留的 localStorage key,若有值则导入 appSettings(SQLite),
/// 然后删除 localStorage 中的旧 key。导入时按各 key 原存格式解析成 JS 值再交给 set
/// (set 内部会 JSON.stringify)。之后 localStorage 只剩 df-ai-gen / df-ai-text 流式快照。
const LEGACY_KEYS_RAW = new Set(['df-theme', 'df-language', 'df-ai-language']) // 裸字符串
const LEGACY_KEYS_JSON = new Set([ // 已 JSON.stringify(裸值或对象)
  'df-ai-width', 'df-ai-ui', 'df-show-token-usage',
  'df-llm-global-concurrency', 'df-llm-per-conv-concurrency',
  'df-ai-active-conv', 'df-connections',
])
function parseLegacy(key: string, raw: string): unknown {
  if (LEGACY_KEYS_RAW.has(key)) return raw // 裸字符串,如 'dark' / 'zh-CN'
  // JSON 类:有的存的是 JSON.stringify('zh-CN') → '"zh-CN"'(虽不在本集合,保险起见 parse)
  try {
    return JSON.parse(raw)
  } catch {
    return raw
  }
}
async function migrateLegacyLocalStorage(): Promise<void> {
  const allKeys = [...LEGACY_KEYS_RAW, ...LEGACY_KEYS_JSON]
  for (const key of allKeys) {
    const raw = localStorage.getItem(key)
    if (raw === null || raw === '') continue
    try {
      await appSettings.set(key, parseLegacy(key, raw))
    } catch (e) {
      console.error(`[migrate] 导入 ${key} 失败:`, e)
    }
    localStorage.removeItem(key)
  }
}

// 知识库候选数量(侧栏 badge)— 解包 computed 取值保持响应式
const knowledgeStore = useKnowledgeStore()
const candidateCount = computed(() => knowledgeStore.candidateCount)

onUnmounted(() => {
  window.removeEventListener('keydown', handleKeydown)
  projectStore.stopDataChangedListener?.()
})

const primaryNav = [
  { path: '/dashboard', label: 'nav.overview', badge: null,
    svg: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>' },
  { path: '/ideas', label: 'nav.ideas', badge: null,
    svg: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6M10 22h4M12 2a7 7 0 014 12.74V17a1 1 0 01-1 1h-6a1 1 0 01-1-1v-2.26A7 7 0 0112 2z"/></svg>' },
  { path: '/projects', label: 'nav.projects', badge: null,
    svg: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/></svg>' },
  { path: '/tasks', label: 'nav.tasks', badge: null,
    svg: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>' },
]

const secondaryNav = [
  { path: '/knowledge', label: 'nav.knowledge',
    svg: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 016.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z"/></svg>' },
  { path: '/audit', label: 'nav.audit',
    svg: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"/></svg>' },
]
</script>

<style scoped>
.df-detached-root { width: 100vw; height: 100vh; overflow: hidden; background: var(--df-bg); }
.df-shell { display: flex; height: 100vh; width: 100vw; overflow: hidden; }

/* ═══ Sidebar ═══ */
.df-sidebar {
  width: 192px;
  min-width: 192px;
  background: var(--df-sidebar-bg);
  border-right: 0.5px solid var(--df-border);
  display: flex;
  flex-direction: column;
  position: relative;
  user-select: none;
}

/* — Logo — */
.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 16px 12px;
  border-bottom: 0.5px solid var(--df-border);
}
.brand-mark {
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  background: var(--df-accent-bg);
  border: 0.5px solid var(--df-border);
}
.brand-text {
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.3px;
  color: var(--df-text);
}
.brand-version {
  font-family: var(--df-font-mono);
  font-size: 10px;
  color: var(--df-text-dim);
  background: rgba(255,255,255,0.04);
  padding: 1px 6px;
  border-radius: var(--df-radius-xs);
  margin-left: auto;
}

/* — Nav — */
.sidebar-nav { flex: 1; overflow-y: auto; padding: 8px 8px; }

.nav-group { margin-bottom: 2px; }
.nav-group-label {
  display: block;
  padding: 8px 10px 4px;
  font-size: 9px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--df-text-dim);
}

.nav-divider {
  height: 0.5px;
  background: var(--df-border);
  margin: 6px 12px;
}

.nav-link {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  margin: 1px 0;
  border-radius: var(--df-radius-sm);
  color: var(--df-text-secondary);
  text-decoration: none;
  font-size: 12px;
  font-weight: 450;
  transition: all 0.18s var(--df-ease);
  position: relative;
}

.nav-link:hover {
  background: var(--df-sidebar-hover);
  color: var(--df-text);
}
.nav-link--active {
  background: var(--df-sidebar-active);
  color: var(--df-accent);
  font-weight: 500;
}
.nav-link--active::before {
  content: '';
  position: absolute;
  left: -10px;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 16px;
  background: var(--df-accent);
  border-radius: 0 3px 3px 0;
}

.nav-link-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  opacity: 0.7;
  flex-shrink: 0;
}
.nav-link--active .nav-link-icon { opacity: 1; }

.nav-link-text { flex: 1; }

.nav-link-badge {
  font-family: var(--df-font-mono);
  font-size: 10px;
  font-weight: 500;
  padding: 1px 6px;
  border-radius: var(--df-radius);
  min-width: 18px;
  text-align: center;
  background: var(--df-accent);
  color: #fff;
  line-height: 1.4;
}

/* — AI Toggle Button — */
.ai-toggle-btn {
  cursor: pointer;
  border: none;
  background: transparent;
  width: 100%;
  font: inherit;
  text-align: left;
  appearance: none;
  -webkit-appearance: none;
}
.ai-toggle-btn--active {
  color: var(--df-accent);
  background: var(--df-sidebar-active);
}
.ai-toggle-shortcut {
  font-family: var(--df-font-mono);
  font-size: 9px;
  color: var(--df-text-dim);
  background: rgba(255,255,255,0.04);
  padding: 1px 5px;
  border-radius: var(--df-radius-sm);
}

/* — Footer — */
.sidebar-footer {
  padding: 6px 8px;
  border-top: 0.5px solid var(--df-border);
}

.sidebar-status {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  margin-bottom: 1px;
}
.status-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--df-success);
  animation: pulseGlow 3s ease-in-out infinite;
}
.status-text {
  font-family: var(--df-font-mono);
  font-size: 10px;
  color: var(--df-text-dim);
  letter-spacing: 0.3px;
}

/* — Main — */
.df-main {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  background: var(--df-bg);
  min-width: 0;
}

/* — AI Panel (全宽) — */
.df-ai-panel {
  position: relative;
  height: 100vh;
  overflow: hidden;
}

/* — AI Resize Handle — */
.ai-resize-handle {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  cursor: col-resize;
  z-index: 10;
  transition: background 0.15s;
}
.ai-resize-handle:hover,
.ai-resize-handle.active {
  background: var(--df-accent);
  opacity: 0.4;
}

/* — AI Slide Transition — */
.ai-slide-enter-active { transition: opacity 0.2s var(--df-ease); }
.ai-slide-leave-active { transition: opacity 0.15s var(--df-ease); }
.ai-slide-enter-from, .ai-slide-leave-to {
  opacity: 0;
}

/* ═══ Responsive ═══ */
@media (max-width: 768px) {
  .df-sidebar { width: 56px; min-width: 56px; }
  .brand-text, .brand-version, .nav-group-label, .nav-link-text,
  .nav-link-badge, .sidebar-status, .status-text, .ai-toggle-shortcut { display: none; }
  .sidebar-brand { padding: 12px 10px 10px; justify-content: center; }
  .brand-mark { width: 32px; height: 32px; }
  .nav-link { justify-content: center; padding: 8px 0; }
  .nav-divider { margin: 4px 8px; }
  .nav-link--active::before { display: none; }
  .df-ai-panel { width: 100vw; min-width: 100vw; max-width: 100vw; position: absolute; right: 0; top: 0; z-index: 100; }
}

/* — Page Transition — */
.page-enter-active { transition: opacity 0.2s var(--df-ease), transform 0.2s var(--df-ease); }
.page-leave-active { transition: opacity 0.12s var(--df-ease), transform 0.12s var(--df-ease); }
.page-enter-from { opacity: 0; transform: translateY(6px); }
.page-leave-to { opacity: 0; transform: translateY(-4px); }

/* ═══ 全局错误 toast ═══ */
.df-toast {
  position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
  background: var(--df-danger); color: #fff;
  padding: 10px 18px; border-radius: var(--df-radius-sm);
  font-size: 13px; z-index: 9999; max-width: 90vw;
  box-shadow: 0 4px 16px rgba(0,0,0,0.3);
}
.toast-enter-active, .toast-leave-active { transition: all 0.25s var(--df-ease); }
.toast-enter-from, .toast-leave-to { opacity: 0; transform: translate(-50%, 10px); }
</style>
