<template>
  <!-- ═══ 设置左导航(master-detail 左栏)═══
       阶段3 UX 重构:6 类导航(外观/AI模型/知识库/性能/安全/高级)+ emoji 图标。
       阶段4 UX 重构:顶部搜索框 → 匹配 label/desc → 高亮匹配类 + 选中首匹配类
       + emit scroll-target(父 Settings.vue 滚动到对应 setting-row)。
       activeCategory 受控(双向),v-model 由 Settings.vue 绑定 + appSettings 持久化。
       底部留导入导出占位(阶段6 接入)。 -->
  <nav class="settings-nav" :class="{ 'is-horizontal': horizontal }">
    <!-- 顶部搜索框(阶段4) -->
    <div v-if="!horizontal" class="nav-search">
      <span class="search-icon">🔍</span>
      <input
        v-model="searchKeyword"
        class="search-input"
        type="text"
        :placeholder="$t('settings.searchPlaceholder')"
      />
      <!-- 无匹配时显「无结果」提示(非空关键词且零命中) -->
      <span v-if="searchKeyword.trim() && matchedCategories.length === 0" class="search-no-result">
        {{ $t('settings.searchNoResult') }}
      </span>
    </div>

    <!-- 分类列表 -->
    <ul class="nav-list">
      <li
        v-for="cat in categories"
        :key="cat.value"
        class="nav-item"
        :class="{
          'nav-item--active': cat.value === modelValue,
          'nav-item--matched': isMatchedCategory(cat.value),
        }"
        @click="$emit('update:modelValue', cat.value)"
      >
        <span class="nav-icon">{{ cat.icon }}</span>
        <span class="nav-label">{{ $t(cat.labelKey) }}</span>
      </li>
    </ul>
  </nav>
</template>

<script setup lang="ts">
// ============================================================
// 左导航 — 静态分类定义 + 受控 activeCategory(v-model) + 搜索(阶段4)
// 分类按用户任务域划分(对齐 plan 设置项分类映射):
//   appearance  外观(theme/language/aiLanguage/showTokenUsage)
//   ai-model    AI 模型(provider 列表 + 负载均衡池 + 模型拉取)
//   knowledge   知识库(auto_extract/trigger_mode/embedding_*)
//   performance 性能(global/per-conv concurrency/max-iterations/max-retries)
//   security    安全(allowed_dirs)
//   advanced    高级(autoExecuteMode/logLevel + 连接)
// ============================================================

import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { SETTINGS_INDEX, type SettingsCategory, type SettingIndexEntry } from './searchIndex'

// re-export SettingsCategory 供 Settings.vue import(类型定义迁至 searchIndex.ts
// 避免组件 ↔ 索引文件循环导入)
export type { SettingsCategory }

interface NavCategory {
  value: SettingsCategory
  icon: string
  labelKey: string
}

const categories: NavCategory[] = [
  { value: 'appearance', icon: '🎨', labelKey: 'settings.navAppearance' },
  { value: 'ai-model', icon: '🤖', labelKey: 'settings.navAiModel' },
  { value: 'knowledge', icon: '📚', labelKey: 'settings.navKnowledge' },
  { value: 'performance', icon: '⚡', labelKey: 'settings.navPerformance' },
  { value: 'security', icon: '🔒', labelKey: 'settings.navSecurity' },
  { value: 'advanced', icon: '⚙️', labelKey: 'settings.navAdvanced' },
]

const props = defineProps<{
  /** 当前激活分类(v-model) */
  modelValue: SettingsCategory
  /** 窄屏横向 tab 模式(由父 Settings.vue 据 @media 控制) */
  horizontal?: boolean
}>()

const emit = defineEmits<{
  (e: 'update:modelValue', value: SettingsCategory): void
  /** 搜索命中首匹配项时上抛,父 Settings.vue 据此滚动右侧 setting-row */
  (e: 'scroll-target', key: string): void
}>()

// ============================================================
// 搜索(阶段4)
// ------------------------------------------------------------
// 索引项的 label/desc 用当前 locale 翻译成文本,小写 includes 模糊匹配。
// matchedCategories:命中项去重后的分类集合(左导航高亮)。
// 首匹配项变化时(输入/选中分类切换)→ emit scroll-target,父组件滚动定位。
// ============================================================
const { t } = useI18n()
const searchKeyword = ref('')

/** 判定单条索引项是否命中关键词(label + desc 文本,小写 includes) */
function matchEntry(entry: SettingIndexEntry, kw: string): boolean {
  const label = t(entry.labelKey)
  const desc = entry.descKey ? t(entry.descKey) : ''
  const hay = `${label} ${desc}`.toLowerCase()
  return hay.includes(kw)
}

/** 当前关键词命中的全部索引项(去 locale 文本匹配) */
const matchedEntries = computed<SettingIndexEntry[]>(() => {
  const kw = searchKeyword.value.trim().toLowerCase()
  if (!kw) return []
  return SETTINGS_INDEX.filter((e) => matchEntry(e, kw))
})

/** 命中项去重后的分类集合(左导航高亮) */
const matchedCategories = computed<SettingsCategory[]>(() => {
  const set = new Set<SettingsCategory>()
  for (const e of matchedEntries.value) set.add(e.category)
  return [...set]
})

/** 分类是否在匹配集中(模板高亮用) */
function isMatchedCategory(cat: SettingsCategory): boolean {
  if (!searchKeyword.value.trim()) return false
  return matchedCategories.value.includes(cat)
}

/** 关键词变化 → 选中首匹配类 + 上抛首匹配项 key(触发右侧滚动定位)。
 *  空关键词时不改 activeCategory(用户清空搜索应留在当前类),也不上抛。 */
watch(
  () => searchKeyword.value,
  (kw) => {
    const trimmed = kw.trim()
    if (!trimmed) return
    const first = matchedEntries.value[0]
    if (!first) return
    if (props.modelValue !== first.category) {
      emit('update:modelValue', first.category)
    }
    // 分类切换后需等右侧 Section 挂载,父组件 watch scroll-target 时自带 nextTick
    emit('scroll-target', first.key)
  },
)

defineExpose({
  /** 清空搜索框(父组件切分类时可调用,但当前保留搜索状态便于用户继续搜) */
  clearSearch() {
    searchKeyword.value = ''
  },
})
</script>

<style scoped>
/* 左导航容器:固定宽度 ~200px,纵向 flex(search 顶 / list 中 flex 占满 / footer 底) */
.settings-nav {
  display: flex;
  flex-direction: column;
  width: 200px;
  flex-shrink: 0;
  gap: 12px;
}

/* ===== 搜索框(阶段4) =====
   宽度跟随 nav(200px),相对定位承载无结果提示浮层。 */
.nav-search {
  position: relative;
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
.search-icon {
  position: absolute;
  left: 8px;
  font-size: 12px;
  pointer-events: none;
  opacity: 0.7;
}
.search-input {
  width: 100%;
  padding: 6px 10px 6px 26px;
  background: var(--df-bg);
  border: 0.5px solid var(--df-border);
  border-radius: var(--df-radius-sm);
  color: var(--df-text);
  font-size: 12px;
  outline: none;
  transition: border-color 0.15s;
}
.search-input:focus { border-color: var(--df-accent); }
.search-input::placeholder { color: var(--df-text-dim); }
/* 无结果提示:浮在搜索框右下,小字 + 红色提示 */
.search-no-result {
  position: absolute;
  top: 100%;
  right: 0;
  margin-top: 2px;
  font-size: 11px;
  color: var(--df-danger);
  white-space: nowrap;
}

.nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: var(--df-radius-sm);
  cursor: pointer;
  font-size: 13px;
  color: var(--df-text-secondary);
  transition: background 0.15s, color 0.15s;
  user-select: none;
}
.nav-item:hover {
  background: var(--df-bg-card);
  color: var(--df-text);
}
.nav-item--active {
  background: var(--df-accent-bg);
  color: var(--df-accent);
  font-weight: 500;
}
.nav-item--active:hover {
  background: var(--df-accent-bg);
  color: var(--df-accent);
}
/* 搜索命中类高亮(左侧色条 + 强调色),即便非当前选中也提示用户该类有匹配 */
.nav-item--matched {
  box-shadow: inset 2px 0 0 var(--df-accent);
  color: var(--df-text);
}
.nav-item--matched.nav-item--active {
  color: var(--df-accent);
}

.nav-icon { font-size: 16px; line-height: 1; }
.nav-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ===== 窄屏响应式:nav 折叠为横向 tab(@media 由父 Settings.vue 控制,
   此处仅保证横向模式下 nav-item 排列正确)。窄屏不显搜索框(横向空间不足,
   搜索改为后续可扩展) ===== */
.settings-nav.is-horizontal {
  width: 100%;
  flex-direction: column;
}
.settings-nav.is-horizontal .nav-list {
  flex-direction: row;
  overflow-x: auto;
  gap: 4px;
}
.settings-nav.is-horizontal .nav-item {
  flex-shrink: 0;
}
/* 横向模式下命中色条改下边框(左侧 inset 阴影在横向 tab 不显眼) */
.settings-nav.is-horizontal .nav-item--matched {
  box-shadow: none;
  border-bottom: 2px solid var(--df-accent);
}
</style>
