<script lang="ts" setup>
/**
 * 系统配置 - AI：二级 Tab
 * 1) 模型配置：选用平台 / 密钥 / 模型
 * 2) 病历配置：勾选 AI 写病历需要生成的字段
 * 3) 提醒文案：VIP 关联的醒目提示与知情同意
 */
import { computed, onMounted, ref, watch } from 'vue';

import { Button, Checkbox, Input, InputNumber, Select, Spin, Switch, Tabs, Tag, message } from 'ant-design-vue';
import { CheckCircleFilled, KeyOutlined } from '@ant-design/icons-vue';

import {
  getAiFeatureCopyList,
  getAiPlatformConfigOptions,
  saveAiFeatureCopy,
} from '#/views/system/ai/platform/api';
import { getSystemConfigList, saveSystemConfig } from '#/views/system/system-config/api';

/** 二级 Tab 持久化 */
const SUB_TAB_STORAGE_KEY = 'system_config_ai_sub_tab';

interface PlatformCard {
  id: number;
  code: string;
  name: string;
  logo: string;
  description: string;
  default_model: string;
  api_url: string;
  models: {
    id: number;
    label: string;
    value: string;
    code: string;
    name: string;
    description: string;
    is_default: number;
  }[];
  keys: {
    id?: number;
    key_name?: string;
    name?: string;
    label?: string;
    value: number;
    is_default?: number;
  }[];
}

interface FeatureCopyRow {
  id: number;
  feature_code: string;
  feature_name: string;
  record_label: string;
  disclaimer_text: string;
  consent_text: string;
}

const loading = ref(false);
const saving = ref(false);
const copySaving = ref(false);
const platforms = ref<PlatformCard[]>([]);
const activeProvider = ref('');
const activeApiKeyId = ref<number | undefined>(undefined);
const activeModel = ref('');
const featureCopies = ref<FeatureCopyRow[]>([]);
const subTab = ref('model');
/** 病历配置：可选字段与已勾选编码 */
const mrFieldOptions = ref<{ label: string; value: string }[]>([]);
const mrSelectedFields = ref<string[]>([]);
const mrSaving = ref(false);

// ===== Agent 高级配置（走 getSystemConfigList，写入 xk_system_config） =====
// 这组配置与上方"模型配置"独立：
//   - 上方"模型配置"决定用什么 provider/key/model（写入 ai_active_provider/_api_key_id/_model）
//   - 这里只决定"是否走 Go Agent 中转"，用独立开关 ai_agent_via_agent 控制
//     这样切换 agent 模式时不会污染模型组合（模型永远在「模型配置 Tab」选）
// 分组 0：Agent 接入（独立开关 + 服务地址 + 鉴权密钥）
// aiActiveProviderAdv 不再使用——只保留模型 Tab 里的 activeProvider 作为真实 provider
const aiAgentViaAgent = ref(false);
const aiAgentBaseUrl = ref('http://127.0.0.1:18123');
const aiAgentSecret = ref('');
const aiAgentTesting = ref(false);
// 分组 1：ReAct 多轮循环
const aiReactEnabled = ref(false);
const aiReactMaxIterations = ref(3);
const aiReactPlanningEnabled = ref(false);
const aiReactReflectionEnabled = ref(false);
const aiReactReflectionTemperature = ref(0.2);
const aiReactJsonRepairEnabled = ref(true);
// 分组 2：Token 预算
const aiTokenBudgetEnabled = ref(true);
const aiTokenBudgetPerRequest = ref(8000);
const aiTokenMaxPerCall = ref(2048);
// 分组 3：流量实验
const aiShadowTrafficEnabled = ref(false);
const aiShadowTrafficRatio = ref(0);
const aiAbTestEnabled = ref(false);
const aiAbTestExperimentRatio = ref(10);
// 高级配置保存状态
const advSaving = ref(false);

const currentPlatform = computed(() =>
  platforms.value.find((p) => p.code === activeProvider.value),
);

const keyOptions = computed(() => currentPlatform.value?.keys || []);
const modelOptions = computed(() => currentPlatform.value?.models || []);

/** 密钥展示名：key_name → name → label → 兜底 */
function keyDisplayName(k: {
  key_name?: string;
  name?: string;
  label?: string;
  value: number;
  id?: number;
}): string {
  const candidates = [k.key_name, k.name, k.label];
  for (const c of candidates) {
    const s = String(c || '').trim();
    if (s) return s;
  }
  return `密钥#${k.id || k.value}`;
}

async function load() {
  loading.value = true;
  try {
    const [optRes, copyRes] = await Promise.all([
      getAiPlatformConfigOptions(),
      getAiFeatureCopyList(),
    ]);
    const data = optRes?.data || optRes || {};
    platforms.value = Array.isArray(data.platforms) ? data.platforms : [];
    const active = data.active || {};
    activeProvider.value = String(active.provider || platforms.value[0]?.code || '');
    activeApiKeyId.value = active.api_key_id ? Number(active.api_key_id) : undefined;
    activeModel.value = String(active.model || '');
    syncSelectionForPlatform();
    const mr = data.medical_record_fields || {};
    mrFieldOptions.value = Array.isArray(mr.options)
      ? mr.options.map((o: any) => ({
          label: String(o.label || o.value || ''),
          value: String(o.value || ''),
        }))
      : [];
    mrSelectedFields.value = Array.isArray(mr.selected)
      ? mr.selected.map((c: any) => String(c))
      : mrFieldOptions.value.map((o) => o.value);
    const copies = copyRes?.data || copyRes || [];
    featureCopies.value = (Array.isArray(copies) ? copies : []).map((r: any) => ({
      id: Number(r.id || 0),
      feature_code: String(r.feature_code || ''),
      feature_name: String(r.feature_name || r.feature_code || ''),
      record_label: String(r.record_label || ''),
      disclaimer_text: String(r.disclaimer_text || ''),
      consent_text: String(r.consent_text || ''),
    }));
  } finally {
    loading.value = false;
  }
}

/** 切换平台后：密钥/模型回落到该平台默认或首个 */
function syncSelectionForPlatform() {
  const keys = keyOptions.value;
  if (activeApiKeyId.value && !keys.some((k) => k.value === activeApiKeyId.value)) {
    const def = keys.find((k) => Number(k.is_default) === 1);
    activeApiKeyId.value = def?.value ?? keys[0]?.value;
  } else if (!activeApiKeyId.value && keys[0]) {
    const def = keys.find((k) => Number(k.is_default) === 1);
    activeApiKeyId.value = def?.value ?? keys[0].value;
  }
  const models = modelOptions.value;
  if (activeModel.value && !models.some((m) => m.value === activeModel.value)) {
    const def = models.find((m) => Number(m.is_default) === 1);
    activeModel.value =
      def?.value || currentPlatform.value?.default_model || models[0]?.value || '';
  } else if (!activeModel.value) {
    const def = models.find((m) => Number(m.is_default) === 1);
    activeModel.value =
      def?.value || currentPlatform.value?.default_model || models[0]?.value || '';
  }
}

watch(activeProvider, () => {
  syncSelectionForPlatform();
});

function onSubTabChange(key: string | number) {
  subTab.value = String(key);
  localStorage.setItem(SUB_TAB_STORAGE_KEY, String(key));
}

/** 病历字段全选 / 清空 */
function selectAllMrFields() {
  mrSelectedFields.value = mrFieldOptions.value.map((o) => o.value);
}

function clearMrFields() {
  mrSelectedFields.value = [];
}

// ===== Agent 高级配置的辅助函数 =====
// 与 PHP SystemConfigService::castValue bool 分支对齐：1/true/yes 视为真
function parseBoolAdv(v: unknown): boolean {
  return v === '1' || v === 1 || v === 'true' || v === true;
}
function numStr(v: number): string {
  return String(v);
}
function boolStr(v: boolean): string {
  return v ? '1' : '0';
}

/**
 * 单独从 getSystemConfigList 加载 Agent 高级配置项
 *
 * 与上方 load() 走 getAiPlatformConfigOptions 不同：
 *   - 上方模型配置是 PHP AiRuntimeConfigService 单独提供的结构化接口
 *   - 这里走通用 key-value 接口，读 xk_system_config 表里的 16 个 ai_* key
 *     （ai_active_provider / ai_agent_* / ai_react_* / ai_token_* / ai_shadow_* / ai_ab_test_*）
 *
 * 不在 load() 里直接合并是为了避免 getAiPlatformConfigOptions 失败时牵连这组配置加载
 */
async function loadAdvancedCfg() {
  try {
    const list = await getSystemConfigList();
    const rows = Array.isArray(list) ? list : list?.data || [];
    for (const row of rows) {
      switch (row.config_key) {
        case 'ai_agent_via_agent': {
          // 是否走 Go Agent 中转：1/true/yes 视为开
          aiAgentViaAgent.value = parseBoolAdv(row.config_value);
          break;
        }
        case 'ai_agent_base_url': {
          aiAgentBaseUrl.value = String(row.config_value || 'http://127.0.0.1:18123');
          break;
        }
        case 'ai_agent_secret': {
          aiAgentSecret.value = String(row.config_value || '');
          break;
        }
        case 'ai_react_enabled': {
          aiReactEnabled.value = parseBoolAdv(row.config_value);
          break;
        }
        case 'ai_react_max_iterations': {
          aiReactMaxIterations.value = Number(row.config_value) || 3;
          break;
        }
        case 'ai_react_planning_enabled': {
          aiReactPlanningEnabled.value = parseBoolAdv(row.config_value);
          break;
        }
        case 'ai_react_reflection_enabled': {
          aiReactReflectionEnabled.value = parseBoolAdv(row.config_value);
          break;
        }
        case 'ai_react_reflection_temperature': {
          aiReactReflectionTemperature.value = Number(row.config_value) || 0.2;
          break;
        }
        case 'ai_react_json_repair_enabled': {
          aiReactJsonRepairEnabled.value = parseBoolAdv(row.config_value);
          break;
        }
        case 'ai_token_budget_enabled': {
          aiTokenBudgetEnabled.value = parseBoolAdv(row.config_value);
          break;
        }
        case 'ai_token_budget_per_request': {
          aiTokenBudgetPerRequest.value = Number(row.config_value) || 8000;
          break;
        }
        case 'ai_token_max_per_call': {
          aiTokenMaxPerCall.value = Number(row.config_value) || 2048;
          break;
        }
        case 'ai_shadow_traffic_enabled': {
          aiShadowTrafficEnabled.value = parseBoolAdv(row.config_value);
          break;
        }
        case 'ai_shadow_traffic_ratio': {
          aiShadowTrafficRatio.value = Number(row.config_value) || 0;
          break;
        }
        case 'ai_ab_test_enabled': {
          aiAbTestEnabled.value = parseBoolAdv(row.config_value);
          break;
        }
        case 'ai_ab_test_experiment_ratio': {
          aiAbTestExperimentRatio.value = Number(row.config_value) || 10;
          break;
        }
        default: {
          break;
        }
      }
    }
  } catch (e) {
    // 高级配置加载失败不阻塞主流程，仅控制台告警
    console.warn('[AiModelConfigPanel] loadAdvancedCfg failed:', e);
  }
}

/** 保存 Agent 高级配置（16 个 key 一次性 POST） */
async function handleSaveAdvanced() {
  advSaving.value = true;
  try {
    await saveSystemConfig([
      // 分组 0：Agent 接入
      // 关键：用 ai_agent_via_agent 独立开关，不再写 ai_active_provider（避免污染模型 Tab 的真实 provider）
      // 这样后台"模型配置 Tab"选什么 provider/key/model，Go Agent 中转就用什么，互不影响
      { config_key: 'ai_agent_via_agent', config_value: boolStr(aiAgentViaAgent.value) },
      { config_key: 'ai_agent_base_url', config_value: aiAgentBaseUrl.value },
      { config_key: 'ai_agent_secret', config_value: aiAgentSecret.value },
      // 分组 1：ReAct
      { config_key: 'ai_react_enabled', config_value: boolStr(aiReactEnabled.value) },
      { config_key: 'ai_react_max_iterations', config_value: numStr(aiReactMaxIterations.value) },
      { config_key: 'ai_react_planning_enabled', config_value: boolStr(aiReactPlanningEnabled.value) },
      { config_key: 'ai_react_reflection_enabled', config_value: boolStr(aiReactReflectionEnabled.value) },
      { config_key: 'ai_react_reflection_temperature', config_value: numStr(aiReactReflectionTemperature.value) },
      { config_key: 'ai_react_json_repair_enabled', config_value: boolStr(aiReactJsonRepairEnabled.value) },
      // 分组 2：Token 预算
      { config_key: 'ai_token_budget_enabled', config_value: boolStr(aiTokenBudgetEnabled.value) },
      { config_key: 'ai_token_budget_per_request', config_value: numStr(aiTokenBudgetPerRequest.value) },
      { config_key: 'ai_token_max_per_call', config_value: numStr(aiTokenMaxPerCall.value) },
      // 分组 3：流量实验
      { config_key: 'ai_shadow_traffic_enabled', config_value: boolStr(aiShadowTrafficEnabled.value) },
      { config_key: 'ai_shadow_traffic_ratio', config_value: numStr(aiShadowTrafficRatio.value) },
      { config_key: 'ai_ab_test_enabled', config_value: boolStr(aiAbTestEnabled.value) },
      { config_key: 'ai_ab_test_experiment_ratio', config_value: numStr(aiAbTestExperimentRatio.value) },
    ]);
    message.success('Agent 高级配置已保存');
  } finally {
    advSaving.value = false;
  }
}

/**
 * 测试 Agent 服务可达性（GET {base_url}/health）
 *
 * 不入库，仅 UI 反馈：
 *   - Go Agent 健康检查路径 /health 已在 middleware.Auth 白名单内，无需鉴权
 *   - 仅校验"地址可达 + 服务存活"，不验证 secret（secret 错误时业务调用会 401）
 */
async function testAgentConnection() {
  const url = String(aiAgentBaseUrl.value || '').trim();
  if (!url) {
    message.warning('请先填写 Agent 服务地址');
    return;
  }
  aiAgentTesting.value = true;
  try {
    const resp = await fetch(`${url.replace(/\/$/, '')}/health`, {
      method: 'GET',
      headers: { Accept: 'application/json' },
      signal: AbortSignal.timeout(5000),
    });
    if (resp.ok) {
      message.success(`Agent 服务可达（${resp.status}）`);
    } else {
      message.error(`Agent 响应异常：HTTP ${resp.status}`);
    }
  } catch (e) {
    message.error(`连接失败：${e instanceof Error ? e.message : String(e)}`);
  } finally {
    aiAgentTesting.value = false;
  }
}

/** 保存 AI 写病历输出字段勾选 */
async function handleSaveMrFields() {
  if (!mrSelectedFields.value.length) {
    message.warning('请至少勾选一个病历字段');
    return;
  }
  // 按 options 顺序保存，保证提示词字段顺序稳定
  const selectedSet = new Set(mrSelectedFields.value);
  const ordered = mrFieldOptions.value
    .map((o) => o.value)
    .filter((v) => selectedSet.has(v));
  mrSaving.value = true;
  try {
    await saveSystemConfig([
      {
        config_key: 'ai_medical_record_fields',
        config_value: JSON.stringify(ordered),
        value_type: 'json',
        config_group: 'ai',
        description: 'AI写病历需生成的字段编码列表（JSON数组，不含主诉）',
        sort: 410,
      } as any,
    ]);
    mrSelectedFields.value = ordered;
    message.success('病历字段配置已保存');
  } finally {
    mrSaving.value = false;
  }
}

function selectProvider(code: string) {
  activeProvider.value = code;
}

function selectKey(id: number) {
  activeApiKeyId.value = id;
}

function selectModel(code: string) {
  activeModel.value = code;
}

async function handleSave() {
  if (!activeProvider.value) {
    message.warning('请选择 AI 平台');
    return;
  }
  saving.value = true;
  try {
    await saveSystemConfig([
      {
        config_key: 'ai_active_provider',
        config_value: activeProvider.value,
        value_type: 'string',
        config_group: 'ai',
        description: '当前 AI 平台编码（spark/deepseek）',
        sort: 400,
      },
      {
        config_key: 'ai_active_api_key_id',
        config_value: String(activeApiKeyId.value || 0),
        value_type: 'string',
        config_group: 'ai',
        description: '当前选用的 AI API Key ID',
        sort: 401,
      },
      {
        config_key: 'ai_active_model',
        config_value: String(activeModel.value || ''),
        value_type: 'string',
        config_group: 'ai',
        description: '当前 AI 模型名（空则用平台默认）',
        sort: 402,
      },
    ]);
    message.success('AI 配置已保存');
  } finally {
    saving.value = false;
  }
}

/** 保存 VIP 关联的 AI 醒目提示 / 知情同意文案 */
async function handleSaveCopies() {
  if (!featureCopies.value.length) {
    message.warning('暂无文案可保存，请先执行 SQL 初始化');
    return;
  }
  copySaving.value = true;
  try {
    await saveAiFeatureCopy({
      items: featureCopies.value.map((r) => ({
        id: r.id,
        feature_code: r.feature_code,
        record_label: r.record_label,
        disclaimer_text: r.disclaimer_text,
        consent_text: r.consent_text,
      })),
    });
    message.success('AI 功能文案已保存');
  } finally {
    copySaving.value = false;
  }
}

onMounted(() => {
  const stored = localStorage.getItem(SUB_TAB_STORAGE_KEY);
  // 新增 advanced 二级 Tab 后，缓存值合法性扩展
  if (stored === 'model' || stored === 'copy' || stored === 'mr' || stored === 'advanced') {
    subTab.value = stored;
  }
  load();
  // 加载 Agent 高级配置（独立接口，失败不阻塞主流程）
  loadAdvancedCfg();
});
</script>

<template>
  <Spin :spinning="loading">
    <div class="ai-config-panel py-2">
      <Tabs :active-key="subTab" @change="onSubTabChange">
        <Tabs.TabPane key="model" tab="模型配置">
          <div class="py-2">
            <div class="mb-2 font-medium text-[hsl(var(--foreground))]">选用 AI 平台</div>
            <div class="mb-3 text-sm text-[hsl(var(--muted-foreground))]">
              平台 / 模型 / 密钥请在「AI平台管理」维护；此处点选当前运行时组合。
            </div>
            <div class="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
              <div
                v-for="p in platforms"
                :key="p.code"
                class="ai-pick-card"
                :class="{ 'ai-pick-card--on': activeProvider === p.code }"
                role="button"
                tabindex="0"
                @click="selectProvider(p.code)"
                @keydown.enter="selectProvider(p.code)"
              >
                <CheckCircleFilled
                  v-if="activeProvider === p.code"
                  class="ai-pick-check"
                />
                <div class="flex items-start gap-3">
                  <div class="ai-pick-logo">
                    <img
                      v-if="p.logo"
                      :src="p.logo"
                      :alt="p.name"
                      class="h-full w-full object-contain"
                    />
                    <span v-else class="ai-pick-logo-fallback">
                      {{ (p.name || p.code || '?').slice(0, 1) }}
                    </span>
                  </div>
                  <div class="min-w-0 flex-1 pr-5">
                    <div class="font-medium text-[hsl(var(--foreground))]">
                      {{ p.name }}
                    </div>
                    <div class="mt-0.5 text-xs text-[hsl(var(--muted-foreground))]">
                      {{ p.code }} · {{ p.default_model || '—' }}
                    </div>
                    <div
                      class="mt-1 line-clamp-2 text-xs text-[hsl(var(--muted-foreground))]"
                    >
                      {{ p.description || p.api_url }}
                    </div>
                  </div>
                </div>
              </div>
            </div>

            <div class="mt-6 mb-2 font-medium text-[hsl(var(--foreground))]">API Key</div>
            <div class="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
              <div
                v-for="k in keyOptions"
                :key="k.value"
                class="ai-pick-card"
                :class="{ 'ai-pick-card--on': activeApiKeyId === k.value }"
                role="button"
                tabindex="0"
                @click="selectKey(k.value)"
                @keydown.enter="selectKey(k.value)"
              >
                <CheckCircleFilled
                  v-if="activeApiKeyId === k.value"
                  class="ai-pick-check"
                />
                <div class="flex items-start gap-3">
                  <div class="ai-pick-icon">
                    <KeyOutlined />
                  </div>
                  <div class="min-w-0 flex-1 pr-5">
                    <div class="flex flex-wrap items-center gap-2">
                      <span class="font-medium text-[hsl(var(--foreground))]">
                        {{ keyDisplayName(k) }}
                      </span>
                      <Tag v-if="k.is_default === 1" color="gold">默认</Tag>
                    </div>
                    <div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">
                      ID {{ k.value }}
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <div
              v-if="!keyOptions.length"
              class="mt-1 text-xs text-amber-600 dark:text-amber-400"
            >
              当前平台暂无启用密钥，请先到「AI平台管理」添加
            </div>

            <div class="mt-6 mb-2 font-medium text-[hsl(var(--foreground))]">模型</div>
            <div class="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
              <div
                v-for="m in modelOptions"
                :key="m.value"
                class="ai-pick-card"
                :class="{ 'ai-pick-card--on': activeModel === m.value }"
                role="button"
                tabindex="0"
                @click="selectModel(m.value)"
                @keydown.enter="selectModel(m.value)"
              >
                <CheckCircleFilled
                  v-if="activeModel === m.value"
                  class="ai-pick-check"
                />
                <div class="min-w-0 pr-5">
                  <div class="flex flex-wrap items-center gap-2">
                    <span class="font-medium text-[hsl(var(--foreground))]">
                      {{ m.name || m.label || m.code }}
                    </span>
                    <Tag v-if="m.is_default === 1" color="gold">默认</Tag>
                  </div>
                  <div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">
                    {{ m.code }}
                  </div>
                  <div
                    v-if="m.description"
                    class="mt-1 line-clamp-2 text-xs text-[hsl(var(--muted-foreground))]"
                  >
                    {{ m.description }}
                  </div>
                </div>
              </div>
            </div>
            <div
              v-if="!modelOptions.length"
              class="mt-1 text-xs text-amber-600 dark:text-amber-400"
            >
              当前平台暂无启用模型，请先到「AI平台管理」添加
            </div>

            <div class="mt-6">
              <Button type="primary" :loading="saving" @click="handleSave">
                保存 AI 配置
              </Button>
            </div>
          </div>
        </Tabs.TabPane>

        <Tabs.TabPane key="mr" tab="病历配置">
          <div class="py-2">
            <div class="mb-3 text-sm text-[hsl(var(--muted-foreground))]">
              勾选后，AI 写病历只会返回这些字段（主诉仍由医生填写，不在此配置）。未勾选的字段不会生成。
            </div>
            <div class="mb-3 flex flex-wrap gap-2">
              <Button size="small" @click="selectAllMrFields">全选</Button>
              <Button size="small" @click="clearMrFields">清空</Button>
              <span class="text-xs text-[hsl(var(--muted-foreground))] self-center">
                已选 {{ mrSelectedFields.length }} / {{ mrFieldOptions.length }}
              </span>
            </div>
            <Checkbox.Group
              v-model:value="mrSelectedFields"
              class="ai-mr-fields grid grid-cols-2 gap-x-4 gap-y-2 md:grid-cols-3 xl:grid-cols-4"
            >
              <Checkbox
                v-for="opt in mrFieldOptions"
                :key="opt.value"
                :value="opt.value"
              >
                {{ opt.label }}
              </Checkbox>
            </Checkbox.Group>
            <div class="mt-6">
              <Button type="primary" :loading="mrSaving" @click="handleSaveMrFields">
                保存病历配置
              </Button>
            </div>
          </div>
        </Tabs.TabPane>

        <Tabs.TabPane key="copy" tab="提醒文案">
          <div class="py-2">
            <div class="mb-3 text-sm text-[hsl(var(--muted-foreground))]">
              按 VIP 功能码维护醒目提示与首次知情同意全文；医生签署后仅该功能无需再次确认。
            </div>
            <div
              v-if="!featureCopies.length"
              class="rounded-lg border border-dashed border-[hsl(var(--border))] p-4 text-sm text-[hsl(var(--muted-foreground))]"
            >
              暂无文案数据，请执行 sql/20260806/xk_ai_disclaimer_consent.sql
            </div>
            <div
              v-for="row in featureCopies"
              :key="row.feature_code"
              class="ai-copy-card mb-4"
            >
              <div class="mb-2 flex flex-wrap items-center gap-2">
                <span class="font-medium text-[hsl(var(--foreground))]">
                  {{ row.feature_name || row.feature_code }}
                </span>
                <span class="text-xs text-[hsl(var(--muted-foreground))]">
                  {{ row.feature_code }}
                </span>
              </div>
              <div class="mb-1 text-xs text-[hsl(var(--muted-foreground))]">
                记录称谓（用于「本{称谓}由 AI…」）
              </div>
              <Input
                v-model:value="row.record_label"
                class="mb-3 max-w-xs"
                placeholder="病历 / 处方"
              />
              <div class="mb-1 text-xs text-[hsl(var(--muted-foreground))]">
                醒目提示（生成记录 / 模态框顶部）
              </div>
              <Input.TextArea
                v-model:value="row.disclaimer_text"
                :rows="4"
                class="mb-3"
                placeholder="本病历/处方由 AI…"
              />
              <div class="mb-1 text-xs text-[hsl(var(--muted-foreground))]">
                首次知情同意全文
              </div>
              <Input.TextArea
                v-model:value="row.consent_text"
                :rows="6"
                placeholder="我已知悉：…"
              />
            </div>
            <div v-if="featureCopies.length" class="mt-2">
              <Button type="primary" :loading="copySaving" @click="handleSaveCopies">
                保存 AI 功能文案
              </Button>
            </div>
          </div>
        </Tabs.TabPane>

        <!-- Agent 高级：Go Agent 接入 / ReAct / Token 预算 / 流量实验 -->
        <Tabs.TabPane key="advanced" tab="Agent 高级">
          <div class="py-4 space-y-6">
            <!-- 分组 0：Agent 接入配置（独立开关 + 服务地址 + 鉴权密钥） -->
            <div class="rounded border border-solid border-blue-200 bg-blue-50/30 p-4 dark:border-blue-900 dark:bg-blue-950/20">
              <div class="mb-3 flex items-center justify-between">
                <div>
                  <div class="font-medium text-[hsl(var(--foreground))]">Agent 接入配置</div>
                  <div class="text-xs text-[hsl(var(--muted-foreground))]">
                    独立开关：开启后所有 AI 调用走 Go Agent 中转（含本地知识库检索 / ReAct 多轮），模型/密钥/平台始终由上方「模型配置」Tab 决定
                  </div>
                </div>
                <Switch
                  v-model:checked="aiAgentViaAgent"
                  checked-children="中转"
                  un-checked-children="直连"
                />
              </div>
              <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
                <div>
                  <div class="mb-1 text-sm text-[hsl(var(--foreground))]">
                    Agent 服务地址
                    <Tag v-if="aiAgentViaAgent" color="processing" class="ml-1">必填</Tag>
                  </div>
                  <Input
                    v-model:value="aiAgentBaseUrl"
                    placeholder="http://127.0.0.1:18123"
                    :disabled="!aiAgentViaAgent"
                  />
                  <div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">
                    仅当上方开关开启时生效；与 Go 端 config.yaml 的 server.port 对应
                  </div>
                </div>
                <div>
                  <div class="mb-1 text-sm text-[hsl(var(--foreground))]">
                    当前生效的模型组合
                    <Tag color="blue" class="ml-1">{{ activeProvider || '未选择' }}</Tag>
                  </div>
                  <div class="text-xs text-[hsl(var(--muted-foreground))]">
                    模型/密钥/平台由「模型配置」Tab 决定，Go Agent 中转会原样使用，
                    切换模型请前往<a class="ml-1 underline" @click="subTab = 'model'">模型配置 Tab</a>
                  </div>
                </div>
              </div>
              <div class="mt-3">
                <div class="mb-1 text-sm text-[hsl(var(--foreground))]">
                  Agent 鉴权密钥
                  <span class="ml-1 text-xs text-[hsl(var(--muted-foreground))]">
                    （与 Go 端 config.Agent.SharedSecret 对应；留空则不发 Authorization）
                  </span>
                </div>
                <Input.Password
                  v-model:value="aiAgentSecret"
                  placeholder="如 qiqi991012"
                  :disabled="!aiAgentViaAgent"
                  autocomplete="new-password"
                />
              </div>
              <div class="mt-3 flex flex-wrap items-center gap-3">
                <Button :loading="aiAgentTesting" :disabled="!aiAgentViaAgent" @click="testAgentConnection">
                  测试连接
                </Button>
                <span class="text-xs text-[hsl(var(--muted-foreground))]">
                  仅校验服务可达（GET /health），不验证密钥；密钥错误会在实际业务调用时报 401
                </span>
              </div>
            </div>

            <!-- 分组 1：ReAct 多轮推理循环 -->
            <div class="rounded border border-solid border-[hsl(var(--border))] p-4">
              <div class="mb-3 flex items-center justify-between">
                <div>
                  <div class="font-medium text-[hsl(var(--foreground))]">ReAct 多轮推理循环</div>
                  <div class="text-xs text-[hsl(var(--muted-foreground))]">
                    开启后单次任务会走 Think-Act-Observe 多轮（含 Planning / Reflection / JSON 修复），质量更好但延迟与 token 成本更高
                  </div>
                </div>
                <Switch v-model:checked="aiReactEnabled" checked-children="开" un-checked-children="关" />
              </div>
              <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
                <div>
                  <div class="mb-1 text-sm text-[hsl(var(--foreground))]">单任务最大轮数</div>
                  <InputNumber v-model:value="aiReactMaxIterations" :min="1" :max="10" style="width: 100%" />
                  <div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">建议 2-5，过大会显著拉高延迟和成本</div>
                </div>
                <div>
                  <div class="mb-1 text-sm text-[hsl(var(--foreground))]">反思步骤温度</div>
                  <InputNumber
                    v-model:value="aiReactReflectionTemperature"
                    :min="0"
                    :max="1"
                    :step="0.05"
                    style="width: 100%"
                  />
                  <div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">低于生成温度（0.3），保证审核结果稳定</div>
                </div>
              </div>
              <div class="mt-3 flex flex-wrap items-center gap-6">
                <div class="flex items-center gap-2">
                  <span class="text-sm text-[hsl(var(--foreground))]">Planning 规划阶段</span>
                  <Switch v-model:checked="aiReactPlanningEnabled" checked-children="开" un-checked-children="关" />
                </div>
                <div class="flex items-center gap-2">
                  <span class="text-sm text-[hsl(var(--foreground))]">Reflection 反思自检</span>
                  <Switch v-model:checked="aiReactReflectionEnabled" checked-children="开" un-checked-children="关" />
                </div>
                <div class="flex items-center gap-2">
                  <span class="text-sm text-[hsl(var(--foreground))]">JSON 自动修复</span>
                  <Switch v-model:checked="aiReactJsonRepairEnabled" checked-children="开" un-checked-children="关" />
                </div>
              </div>
            </div>

            <!-- 分组 2：Token 预算管理 -->
            <div class="rounded border border-solid border-[hsl(var(--border))] p-4">
              <div class="mb-3 flex items-center justify-between">
                <div>
                  <div class="font-medium text-[hsl(var(--foreground))]">Token 预算管理</div>
                  <div class="text-xs text-[hsl(var(--muted-foreground))]">
                    开启后单次任务累计 token 超限即中止，防 Agent 死循环烧钱（双层控制：单任务总预算 + 单次调用上限）
                  </div>
                </div>
                <Switch v-model:checked="aiTokenBudgetEnabled" checked-children="开" un-checked-children="关" />
              </div>
              <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
                <div>
                  <div class="mb-1 text-sm text-[hsl(var(--foreground))]">单任务总 token 上限</div>
                  <InputNumber v-model:value="aiTokenBudgetPerRequest" :min="1000" :step="1000" style="width: 100%" />
                  <div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">prompt + completion 累加；8000 约等于 1-2 万汉字</div>
                </div>
                <div>
                  <div class="mb-1 text-sm text-[hsl(var(--foreground))]">单次 LLM 调用输出上限</div>
                  <InputNumber v-model:value="aiTokenMaxPerCall" :min="256" :step="256" style="width: 100%" />
                  <div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">透传给厂商的 max_tokens 字段，防止单次响应爆炸</div>
                </div>
              </div>
            </div>

            <!-- 分组 3：流量实验 -->
            <div class="rounded border border-solid border-[hsl(var(--border))] p-4">
              <div class="mb-3 font-medium text-[hsl(var(--foreground))]">流量实验</div>
              <div class="mb-4 flex items-center justify-between">
                <div>
                  <div class="text-sm text-[hsl(var(--foreground))]">影子流量（Shadow Traffic）</div>
                  <div class="text-xs text-[hsl(var(--muted-foreground))]">
                    开启后按比例异步复制请求到 Go Agent，结果不返回用户只做对比；运维用对比报表决定是否放量
                  </div>
                </div>
                <Switch v-model:checked="aiShadowTrafficEnabled" checked-children="开" un-checked-children="关" />
              </div>
              <div class="mb-4 pl-2">
                <div class="mb-1 text-sm text-[hsl(var(--foreground))]">影子流量百分比（0-100）</div>
                <InputNumber v-model:value="aiShadowTrafficRatio" :min="0" :max="100" style="width: 200px" />
                <div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">0=关闭；建议先用 10% 跑 1 周再考虑放量</div>
              </div>
              <div class="mb-2 flex items-center justify-between">
                <div>
                  <div class="text-sm text-[hsl(var(--foreground))]">A/B Test（实验组直接返回 Agent 结果）</div>
                  <div class="text-xs text-[hsl(var(--muted-foreground))]">
                    开启后按比例把请求 provider 切换为 agent，实验组用户直接看到 Agent 输出
                  </div>
                </div>
                <Switch v-model:checked="aiAbTestEnabled" checked-children="开" un-checked-children="关" />
              </div>
              <div class="pl-2">
                <div class="mb-1 text-sm text-[hsl(var(--foreground))]">实验组流量百分比（0-100）</div>
                <InputNumber v-model:value="aiAbTestExperimentRatio" :min="0" :max="100" style="width: 200px" />
                <div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">10 表示 10% 用户走 Agent 实验组</div>
              </div>
            </div>

            <div>
              <Button type="primary" :loading="advSaving" @click="handleSaveAdvanced">
                保存 Agent 高级配置
              </Button>
            </div>
          </div>
        </Tabs.TabPane>
      </Tabs>
    </div>
  </Spin>
</template>

<style scoped>
.ai-pick-card {
  position: relative;
  cursor: pointer;
  border: 1px solid hsl(var(--border));
  border-radius: 12px;
  padding: 14px 16px;
  background: hsl(var(--card));
  color: hsl(var(--foreground));
  outline: none;
  transition:
    border-color 0.15s ease,
    box-shadow 0.15s ease,
    background-color 0.15s ease,
    transform 0.15s ease;
}
.ai-pick-card:hover {
  border-color: hsl(var(--primary) / 0.45);
  box-shadow: 0 4px 14px hsl(var(--foreground) / 0.06);
}
.ai-pick-card--on {
  border-color: hsl(var(--primary));
  background: linear-gradient(
    135deg,
    hsl(var(--primary) / 0.14),
    hsl(var(--primary) / 0.04)
  );
  box-shadow:
    0 0 0 1px hsl(var(--primary) / 0.35),
    0 8px 20px hsl(var(--primary) / 0.12);
}
.ai-pick-check {
  position: absolute;
  top: 10px;
  right: 10px;
  font-size: 18px;
  color: hsl(var(--primary));
}
.ai-pick-logo,
.ai-pick-icon {
  display: flex;
  height: 44px;
  width: 44px;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: 10px;
  border: 1px solid hsl(var(--border));
  background: hsl(var(--muted) / 0.45);
}
.ai-pick-logo {
  padding: 5px;
}
.ai-pick-icon {
  font-size: 18px;
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 0.1);
  border-color: hsl(var(--primary) / 0.25);
}
.ai-pick-logo-fallback {
  font-size: 15px;
  font-weight: 600;
  color: hsl(var(--muted-foreground));
}
.ai-copy-card {
  border: 1px solid hsl(var(--border));
  border-radius: 10px;
  padding: 14px 16px;
  background: hsl(var(--card));
}
.ai-mr-fields :deep(.ant-checkbox-wrapper) {
  margin-inline-start: 0;
  color: hsl(var(--foreground));
}
</style>
