<template>
  <!-- ═══ F-260619-03 Phase A: AI 工具授权目录 ═══ -->
  <section class="panel">
    <div class="panel-header">
      <h2>{{ $t('settings.panelAllowedDirs') }}</h2>
    </div>
    <div class="allowed-dirs">
      <!-- 说明 -->
      <p class="allowed-dirs-desc">{{ $t('settings.descAllowedDirs') }}</p>

      <!-- 列表 -->
      <div v-if="loading" class="empty-hint">{{ $t('settings.allowedDirsLoading') }}</div>
      <template v-else>
        <div v-for="(dir, idx) in dirs" :key="dir + idx" class="dir-row">
          <span class="dir-path" :title="dir">{{ dir }}</span>
          <button class="btn btn-ghost btn-sm" :disabled="saving" @click="removeDir(idx)">
            {{ $t('common.delete') }}
          </button>
        </div>
        <div v-if="dirs.length === 0" class="empty-hint">{{ $t('settings.allowedDirsEmpty') }}</div>
      </template>

      <!-- 新增输入 -->
      <div class="add-row">
        <input
          v-model="newDir"
          class="setting-input dir-input"
          :placeholder="$t('settings.allowedDirsInputPlaceholder')"
          @keyup.enter="onAddClick"
        />
        <button class="btn btn-primary btn-sm" :disabled="saving" @click="onAddClick">
          {{ newDir.trim() ? $t('settings.allowedDirsAdd') : $t('settings.allowedDirsSelectDir') }}
        </button>
      </div>

      <!-- 操作 -->
      <div class="actions">
        <button class="btn btn-ghost btn-sm" :disabled="loading" @click="reload">
          {{ $t('settings.allowedDirsReload') }}
        </button>
      </div>
    </div>
  </section>

  <!-- ═══ 命令执行安全:ScriptNode 白/黑名单(前端仅 UI + 持久化,后端当前从环境变量读) ═══ -->
  <section class="panel">
    <div class="panel-header">
      <h2>{{ $t('settings.panelScriptSafety') }}</h2>
    </div>
    <div class="script-safety">
      <p class="script-safety-desc">{{ $t('settings.descScriptSafety') }}</p>

      <SettingRow :label="$t('settings.labelScriptWhitelist')" :desc="$t('settings.descScriptWhitelist')">
        <input
          v-model="scriptWhitelist"
          class="setting-input script-safety-input"
          :placeholder="$t('settings.placeholderScriptWhitelist')"
          @blur="onScriptWhitelistCommit"
          @keyup.enter="onScriptWhitelistCommit"
        />
      </SettingRow>

      <SettingRow :label="$t('settings.labelScriptBlacklist')" :desc="$t('settings.descScriptBlacklist')">
        <input
          v-model="scriptBlacklist"
          class="setting-input script-safety-input"
          :placeholder="$t('settings.placeholderScriptBlacklist')"
          @blur="onScriptBlacklistCommit"
          @keyup.enter="onScriptBlacklistCommit"
        />
      </SettingRow>

      <p class="script-safety-note">{{ $t('settings.noteScriptSafety') }}</p>
    </div>
  </section>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { open } from '@tauri-apps/plugin-dialog'
import { useI18n } from 'vue-i18n'
import { invoke } from '@tauri-apps/api/core'
import { aiApi, settingsApi } from '@/api'
import { useAppSettingsStore } from '@/stores/appSettings'
import SettingRow from './SettingRow.vue'

const { t } = useI18n()
const appSettings = useAppSettingsStore()
// F-260620: 接共享 toast 通道(Settings.vue showToast),替代 console.error/warn 用户零反馈
const emit = defineEmits<{ (e: 'toast', msg: string, type: 'error' | 'warning' | 'info' | 'success'): void }>()

// F-260619-03 Phase A: AI 工具文件访问授权目录白名单 UI
// 即时持久化:添加/删除立即写后端 KV + 内存白名单,无需显式"保存"(符合"操作即生效"心智,
// 避免暂存+刷新丢失的认知错配)。dirs 始终由后端 setAllowedDirs 返回值回填(canonicalize +
// strip_verbatim 规范化后的真实路径),保证前后端读写一致。workspace_root 由后端 get 返回时
// 过滤(内部隐式授权,不进用户管理列表),前端原样展示不再猜测 root 形态。
const dirs = ref<string[]>([])
const newDir = ref('')
const loading = ref(true)
const saving = ref(false)

async function load() {
  loading.value = true
  try {
    dirs.value = await aiApi.getAllowedDirs()
  } catch (e) {
    console.error('加载授权目录失败:', e)
    emit('toast', t('settings.allowedDirsLoadFail'), 'error')
    dirs.value = []
  } finally {
    loading.value = false
  }
}

// 单按钮双态:newDir 空 → 选目录(Tauri dialog::open 填入 newDir);有值 → 提交白名单
async function onAddClick() {
  if (!newDir.value.trim()) {
    await pickDir()
    return
  }
  await addDir()
}

async function pickDir() {
  try {
    const selected = await open({ directory: true, multiple: false })
    if (!selected || Array.isArray(selected)) return
    newDir.value = selected as string
    // F-260620: 选完即加(消除选目录后需二次点"添加"的摩擦)
    await addDir()
  } catch (e) {
    console.error('选择目录失败:', e)
  }
}

// 添加:本地大小写不敏感去重预检(减少无效 IPC)+ 即时持久化。
// dirs 由 setAllowedDirs 返回值回填,保证与后端规范化形态一致(读写不再漂移)。
async function addDir() {
  const d = newDir.value.trim()
  if (!d) return
  if (dirs.value.some(x => x.toLowerCase() === d.toLowerCase())) {
    emit('toast', t('settings.allowedDirsDup'), 'warning')
    newDir.value = ''
    return
  }
  saving.value = true
  try {
    const next = [...dirs.value, d]
    dirs.value = await aiApi.setAllowedDirs(next)
    newDir.value = ''
  } catch (e) {
    console.error('添加授权目录失败:', e)
    emit('toast', t('settings.allowedDirsAddFail'), 'error')
  } finally {
    saving.value = false
  }
}

// 删除:即时持久化,dirs 由后端返回值回填(与添加对称,删除也立即生效)。
async function removeDir(idx: number) {
  const next = dirs.value.filter((_, i) => i !== idx)
  saving.value = true
  try {
    dirs.value = await aiApi.setAllowedDirs(next)
  } catch (e) {
    console.error('删除授权目录失败:', e)
    emit('toast', t('settings.allowedDirsRemoveFail'), 'error')
  } finally {
    saving.value = false
  }
}

async function reload() {
  await load()
}

// ============================================================
// ScriptNode 命令执行安全 — 白/黑名单 UI
// ------------------------------------------------------------
// 前端持久化到 appSettings KV 表(df-script-whitelist / df-script-blacklist)。
// 后端 ScriptNode 通过 set_script_safety IPC 注入运行时白/黑名单(即时生效)。
// 本组件同时持久化到前端 KV(localStorage)和后端(app_settings 表+ OnceLock 全局态)。
// ============================================================
const scriptWhitelist = ref(appSettings.get<string>('df-script-whitelist', ''))
const scriptBlacklist = ref(appSettings.get<string>('df-script-blacklist', ''))

async function syncScriptSafety() {
  try {
    await settingsApi.set('df-script-whitelist', scriptWhitelist.value.trim())
    await settingsApi.set('df-script-blacklist', scriptBlacklist.value.trim())
    // 走新的 set_script_safety IPC 同时持久化 + 注入运行时(一次调用完成两件事)
    await invoke('set_script_safety', {
      whitelist: scriptWhitelist.value.trim(),
      blacklist: scriptBlacklist.value.trim(),
    })
    emit('toast', t('settings.savedHint'), 'info')
  } catch (e) {
    console.error('[SCRIPT-SAFETY] 同步后端失败:', e)
    emit('toast', t('settings.toastSaveFail', { msg: String(e) }), 'error')
  }
}

function onScriptWhitelistCommit() {
  appSettings.set('df-script-whitelist', scriptWhitelist.value.trim())
  syncScriptSafety()
}

function onScriptBlacklistCommit() {
  appSettings.set('df-script-blacklist', scriptBlacklist.value.trim())
  syncScriptSafety()
}

onMounted(load)
</script>

<style scoped>
/* 阶段1/3 UX 重构:panel/panel-header/empty-hint/btn 系列/setting-select/
   已迁移到 settings.css 全局。
   本 scoped 仅保留 AllowedDirsPanel 特有样式:allowed-dirs/dir-row/dir-path/
   add-row/dir-input/actions。 */
.allowed-dirs { display: flex; flex-direction: column; gap: 8px; }
.allowed-dirs-desc {
  font-size: 12px; color: var(--df-text-dim); margin: 0 0 4px; line-height: 1.5;
}
.dir-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 12px;
  background: var(--df-bg);
  border: 0.5px solid var(--df-border);
  border-radius: var(--df-radius-sm);
}
.dir-path {
  font-size: 13px; color: var(--df-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  flex: 1; margin-right: 12px;
}
.add-row { display: flex; gap: 8px; margin-top: 4px; }
.dir-input { flex: 1; min-width: 0; }
.actions { display: flex; gap: 8px; margin-top: 8px; }

/* ===== ScriptNode 命令执行安全面板 ===== */
.script-safety { display: flex; flex-direction: column; gap: 4px; }
.script-safety-desc {
  font-size: 12px; color: var(--df-text-dim); margin: 0 0 4px; line-height: 1.5;
}
.script-safety-input {
  min-width: 280px;
  font-family: var(--df-font-mono, monospace);
}
.script-safety-note {
  font-size: 11px; color: var(--df-text-dim); margin: 4px 0 0; line-height: 1.5;
}
</style>
