<script setup lang="ts">
import { Hide, View } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import { computed, reactive, ref } from "vue";

import { api } from "../services/api";
import {
  systemSettingSections,
  type SystemSettingDefinition,
  type SystemSettingValue,
} from "../config/systemSettings";

interface SettingGroupBlueprint {
  title: string;
  description: string;
  keys: string[];
}

interface SettingGroup extends Omit<SettingGroupBlueprint, "keys"> {
  settings: SystemSettingDefinition[];
}

const props = defineProps<{
  values: Record<string, SystemSettingValue>;
  saving: boolean;
  dirty: boolean;
}>();

const emit = defineEmits<{
  save: [];
  update: [key: string, value: SystemSettingValue];
}>();

const groupBlueprints: Record<string, SettingGroupBlueprint[]> = {
  用户与登录: [
    {
      title: "学员默认配置",
      description: "新学员创建、导入时采用的默认资料和额度。",
      keys: ["daily_chat_limit", "default_user_name_prefix"],
    },
    {
      title: "登录与验证码",
      description: "控制登录状态和验证码的有效时间。",
      keys: ["access_token_expire_minutes", "sms_code_expire_minutes"],
    },
    {
      title: "短信服务",
      description: "在模拟短信与阿里云短信之间切换，并维护短信通道参数。",
      keys: [
        "mock_sms_enabled",
        "mock_sms_code",
        "aliyun_sms_access_key_id",
        "aliyun_sms_access_key_secret",
        "aliyun_sms_sign_name",
        "aliyun_sms_template_code",
        "aliyun_sms_template_param_key",
        "aliyun_sms_endpoint",
      ],
    },
  ],
  "AI 问答": [
    {
      title: "回答体验",
      description: "控制引用展示和模型请求的基本行为。",
      keys: [
        "ai_timeout_seconds",
        "mock_model_enabled",
        "show_reference_sources",
      ],
    },
    {
      title: "对话记忆与实修回顾",
      description: "控制会话记忆，以及成功问答达到多少轮后开始整理实修回顾资料。",
      keys: ["chat_context_message_count", "practice_review_auto_settle_successful_rounds"],
    },
    {
      title: "模型分流",
      description: "决定正式问答在不同问题类型下如何选择模型。",
      keys: ["chat_model_routing_mode"],
    },
    {
      title: "并发与排队",
      description: "控制集中提问时的执行上限、等待容量和超时保护。",
      keys: [
        "chat_max_active_requests",
        "chat_max_queue_size",
        "chat_queue_timeout_seconds",
        "chat_active_lease_seconds",
      ],
    },
    {
      title: "语音输入",
      description: "控制用户端麦克风入口、录音上限和独立的阿里云语音凭证。",
      keys: [
        "voice_input_enabled",
        "voice_max_duration_seconds",
        "aliyun_nls_app_key",
        "aliyun_nls_access_key_id",
        "aliyun_nls_access_key_secret",
        "aliyun_nls_endpoint",
      ],
    },
  ],
  外部服务: [
    {
      title: "飞书接入",
      description: "配置飞书知识库直读和独立检索服务。",
      keys: ["feishu_search_url", "feishu_app_id", "feishu_app_secret"],
    },
    {
      title: "稳定性策略",
      description: "控制飞书接口慢响应和临时失败时的处理方式。",
      keys: ["feishu_timeout_seconds", "feishu_retry_count"],
    },
  ],
  系统开关: [
    {
      title: "审计与数据",
      description: "控制后台审计信息和聊天记录导出能力。",
      keys: ["show_operation_log_detail", "chat_export_enabled"],
    },
    {
      title: "运行状态",
      description: "用于系统维护期间的访问和提示策略。",
      keys: ["maintenance_mode"],
    },
  ],
};

const wideSettingKeys = new Set([
  "chat_model_routing_mode",
  "chat_max_active_requests",
  "feishu_search_url",
]);

const activeSectionTitle = ref(systemSettingSections[0]?.title || "");
const revealedSecrets = reactive<Record<string, string>>({});
const revealingSecrets = reactive<Record<string, boolean>>({});

const activeSection = computed(
  () =>
    systemSettingSections.find(
      (section) => section.title === activeSectionTitle.value,
    ) || systemSettingSections[0],
);

const activeGroups = computed<SettingGroup[]>(() => {
  const section = activeSection.value;
  if (!section) return [];

  const byKey = new Map(section.settings.map((setting) => [setting.key, setting]));
  const configuredKeys = new Set<string>();
  const groups = (groupBlueprints[section.title] || []).map((group) => {
    const settings = group.keys
      .map((key) => byKey.get(key))
      .filter((setting): setting is SystemSettingDefinition => Boolean(setting));
    settings.forEach((setting) => configuredKeys.add(setting.key));
    return { title: group.title, description: group.description, settings };
  });

  const ungrouped = section.settings.filter(
    (setting) => !configuredKeys.has(setting.key),
  );
  if (ungrouped.length) {
    groups.push({
      title: "其他设置",
      description: "尚未归入固定分组的新配置项。",
      settings: ungrouped,
    });
  }
  return groups.filter((group) => group.settings.length);
});

const statusItems = computed(() => [
  {
    label: "短信通道",
    value: props.values.mock_sms_enabled ? "模拟验证码" : "阿里云短信",
    tone: props.values.mock_sms_enabled ? "warning" : "success",
  },
  {
    label: "AI 模式",
    value: props.values.mock_model_enabled ? "模拟模型" : "真实模型",
    tone: props.values.mock_model_enabled ? "warning" : "success",
  },
  {
    label: "会话记忆",
    value:
      Number(props.values.chat_context_message_count) > 0
        ? `最近 ${props.values.chat_context_message_count} 条`
        : "已关闭",
    tone:
      Number(props.values.chat_context_message_count) > 0 ? "neutral" : "warning",
  },
  {
    label: "问答容量",
    value: `${props.values.chat_max_active_requests || 0} 并发`,
    tone: "neutral",
  },
  {
    label: "语音输入",
    value: props.values.voice_input_enabled
      ? `已开启 · ${props.values.voice_max_duration_seconds || 60} 秒`
      : "已关闭",
    tone: props.values.voice_input_enabled ? "success" : "neutral",
  },
]);

function updateSetting(key: string, value: unknown) {
  if (value === undefined || value === null) return;
  emit("update", key, value as SystemSettingValue);
}

async function toggleSecret(setting: SystemSettingDefinition) {
  if (revealedSecrets[setting.key] !== undefined) {
    delete revealedSecrets[setting.key];
    return;
  }
  const currentValue = String(props.values[setting.key] ?? "");
  if (currentValue && currentValue !== "******") {
    revealedSecrets[setting.key] = currentValue;
    return;
  }
  if (revealingSecrets[setting.key]) return;
  revealingSecrets[setting.key] = true;
  try {
    const result = await api.revealConfigSecret(setting.key);
    revealedSecrets[setting.key] = result.configValue;
  } catch (error) {
    ElMessage.error(error instanceof Error ? error.message : "敏感配置读取失败");
  } finally {
    revealingSecrets[setting.key] = false;
  }
}

function updatePasswordSetting(key: string, value: string) {
  if (revealedSecrets[key] !== undefined) revealedSecrets[key] = value;
  updateSetting(key, value);
}
</script>

<template>
  <section class="system-config-page">
    <header class="system-config-header">
      <div>
        <div class="system-config-title-line">
          <h2>系统配置</h2>
          <span :class="['save-state', { dirty }]">
            {{ dirty ? "有未保存修改" : "配置已同步" }}
          </span>
        </div>
        <p>集中维护学员登录、AI 问答、飞书接入和系统运行策略。</p>
      </div>
      <div class="system-config-actions">
        <small>保存后立即对后续请求生效</small>
        <el-button
          type="primary"
          :loading="saving"
          :disabled="!dirty"
          @click="emit('save')"
        >
          保存修改
        </el-button>
      </div>
    </header>

    <section class="config-status-strip" aria-label="当前关键配置摘要">
      <div
        v-for="item in statusItems"
        :key="item.label"
        class="config-status-item"
      >
        <span>{{ item.label }}</span>
        <strong :class="`tone-${item.tone}`">{{ item.value }}</strong>
      </div>
    </section>

    <div class="config-workspace">
      <nav class="config-section-nav" aria-label="系统配置分组">
        <div class="config-nav-heading">
          <strong>配置分类</strong>
          <span>选择一个分类集中编辑</span>
        </div>
        <button
          v-for="section in systemSettingSections"
          :key="section.title"
          type="button"
          :class="{ active: activeSectionTitle === section.title }"
          @click="activeSectionTitle = section.title"
        >
          <span>
            <strong>{{ section.title }}</strong>
            <small>{{ section.description }}</small>
          </span>
          <b>{{ section.settings.length }}</b>
        </button>
      </nav>

      <section v-if="activeSection" class="config-editor-panel">
        <header class="config-editor-heading">
          <div>
            <span>当前分类</span>
            <h3>{{ activeSection.title }}</h3>
            <p>{{ activeSection.description }}</p>
          </div>
          <strong>{{ activeSection.settings.length }} 项配置</strong>
        </header>

        <div class="config-groups">
          <article
            v-for="group in activeGroups"
            :key="group.title"
            class="config-group-card"
          >
            <header>
              <h4>{{ group.title }}</h4>
              <p>{{ group.description }}</p>
            </header>

            <div class="config-fields-grid">
              <div
                v-for="setting in group.settings"
                :key="setting.key"
                :class="[
                  'config-field',
                  `config-field-${setting.type}`,
                  { wide: wideSettingKeys.has(setting.key) },
                ]"
              >
                <template v-if="setting.type === 'switch'">
                  <div class="config-switch-copy">
                    <strong>{{ setting.label }}</strong>
                    <small>{{ setting.description }}</small>
                  </div>
                  <el-switch
                    :model-value="Boolean(values[setting.key])"
                    @update:model-value="updateSetting(setting.key, $event)"
                  />
                </template>

                <template v-else>
                  <label :for="`setting-${setting.key}`">
                    <strong>{{ setting.label }}</strong>
                    <span v-if="setting.type === 'password'">敏感配置</span>
                  </label>
                  <el-input-number
                    v-if="setting.type === 'number'"
                    :id="`setting-${setting.key}`"
                    :model-value="Number(values[setting.key])"
                    :min="setting.min"
                    :max="setting.max"
                    controls-position="right"
                    @update:model-value="updateSetting(setting.key, $event)"
                  />
                  <el-input
                    v-else-if="setting.type === 'text'"
                    :id="`setting-${setting.key}`"
                    :model-value="String(values[setting.key] ?? '')"
                    :placeholder="setting.placeholder"
                    clearable
                    @update:model-value="updateSetting(setting.key, $event)"
                  />
                  <el-input
                    v-else-if="setting.type === 'password'"
                    :id="`setting-${setting.key}`"
                    :model-value="revealedSecrets[setting.key] ?? String(values[setting.key] ?? '')"
                    :placeholder="setting.placeholder"
                    :type="revealedSecrets[setting.key] !== undefined ? 'text' : 'password'"
                    @update:model-value="updatePasswordSetting(setting.key, $event)"
                  >
                    <template #suffix>
                      <button
                        type="button"
                        class="secret-visibility-toggle"
                        :class="{ loading: revealingSecrets[setting.key] }"
                        :aria-label="revealedSecrets[setting.key] !== undefined ? '隐藏真实内容' : '查看真实内容'"
                        @click="toggleSecret(setting)"
                      >
                        <el-icon>
                          <Hide v-if="revealedSecrets[setting.key] !== undefined" />
                          <View v-else />
                        </el-icon>
                      </button>
                    </template>
                  </el-input>
                  <el-select
                    v-else-if="setting.type === 'select'"
                    :id="`setting-${setting.key}`"
                    :model-value="String(values[setting.key] ?? '')"
                    :placeholder="setting.placeholder"
                    @update:model-value="updateSetting(setting.key, $event)"
                  >
                    <el-option
                      v-for="option in setting.options || []"
                      :key="option.value"
                      :label="option.label"
                      :value="option.value"
                    />
                  </el-select>
                  <small>{{ setting.description }}</small>
                  <small
                    v-if="setting.valueHint"
                    class="config-value-hint"
                  >
                    {{ setting.valueHint(values[setting.key]) }}
                  </small>
                </template>
              </div>
            </div>
          </article>
        </div>
      </section>
    </div>
  </section>
</template>

<style scoped>
.system-config-page {
  display: grid;
  gap: 18px;
  min-width: 0;
  padding-bottom: 24px;
}

.system-config-header {
  display: flex;
  gap: 24px;
  align-items: flex-start;
  justify-content: space-between;
}

.system-config-title-line {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

.system-config-title-line h2 {
  margin: 0;
}

.system-config-header p {
  margin: 7px 0 0;
  color: #687a75;
  line-height: 1.6;
}

.save-state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 9px;
  border-radius: 999px;
  background: #eaf3f0;
  color: #55716a;
  font-size: 12px;
  font-weight: 600;
}

.save-state::before {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #62a38d;
  content: "";
}

.save-state.dirty {
  background: #fff4df;
  color: #95651b;
}

.save-state.dirty::before {
  background: #d79a37;
}

.system-config-actions {
  display: flex;
  flex: 0 0 auto;
  gap: 12px;
  align-items: center;
}

.system-config-actions small {
  color: #83918d;
}

.system-config-actions .el-button {
  min-width: 104px;
}

.config-status-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  overflow: hidden;
  border: 1px solid #dfe9e5;
  border-radius: 14px;
  background: #fff;
}

.config-status-item {
  display: grid;
  gap: 7px;
  min-width: 0;
  padding: 15px 18px;
}

.config-status-item + .config-status-item {
  border-left: 1px solid #e6eeeb;
}

.config-status-item span {
  color: #7a8b86;
  font-size: 12px;
}

.config-status-item strong {
  overflow: hidden;
  color: #2d463f;
  font-size: 14px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.config-status-item .tone-success {
  color: #197653;
}

.config-status-item .tone-warning {
  color: #a26919;
}

.config-workspace {
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  gap: 18px;
  align-items: start;
  min-width: 0;
}

.config-section-nav,
.config-editor-panel {
  border: 1px solid #dfe8e5;
  border-radius: 16px;
  background: #fff;
}

.config-section-nav {
  position: sticky;
  top: 18px;
  display: grid;
  gap: 6px;
  padding: 14px;
}

.config-nav-heading {
  display: grid;
  gap: 4px;
  padding: 5px 7px 11px;
}

.config-nav-heading strong {
  color: #29423b;
  font-size: 14px;
}

.config-nav-heading span {
  color: #879691;
  font-size: 12px;
}

.config-section-nav button {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px;
  align-items: center;
  width: 100%;
  padding: 12px;
  border: 1px solid transparent;
  border-radius: 11px;
  background: transparent;
  color: #49605a;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.18s ease, background-color 0.18s ease;
}

.config-section-nav button:hover {
  background: #f5f8f7;
}

.config-section-nav button.active {
  border-color: #bcded2;
  background: #edf7f3;
  color: #176d53;
}

.config-section-nav button > span {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.config-section-nav button strong {
  font-size: 14px;
}

.config-section-nav button small {
  overflow: hidden;
  color: #82908c;
  font-size: 11px;
  line-height: 1.45;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.config-section-nav button b {
  min-width: 26px;
  padding: 4px 7px;
  border-radius: 999px;
  background: #eef3f1;
  color: #71817c;
  font-size: 11px;
  text-align: center;
}

.config-section-nav button.active b {
  background: #d9eee7;
  color: #176d53;
}

.config-editor-panel {
  min-width: 0;
  overflow: hidden;
}

.config-editor-heading {
  display: flex;
  gap: 20px;
  align-items: flex-start;
  justify-content: space-between;
  padding: 22px 24px;
  border-bottom: 1px solid #e5ecea;
  background: linear-gradient(135deg, #fbfdfc, #f4f9f7);
}

.config-editor-heading > div {
  min-width: 0;
}

.config-editor-heading span {
  color: #248064;
  font-size: 12px;
  font-weight: 700;
}

.config-editor-heading h3 {
  margin: 6px 0 0;
  color: #203b33;
  font-size: 20px;
}

.config-editor-heading p {
  margin: 7px 0 0;
  color: #70827d;
  font-size: 13px;
  line-height: 1.6;
}

.config-editor-heading > strong {
  flex: 0 0 auto;
  padding: 6px 10px;
  border-radius: 999px;
  background: #e7f2ee;
  color: #4d6d64;
  font-size: 12px;
}

.config-groups {
  display: grid;
  gap: 16px;
  padding: 20px;
}

.config-group-card {
  overflow: hidden;
  border: 1px solid #e1eae7;
  border-radius: 13px;
  background: #fff;
}

.config-group-card > header {
  padding: 15px 18px;
  border-bottom: 1px solid #e8efed;
  background: #fafcfb;
}

.config-group-card h4 {
  margin: 0;
  color: #2c453e;
  font-size: 15px;
}

.config-group-card header p {
  margin: 5px 0 0;
  color: #7b8c87;
  font-size: 12px;
  line-height: 1.55;
}

.config-fields-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  padding: 4px 18px 18px;
}

.config-field {
  display: grid;
  align-content: start;
  gap: 8px;
  min-width: 0;
  padding: 15px 16px 2px 0;
}

.config-field:nth-child(even):not(.wide) {
  padding-right: 0;
  padding-left: 16px;
  border-left: 1px solid #edf2f0;
}

.config-field.wide {
  grid-column: 1 / -1;
  padding-right: 0;
}

.config-field label {
  display: flex;
  gap: 8px;
  align-items: center;
  justify-content: space-between;
  min-width: 0;
}

.config-field label strong,
.config-switch-copy strong {
  color: #324841;
  font-size: 14px;
}

.config-field label span {
  flex: 0 0 auto;
  padding: 3px 7px;
  border-radius: 999px;
  background: #fff1ed;
  color: #a4503f;
  font-size: 10px;
  font-weight: 600;
}

.config-field > small,
.config-switch-copy small {
  color: #7b8985;
  font-size: 12px;
  line-height: 1.55;
}

.config-field :deep(.el-input-number),
.config-field :deep(.el-input),
.config-field :deep(.el-select) {
  width: 100%;
}

.secret-visibility-toggle {
  display: inline-flex;
  padding: 2px;
  border: 0;
  background: transparent;
  cursor: pointer;
  color: #66736f;
}

.secret-visibility-toggle.loading {
  pointer-events: none;
  opacity: 0.45;
}

.config-field :deep(.el-input__wrapper),
.config-field :deep(.el-select__wrapper),
.config-field :deep(.el-input-number) {
  min-height: 40px;
}

.config-field-switch {
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 20px;
  align-items: center;
  min-height: 70px;
  padding-bottom: 12px;
}

.config-switch-copy {
  display: grid;
  gap: 5px;
  min-width: 0;
}

.config-value-hint {
  padding: 10px 12px;
  border: 1px solid #cde4db;
  border-radius: 9px;
  background: #f2f9f6;
  color: #2c6e58 !important;
}

@media (max-width: 1320px) {
  .config-workspace {
    grid-template-columns: 220px minmax(0, 1fr);
  }

  .config-status-strip {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .config-status-item:nth-child(3) {
    border-left: 0;
    border-top: 1px solid #e6eeeb;
  }

  .config-status-item:nth-child(4) {
    border-top: 1px solid #e6eeeb;
  }
}

@media (max-width: 1080px) {
  .system-config-header {
    flex-direction: column;
  }

  .system-config-actions {
    width: 100%;
    justify-content: space-between;
  }

  .config-workspace {
    grid-template-columns: 1fr;
  }

  .config-section-nav {
    position: static;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .config-nav-heading {
    grid-column: 1 / -1;
  }
}

@media (max-width: 760px) {
  .config-status-strip,
  .config-section-nav,
  .config-fields-grid {
    grid-template-columns: 1fr;
  }

  .config-status-item + .config-status-item,
  .config-status-item:nth-child(3),
  .config-status-item:nth-child(4) {
    border-top: 1px solid #e6eeeb;
    border-left: 0;
  }

  .config-field,
  .config-field:nth-child(even):not(.wide) {
    padding-right: 0;
    padding-left: 0;
    border-left: 0;
  }
}
</style>
