<template>
  <div class="settings">
    <Transition name="toast">
      <div v-if="toast.visible" class="toast" :class="'toast-' + toast.type">{{ toast.msg }}</div>
    </Transition>
    <ConfirmDialog :visible="confirmState.visible" :msg="confirmState.msg" :danger-label="$t('common.delete')" @result="answerConfirm" />
    <!-- 页面头部 -->
    <header class="page-header">
      <h1>{{ $t('settings.title') }}</h1>
    </header>

    <!-- ═══ master-detail 布局(阶段3 UX 重构)═══
         左 SettingsNav(~200px 固定):6 类导航,activeCategory 受控 + appSettings 持久化
         右 content(scroll):按 activeCategory 渲染对应 Section/Panel -->
    <div class="settings-body">
      <SettingsNav
        v-model="activeCategory"
        :horizontal="isNarrowScreen"
        @scroll-target="scrollToSettingItem"
      />
      <!-- 导入/导出已移除:实测价值低且占用 tab 底部空间,如需跨设备同步建议直接复制 SQLite 文件 -->

      <div ref="contentRef" class="settings-content">
        <!-- 外观:theme/language/aiLanguage/showTokenUsage -->
        <AppearanceSection v-if="activeCategory === 'appearance'" />

        <!-- AI 模型:provider 列表 + 负载均衡池 + 模型拉取 -->
        <ProviderPanel
          v-else-if="activeCategory === 'ai-model'"
          ref="providerPanelRef"
          :confirm-dialog="confirmDialog"
          @toast="showToast"
          @providers-changed="onProvidersChanged"
        />

        <!-- 知识库:auto_extract/trigger_mode/embedding_* -->
        <KnowledgePanel
          v-else-if="activeCategory === 'knowledge'"
          :ai-providers="aiProviders"
        />

        <!-- 性能:global/per-conv concurrency/max-iterations/max-retries -->
        <PerformanceSection v-else-if="activeCategory === 'performance'" />

        <!-- 安全:allowed_dirs -->
        <AllowedDirsPanel
          v-else-if="activeCategory === 'security'"
          @toast="showToast"
        />

        <!-- 高级:autoExecuteMode/logLevel + 连接(暂留待定) -->
        <template v-else-if="activeCategory === 'advanced'">
          <AdvancedSection />
          <ConnectionPanel
            ref="connectionPanelRef"
            :confirm-dialog="confirmDialog"
            @toast="showToast"
          />
        </template>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, shallowRef, onMounted, onUnmounted, nextTick } from 'vue'
import { useConfirm } from '@/composables/useConfirm'
import { useToast } from '@/composables/useToast'
import ConfirmDialog from '@/components/ConfirmDialog.vue'
import SettingsNav, { type SettingsCategory } from '@/components/settings/SettingsNav.vue'
import ProviderPanel from '@/components/settings/ProviderPanel.vue'
import ConnectionPanel from '@/components/settings/ConnectionPanel.vue'
import AppearanceSection from '@/components/settings/AppearanceSection.vue'
import PerformanceSection from '@/components/settings/PerformanceSection.vue'
import AdvancedSection from '@/components/settings/AdvancedSection.vue'
import KnowledgePanel from '@/components/settings/KnowledgePanel.vue'
import AllowedDirsPanel from '@/components/settings/AllowedDirsPanel.vue'
// SettingsImportExport 已移除(价值低,占用 UI 空间;跨设备同步建议直接复制 SQLite)
import { useAppSettingsStore } from '@/stores/appSettings'
import { aiApi } from '@/api'
import type { AiProviderConfig } from '@/api/types'
import { SETTINGS_INDEX } from '@/components/settings/searchIndex'
import { t as ti18n } from '@/i18n/i18n-helpers'

// ============================================================
// Settings 壳 — toast/confirm 共享 UI + master-detail 布局组合各 Section/Panel
// (阶段3 UX 重构:左导航 + 右按 activeCategory 渲染,各功能域 state/methods 已拆到子组件)
// ============================================================

// 顶部轻量提示(useToast composable,消除 4 处重复;默认 3000ms)
const { toast, showToast } = useToast()

// 确认弹层状态机抽至 composables/useConfirm(原 4 视图重复:Projects/ProjectDetail/Ideas/Settings)
// 模板 confirmState.visible/msg 在 <script setup> 中自动解包 ref,沿用既有内联确认弹层
const { confirmState, confirmDialog, answerConfirm } = useConfirm()

// 子面板 ref(挂载时触发各自 load,卸载时清子面板 timer)
const providerPanelRef = shallowRef<InstanceType<typeof ProviderPanel> | null>(null)
const connectionPanelRef = shallowRef<InstanceType<typeof ConnectionPanel> | null>(null)

// ============================================================
// master-detail:activeCategory 持久化(appSettings,默认 appearance)
// 用 computed 包 useSetting,双向绑 SettingsNav v-model
// ============================================================
const appSettings = useAppSettingsStore()
const activeCategory = computed<SettingsCategory>({
  get: () => appSettings.get<SettingsCategory>('df-settings-active-category', 'appearance'),
  set: (v) => { void appSettings.set('df-settings-active-category', v) },
})

// 窄屏响应式:监听窗口宽度,nav 折叠为横向 tab(避免依赖仅 CSS 的 :has,
// 并让 SettingsNav 按横向模式渲染 list)
const isNarrowScreen = ref(false)
function syncNarrow() {
  isNarrowScreen.value = window.innerWidth <= 768
}
let _resizeTimer: ReturnType<typeof setTimeout> | null = null
function onResize() {
  if (_resizeTimer) clearTimeout(_resizeTimer)
  _resizeTimer = setTimeout(syncNarrow, 100)
}

// ============================================================
// 设置搜索滚动定位(阶段4)
// ------------------------------------------------------------
// SettingsNav 搜索命中首匹配项时上抛 scroll-target(key)。
// 此处据 key 查 SETTINGS_INDEX 取 labelKey → t() 得当前 locale 文本,
// 在右侧内容 DOM 中找匹配的 .setting-label(表单型 Section)或
// .panel-header h2(列表型面板如 ProviderPanel),scrollIntoView 平滑定位。
//
// 匹配策略(2026-08-02 加固):
// 旧实现用 textContent.trim() === labelText 严格相等,SettingRow 的 .setting-label
// 常带 next-round-badge 子 span(「下次对话生效」) → textContent 拼接出
// 「主题 下次对话生效」≠「主题」,严格相等失效,搜索定位静默无命中。
// 现改读 .setting-label 的"首子文本节点"(纯 label,排除 badge/desc 等子元素),
// 与 labelText trim 后 includes 匹配(双向 includes 兜底首尾空白/标点)。
// h2 同理改 includes 提升容错。理想方案是 .setting-label 加 data-setting-key,
// 但那需改 SettingRow.vue + 6 个 Section(越"仅改指定文件"范围),故此处加固文本匹配。
// ============================================================
const contentRef = ref<HTMLElement | null>(null)

/** 取元素"首文本节点"内容(跳过子元素,排除 SettingRow 的 next-round-badge 拼接干扰) */
function firstTextNodeText(el: HTMLElement | null): string {
  if (!el) return ''
  // 优先 childNodes 中的首个 text node;退而 textContent(无子元素场景)
  for (const node of Array.from(el.childNodes)) {
    if (node.nodeType === Node.TEXT_NODE) {
      return node.textContent || ''
    }
  }
  return el.textContent || ''
}

/** 双向 includes 匹配(容错首尾空白/标点差异;空串不命中) */
function fuzzyMatch(a: string, b: string): boolean {
  const sa = a.trim()
  const sb = b.trim()
  if (!sa || !sb) return false
  return sa.includes(sb) || sb.includes(sa)
}

function scrollToSettingItem(itemKey: string) {
  const entry = SETTINGS_INDEX.find((e) => e.key === itemKey)
  if (!entry) return
  // 用 i18n-helpers 的 t(规避 vue-i18n 动态 key 的 TS2589,见 i18n-helpers.ts 注释)
  const labelText = ti18n(entry.labelKey)
  // 等右侧对应 Section v-if 切换后挂载(nextTick);再用二次 rAF 确保布局就绪
  void nextTick(() => {
    requestAnimationFrame(() => {
      const root = contentRef.value
      if (!root) return
      // 优先 .setting-label 文本(表单型);退而求 .panel-header h2(面板标题)
      const labels = root.querySelectorAll<HTMLElement>('.setting-label')
      let target: HTMLElement | null = null
      for (const el of labels) {
        if (fuzzyMatch(firstTextNodeText(el), labelText)) {
          target = el.closest<HTMLElement>('.setting-row') ?? el
          break
        }
      }
      if (!target) {
        const headers = root.querySelectorAll<HTMLElement>('.panel-header h2')
        for (const el of headers) {
          if (fuzzyMatch(firstTextNodeText(el), labelText)) {
            target = el
            break
          }
        }
      }
      if (target) {
        target.scrollIntoView({ behavior: 'smooth', block: 'center' })
      }
    })
  })
}

// ============================================================
// KnowledgePanel embedding provider 下拉依赖 provider 列表。
// 阶段3 改 master-detail 后 ProviderPanel 仅在 ai-model 类挂载,
// knowledge 类切到时 ProviderPanel 可能未挂载 → 此处独立拉一次 providers
// 保证 KnowledgePanel 始终拿到 embedding provider 候选(与 ProviderPanel 自身
// onMounted loadProviders 互补,后者仅刷新 UI 列表 + aiStore)。
// ============================================================
const aiProviders = ref<AiProviderConfig[]>([])

async function loadAiProviders() {
  try {
    aiProviders.value = await aiApi.listProviders()
  } catch (e) {
    console.error('加载 AI 提供商失败:', e)
  }
}

function onProvidersChanged() {
  // ProviderPanel 自己 reload 后上抛,优先取其内存副本(最新),失败回退此处独立拉
  aiProviders.value = providerPanelRef.value?.aiProviders ?? aiProviders.value
}

onMounted(() => {
  syncNarrow()
  window.addEventListener('resize', onResize)
  // provider 列表预拉(knowledge 类依赖);connection 列表 advanced 类挂载时自载
  void loadAiProviders()
})

// 组件卸载清 resize timer;toast 清理由 useToast 自动管理;各子面板自管自身 debounce timer
onUnmounted(() => {
  if (_resizeTimer) clearTimeout(_resizeTimer)
  window.removeEventListener('resize', onResize)
  providerPanelRef.value?.clearPoolTimer()
})
</script>

<style scoped>
.settings { padding: 16px 20px 20px; }

/* ===== 顶部 Toast 提示 ===== */
.toast {
  position: fixed;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  padding: 8px 16px;
  border-radius: var(--df-radius-sm);
  font-size: 13px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.3);
}
.toast-error { background: var(--df-danger-bg); color: var(--df-danger); border: 0.5px solid var(--df-danger); }
.toast-warning { background: var(--df-warning-bg); color: var(--df-warning); border: 0.5px solid var(--df-warning); }
.toast-info { background: var(--df-accent-bg); color: var(--df-accent); border: 0.5px solid var(--df-accent); }
/* P0-2: 保存成功明确反馈(绿色),与 info(中性强调)区分 */
.toast-success { background: var(--df-success-bg); color: var(--df-success); border: 0.5px solid var(--df-success); }
.toast-enter-active, .toast-leave-active { transition: opacity 0.2s, transform 0.2s; }
.toast-enter-from, .toast-leave-to { opacity: 0; transform: translate(-50%, -8px); }

/* 确认弹层已替换为 ConfirmDialog 组件(不再需要自建 CSS) */

/* page-header / btn 等已提取到 global.css 全局 */

/* ===== master-detail 布局(阶段3 UX 重构) =====
   左 SettingsNav(~200px 固定)+ 右 content(flex 占满 + 纵向滚动)。
   nav 宽度/折叠由 SettingsNav scoped 控制;此处仅布局骨架。 */
.settings-body {
  display: flex;
  gap: 20px;
  align-items: flex-start;
}
.settings-content {
  flex: 1;
  min-width: 0;  /* 允许收缩,防子面板溢出撑破 flex */
  display: flex;
  flex-direction: column;
  gap: var(--df-gap-grid);
}

/* .btn / .btn-primary / .btn-ghost / .btn-sm 已迁移到 settings.css 全局,
   本壳的 toast/confirm 内联按钮沿用全局基础样式;此处仅保留全局未定义的 .btn-danger。 */

/* ===== 窄屏响应式:nav 折叠为顶部横向 tab(由 SettingsNav is-horizontal 渲染) ===== */
@media (max-width: 768px) {
  .settings-body {
    flex-direction: column;
    gap: 12px;
  }
}
</style>
