<template>
  <div class="desktop-preferences">
    <aside class="preferences-sidebar">
      <button class="window-close" type="button" aria-label="关闭系统偏好" @click="emit('close-request')">
        <el-icon><Close /></el-icon>
      </button>

      <div class="sidebar-heading">
        <p>CTMS Desktop</p>
        <h2>设置</h2>
      </div>

      <nav class="preferences-nav" aria-label="系统偏好分类">
        <button
          v-for="section in preferenceSections"
          :key="section.id"
          type="button"
          class="preferences-nav-item"
          :class="{ active: activeSectionId === section.id }"
          @click="selectSection(section.id)"
        >
          <el-icon><component :is="section.icon" /></el-icon>
          <span>{{ section.label }}</span>
        </button>
      </nav>
    </aside>

    <section class="preferences-pane">
      <header class="pane-header">
        <Transition name="preferences-header" mode="out-in">
          <div :key="activeSectionId" class="pane-header-content">
            <div class="pane-icon">
              <el-icon><component :is="activeSection.icon" /></el-icon>
            </div>
            <div>
              <p>系统偏好</p>
              <h3>{{ activeSection.label }}</h3>
              <span>{{ activeSection.description }}</span>
            </div>
          </div>
        </Transition>
      </header>

      <div ref="paneBodyRef" class="pane-body">
        <Transition name="preferences-panel" mode="out-in">
        <section v-if="activeSectionId === 'connection'" key="connection" class="preference-panel">
          <div class="server-overview">
            <span class="server-status-dot" :class="{ connected: Boolean(desktopServerUrl) }"></span>
            <div>
              <div class="row-title">{{ desktopServerUrl ? "已连接" : "未配置服务器" }}</div>
              <code>{{ desktopServerUrl || "未配置" }}</code>
            </div>
          </div>

          <Transition name="connection-feedback">
            <el-alert
              v-if="connectionStatus"
              class="connection-alert"
              :type="connectionStatus.type"
              :title="connectionStatus.title"
              :description="connectionStatus.message"
              show-icon
              :closable="false"
            />
          </Transition>

          <Transition name="connection-feedback">
            <div v-if="connectionDiagnostic" class="connection-diagnostic">
              <div class="diagnostic-grid">
                <span>检查时间</span>
                <strong>{{ connectionDiagnostic.checkedAt }}</strong>
                <span>健康检查</span>
                <code>{{ connectionDiagnostic.healthUrl }}</code>
                <span>耗时</span>
                <strong>{{ connectionDiagnostic.durationMs }}ms</strong>
                <span>HTTP</span>
                <strong>{{ connectionDiagnostic.httpStatus || "-" }}</strong>
              </div>
            </div>
          </Transition>

          <el-form class="server-config-form" label-position="top" @submit.prevent>
            <el-form-item label="服务器地址" :error="urlError">
              <el-input
                v-model.trim="serverUrl"
                placeholder="https://ctms.example.com"
                autocomplete="url"
                @keyup.enter="testDesktopServerConnection"
              />
            </el-form-item>

            <div class="connection-actions">
              <el-button :loading="serverChecking" :disabled="!serverUrl.trim()" @click="testDesktopServerConnection">
                测试连通性
              </el-button>
              <el-button type="primary" :loading="serverSaving" :disabled="!serverUrl.trim()" @click="saveDesktopServerConfig">
                保存
              </el-button>
            </div>
          </el-form>
        </section>

        <section v-else-if="activeSectionId === 'appearance'" key="appearance" class="preference-panel">
          <div class="preference-row">
            <div>
              <div class="row-title">界面主题</div>
              <div class="row-desc">桌面工作台与系统弹窗配色</div>
            </div>
            <div class="theme-segmented" role="group" aria-label="界面主题">
              <button
                v-for="option in themeOptions"
                :key="option.value"
                type="button"
                class="theme-option"
                :class="{ active: desktopTheme === option.value }"
                @click="setTheme(option.value)"
              >
                <el-icon><component :is="option.icon" /></el-icon>
                <span>{{ option.label }}</span>
              </button>
            </div>
          </div>
        </section>

        <section v-else-if="activeSectionId === 'notifications'" key="notifications" class="preference-panel">
          <div class="preference-row">
            <div>
              <div class="row-title">系统通知</div>
              <div class="row-desc">不含项目详情的文件更新提示</div>
            </div>
            <div class="row-control">
              <el-switch
                v-model="desktopNotificationsEnabled"
                :loading="desktopNotificationLoading"
                @change="onDesktopNotificationChange"
              />
              <el-tag size="small" :type="notificationPermissionTagType">{{ notificationPermissionText }}</el-tag>
            </div>
          </div>
        </section>

        <section v-else-if="activeSectionId === 'updates'" key="updates" class="preference-panel">
          <div class="preference-row">
            <div>
              <div class="row-title">桌面更新</div>
              <div class="row-desc">{{ desktopUpdateDescription }}</div>
            </div>
            <div class="row-control">
              <el-button
                v-if="desktopUpdateStatus.pendingUpdate"
                size="small"
                type="primary"
                :loading="desktopUpdateStatus.installing"
                @click="installPendingDesktopUpdate"
              >
                安装更新
              </el-button>
              <el-button size="small" :disabled="!desktopUpdaterAvailable" :loading="desktopUpdateChecking" @click="checkDesktopUpdateNow">
                检查更新
              </el-button>
            </div>
          </div>
        </section>

        <section v-else key="diagnostics" class="preference-panel">
          <div class="metadata-panel">
            <dl>
              <template v-for="row in clientMetadataRows" :key="row.label">
                <dt>{{ row.label }}</dt>
                <dd>{{ row.value }}</dd>
              </template>
            </dl>
            <el-button size="small" @click="copyClientMetadata">复制诊断信息</el-button>
          </div>
        </section>
        </Transition>
      </div>
    </section>
  </div>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, type Component } from "vue";
import { useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import { Bell, Close, Download, InfoFilled, Link, Monitor, Moon, Sunny } from "@element-plus/icons-vue";
import {
  getDesktopNotificationSubscription,
  setDesktopNotificationSubscription,
} from "../api/desktopNotifications";
import {
  clientRuntime,
  getAppMetadata,
  getDesktopServerUrl,
  getNotificationPermission,
  isDesktopUpdaterAvailable,
  normalizeDesktopServerUrl,
  readDesktopThemePreference,
  requestNotificationPermission,
  setDesktopServerUrl,
  setDesktopThemePreference,
  type DesktopThemePreference,
  type NotificationPermissionState,
} from "../runtime";
import {
  checkDesktopUpdateAndPrompt,
  getDesktopUpdateStatus,
  listenDesktopUpdateStatus,
  promptForPendingDesktopUpdate,
  type DesktopUpdateStatusSnapshot,
} from "../session/desktopUpdateManager";
import { triggerDesktopNotificationPoll } from "../session/desktopNotificationManager";
import { useAuthStore } from "../store/auth";
import { useStudyStore } from "../store/study";

const emit = defineEmits<{
  "close-request": [];
}>();

const router = useRouter();
const auth = useAuthStore();
const studyStore = useStudyStore();
type PreferenceSectionId = "connection" | "appearance" | "notifications" | "updates" | "diagnostics";

const preferenceSections: Array<{
  id: PreferenceSectionId;
  label: string;
  description: string;
  icon: Component;
}> = [
  { id: "connection", label: "连接", description: "服务器入口与在线状态", icon: Link },
  { id: "appearance", label: "外观", description: "主题与界面偏好", icon: Monitor },
  { id: "notifications", label: "通知", description: "系统通知权限与投递状态", icon: Bell },
  { id: "updates", label: "更新", description: "桌面端签名更新与安装状态", icon: Download },
  { id: "diagnostics", label: "诊断信息", description: "客户端版本、平台与能力", icon: InfoFilled },
];
const activeSectionId = ref<PreferenceSectionId>("connection");
const activeSection = computed(
  () => preferenceSections.find((section) => section.id === activeSectionId.value) || preferenceSections[0],
);
const paneBodyRef = ref<HTMLElement | null>(null);
const desktopServerUrl = ref(getDesktopServerUrl());
const serverUrl = ref(desktopServerUrl.value || "");
const urlError = ref("");
const serverChecking = ref(false);
const serverSaving = ref(false);
type ConnectionStatusType = "success" | "warning" | "error" | "info";
const connectionStatus = ref<{ type: ConnectionStatusType; title: string; message: string } | null>(null);
const connectionDiagnostic = ref<{
  healthUrl: string;
  checkedAt: string;
  durationMs: number;
  httpStatus?: number;
} | null>(null);
const clientMetadata = getAppMetadata();
const desktopCapabilities = clientRuntime.capabilities();
const desktopUpdaterAvailable = isDesktopUpdaterAvailable();
const desktopNotificationsEnabled = ref(false);
const desktopNotificationLoading = ref(false);
const desktopUpdateChecking = ref(false);
const desktopTheme = ref<DesktopThemePreference>(readDesktopThemePreference());
const notificationPermission = ref<NotificationPermissionState>("unsupported");
const desktopUpdateStatus = ref<DesktopUpdateStatusSnapshot>(getDesktopUpdateStatus());
let updateStatusUnlisten: (() => void) | undefined;
let connectionPendingTimer: number | undefined;
const themeOptions = [
  { value: "light" as const, label: "明亮", icon: Sunny },
  { value: "dark" as const, label: "暗黑", icon: Moon },
];
const HEALTH_TIMEOUT_MS = 10_000;

const clientMetadataRows = computed(() => [
  { label: "客户端", value: `${clientMetadata.clientType} ${clientMetadata.version}` },
  { label: "平台", value: clientMetadata.platform },
  { label: "构建通道", value: clientMetadata.channel },
  { label: "提交", value: clientMetadata.commit },
  {
    label: "能力",
    value: [
      desktopCapabilities.secureSessionStorage ? "安全会话" : "浏览器会话",
      desktopCapabilities.nativeFiles ? "原生文件" : "浏览器文件",
      desktopCapabilities.systemNotifications ? "系统通知" : "无系统通知",
      desktopCapabilities.automaticUpdates ? "自动更新" : "无自动更新",
    ].join(" / "),
  },
]);

const notificationPermissionText = computed(() => {
  if (notificationPermission.value === "granted") return "已授权";
  if (notificationPermission.value === "denied") return "已拒绝";
  if (notificationPermission.value === "prompt") return "待授权";
  return "不可用";
});

const notificationPermissionTagType = computed(() => {
  if (notificationPermission.value === "granted") return "success";
  if (notificationPermission.value === "denied") return "danger";
  if (notificationPermission.value === "prompt") return "warning";
  return "info";
});

const desktopUpdateDescription = computed(() => {
  const pending = desktopUpdateStatus.value.pendingUpdate;
  if (desktopUpdateStatus.value.checking) return "正在检查签名更新";
  if (desktopUpdateStatus.value.installing) return "正在安装更新并准备重启";
  if (pending && ["postponed", "suppressed"].includes(desktopUpdateStatus.value.lastStatus)) {
    return `发现 ${pending.version}，已选择稍后提醒`;
  }
  if (pending) return `发现 ${pending.version}，当前 ${pending.currentVersion}`;
  if (desktopUpdateStatus.value.lastStatus === "disabled") return "当前构建未启用桌面端自动更新";
  if (desktopUpdateStatus.value.lastStatus === "up-to-date") return "当前已是最新版本";
  if (desktopUpdateStatus.value.lastStatus === "failed") {
    return desktopUpdateStatus.value.lastError || "桌面端更新检查失败";
  }
  return "正式版本按当前服务器发布源检查签名更新";
});

const selectSection = (sectionId: PreferenceSectionId) => {
  if (activeSectionId.value === sectionId) return;
  paneBodyRef.value?.scrollTo({ top: 0, behavior: "smooth" });
  activeSectionId.value = sectionId;
};

const formatDiagnosticTime = (date = new Date()) =>
  date.toLocaleString("zh-CN", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
    hour12: false,
  });

const toConnectionError = (
  message: string,
  details: {
    serverUrl: string;
    healthUrl: string;
    durationMs: number;
    httpStatus?: number;
  },
) => Object.assign(new Error(message), details);

const checkHealth = async (baseUrl: string) => {
  const healthUrl = new URL("health", baseUrl).toString();
  const controller = new AbortController();
  const startedAt = performance.now();
  const timeout = window.setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
  const details = () => ({
    serverUrl: baseUrl,
    healthUrl,
    durationMs: Math.max(0, Math.round(performance.now() - startedAt)),
  });
  try {
    const response = await fetch(healthUrl, {
      method: "GET",
      headers: { Accept: "application/json" },
      signal: controller.signal,
    });
    if (!response.ok) {
      throw toConnectionError(`服务器健康检查返回 HTTP ${response.status}`, {
        ...details(),
        httpStatus: response.status,
      });
    }
    return {
      ...details(),
      httpStatus: response.status,
    };
  } catch (error) {
    if (error instanceof DOMException && error.name === "AbortError") {
      throw toConnectionError("连接超时，请确认服务端地址和网络状态", details());
    }
    if (error instanceof TypeError) {
      throw toConnectionError("网络请求失败，请确认地址、证书或 CORS 配置", details());
    }
    throw error;
  } finally {
    window.clearTimeout(timeout);
  }
};

const writeConnectionResult = (
  type: ConnectionStatusType,
  title: string,
  message: string,
  details: {
    serverUrl: string;
    healthUrl: string;
    durationMs: number;
    httpStatus?: number;
  },
) => {
  connectionStatus.value = { type, title, message };
  connectionDiagnostic.value = {
    healthUrl: details.healthUrl,
    checkedAt: formatDiagnosticTime(),
    durationMs: details.durationMs,
    httpStatus: details.httpStatus,
  };
};

const setConnectionPending = (message: string) => {
  connectionStatus.value = {
    type: "info",
    title: "正在检查",
    message,
  };
};

const clearConnectionPendingTimer = () => {
  if (connectionPendingTimer === undefined) return;
  window.clearTimeout(connectionPendingTimer);
  connectionPendingTimer = undefined;
};

const scheduleConnectionPending = (message: string) => {
  clearConnectionPendingTimer();
  connectionPendingTimer = window.setTimeout(() => {
    connectionPendingTimer = undefined;
    if (serverChecking.value || serverSaving.value) {
      setConnectionPending(message);
    }
  }, 180);
};

const clearSessionForServerChange = async () => {
  await auth.logout({ rememberCurrentStudy: false });
  studyStore.clearCurrentStudy();
};

const confirmServerChange = async (previous: string | null, next: string) => {
  if (!previous || previous === next) return true;
  const confirmed = await ElMessageBox.confirm(
    "切换服务器会退出当前会话并清除当前项目上下文，确认后需要重新登录。",
    "确认切换服务器",
    {
      type: "warning",
      confirmButtonText: "切换并退出登录",
      cancelButtonText: "继续编辑",
      distinguishCancelAndClose: true,
    },
  ).catch(() => null);
  return Boolean(confirmed);
};

const validateServerUrl = () => {
  urlError.value = "";
  const normalized = normalizeDesktopServerUrl(serverUrl.value);
  if (!normalized.ok) {
    urlError.value = normalized.reason;
    return null;
  }
  return normalized.url;
};

const testDesktopServerConnection = async () => {
  if (serverChecking.value || serverSaving.value) return;
  const normalizedUrl = validateServerUrl();
  if (!normalizedUrl) return;

  serverChecking.value = true;
  scheduleConnectionPending("正在请求服务器健康检查");
  try {
    const health = await checkHealth(normalizedUrl);
    clearConnectionPendingTimer();
    writeConnectionResult("success", "连通性正常", "服务器健康检查通过", health);
  } catch (error) {
    clearConnectionPendingTimer();
    const message = error instanceof Error ? error.message : "无法连接服务器的 /health";
    const details = error as Error & {
      serverUrl?: string;
      healthUrl?: string;
      durationMs?: number;
      httpStatus?: number;
    };
    writeConnectionResult("error", "连接检查失败", message, {
      serverUrl: normalizedUrl,
      healthUrl: details.healthUrl || new URL("health", normalizedUrl).toString(),
      durationMs: details.durationMs ?? 0,
      httpStatus: details.httpStatus,
    });
    urlError.value = message;
  } finally {
    serverChecking.value = false;
  }
};

const saveDesktopServerConfig = async () => {
  if (serverChecking.value || serverSaving.value) return;
  const normalizedUrl = validateServerUrl();
  if (!normalizedUrl) {
    ElMessage.error(urlError.value || "服务器地址无效");
    return;
  }

  serverSaving.value = true;
  scheduleConnectionPending("保存前正在检查服务器连通性");
  try {
    const health = await checkHealth(normalizedUrl);
    clearConnectionPendingTimer();
    const previous = getDesktopServerUrl();
    if (!(await confirmServerChange(previous, normalizedUrl))) {
      connectionStatus.value = {
        type: "warning",
        title: "未保存",
        message: "服务器地址未更改",
      };
      return;
    }
    const result = setDesktopServerUrl(normalizedUrl);
    if (!result.ok) {
      urlError.value = result.reason;
      connectionStatus.value = {
        type: "error",
        title: "保存失败",
        message: result.reason,
      };
      ElMessage.error(`保存失败：${result.reason}`);
      return;
    }
    desktopServerUrl.value = result.url;
    serverUrl.value = result.url;
    writeConnectionResult(
      "success",
      "连接已确认",
      result.url,
      {
        serverUrl: result.url,
        healthUrl: health.healthUrl,
        durationMs: health.durationMs,
        httpStatus: health.httpStatus,
      },
    );
    if (previous !== result.url) {
      await clearSessionForServerChange();
      emit("close-request");
      void router.replace("/login");
    }
    ElMessage.success("服务器设置已保存");
  } catch (error) {
    clearConnectionPendingTimer();
    const message = error instanceof Error ? error.message : "无法连接服务器的 /health";
    const details = error as Error & {
      serverUrl?: string;
      healthUrl?: string;
      durationMs?: number;
      httpStatus?: number;
    };
    writeConnectionResult("error", "保存失败", message, {
      serverUrl: normalizedUrl,
      healthUrl: details.healthUrl || new URL("health", normalizedUrl).toString(),
      durationMs: details.durationMs ?? 0,
      httpStatus: details.httpStatus,
    });
    urlError.value = message;
    ElMessage.error(`保存失败：${message}`);
  } finally {
    serverSaving.value = false;
  }
};

const loadNotificationState = async () => {
  notificationPermission.value = await getNotificationPermission();
  try {
    const { data } = await getDesktopNotificationSubscription();
    desktopNotificationsEnabled.value = data.enabled;
  } catch {
    desktopNotificationsEnabled.value = false;
  }
};

const onDesktopNotificationChange = async (value: string | number | boolean) => {
  if (desktopNotificationLoading.value) return;
  desktopNotificationLoading.value = true;
  try {
    if (value) {
      notificationPermission.value = await requestNotificationPermission();
      if (notificationPermission.value !== "granted") {
        desktopNotificationsEnabled.value = false;
        ElMessage.warning("系统通知权限未开启，请在系统设置中允许 CTMS 通知");
        return;
      }
    }
    const { data } = await setDesktopNotificationSubscription(Boolean(value));
    desktopNotificationsEnabled.value = data.enabled;
    if (data.enabled) triggerDesktopNotificationPoll();
  } catch (error: any) {
    desktopNotificationsEnabled.value = !Boolean(value);
    ElMessage.error(error?.response?.data?.detail || "系统通知设置失败");
  } finally {
    desktopNotificationLoading.value = false;
  }
};

const checkDesktopUpdateNow = async () => {
  if (desktopUpdateChecking.value) return;
  desktopUpdateChecking.value = true;
  try {
    await checkDesktopUpdateAndPrompt({ notifyWhenCurrent: true, promptWhenAvailable: true });
  } finally {
    desktopUpdateChecking.value = false;
  }
};

const installPendingDesktopUpdate = async () => {
  await promptForPendingDesktopUpdate();
};

const setTheme = (theme: DesktopThemePreference) => {
  desktopTheme.value = setDesktopThemePreference(theme);
};

const copyClientMetadata = async () => {
  const text = clientMetadataRows.value.map((row) => `${row.label}: ${row.value}`).join("\n");
  if (!navigator.clipboard?.writeText) {
    ElMessage.warning("当前环境无法访问剪贴板");
    return;
  }
  await navigator.clipboard.writeText(text);
  ElMessage.success("诊断信息已复制");
};

onMounted(() => {
  updateStatusUnlisten = listenDesktopUpdateStatus((status) => {
    desktopUpdateStatus.value = status;
  });
  void loadNotificationState();
});

onBeforeUnmount(() => {
  clearConnectionPendingTimer();
  updateStatusUnlisten?.();
});
</script>

<style scoped>
/* =====================================================
 * 设计变量（色彩 / 尺寸）
 * ===================================================== */
.desktop-preferences {
  /* 品牌主色 */
  --pref-accent: #1e4d82;
  --pref-accent-light: #e8f0fb;
  --pref-accent-active: #d0e2f8;

  /* 中性色
   * 侧边栏用极浅灰，内容区用纯白，分隔线用半透明细线
   * 避免两块色块对撞产生割裂感 */
  --pref-bg-sidebar: #f2f4f7;
  --pref-bg-pane: #ffffff;
  --pref-bg-card: #ffffff;
  --pref-sidebar-border: rgba(0, 0, 0, 0.07);
  --pref-border: #e6eaef;
  --pref-border-card: #dde5f0;

  /* 文字 */
  --pref-text-primary: #0f172a;
  --pref-text-secondary: #4b5563;
  --pref-text-muted: #8b97a8;

  /* 状态色 */
  --pref-green: #22a06b;
  --pref-amber: #b45309;

  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  width: 100%;
  /* height 让组件有确定高度，配合 dialog body 的 flex:1 撑满容器
   * 若 dialog 比 700px 高，组件会通过 flex:1 继续撑大 */
  height: min(700px, calc(100vh - 96px));
  overflow: hidden;
  background: var(--pref-bg-pane);
  border-radius: inherit;
}

/* =====================================================
 * 侧边栏
 * ===================================================== */
.preferences-sidebar {
  display: flex;
  min-width: 0;
  min-height: 0;
  flex-direction: column;
  gap: 20px;
  padding: 20px 12px;
  /* 用半透明线替代硬边框，消除左右割裂感 */
  border-right: 1px solid var(--pref-sidebar-border);
  background: var(--pref-bg-sidebar);
}

.window-close {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  padding: 0;
  border: 1.5px solid rgba(220, 38, 38, 0.5);
  border-radius: 999px;
  background: #ff5f57;
  color: transparent;
  cursor: pointer;
  transition: box-shadow 160ms ease;
}

.window-close :deep(.el-icon) {
  width: 9px;
  height: 9px;
}

.window-close:hover {
  color: rgba(127, 29, 29, 0.85);
  box-shadow: 0 0 0 3px rgba(255, 95, 87, 0.25);
}

.sidebar-heading {
  padding: 4px 10px 0;
}

.sidebar-heading p,
.pane-header p {
  margin: 0 0 3px;
  color: var(--pref-text-muted);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.sidebar-heading h2,
h3 {
  margin: 0;
  color: var(--pref-text-primary);
  font-weight: 750;
  letter-spacing: -0.3px;
}

.sidebar-heading h2 {
  font-size: 20px;
}

/* =====================================================
 * 导航列表
 * ===================================================== */
.preferences-nav {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
}

.preferences-nav-item {
  appearance: none;
  position: relative;
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 38px;
  padding: 0 11px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--pref-text-secondary);
  cursor: pointer;
  font: inherit;
  font-size: 13.5px;
  font-weight: 580;
  overflow: hidden;
  text-align: left;
  transition:
    background-color 140ms ease,
    color 140ms ease,
    transform 140ms ease;
}

.preferences-nav-item::before {
  content: "";
  position: absolute;
  top: 8px;
  bottom: 8px;
  left: 0;
  width: 3px;
  border-radius: 0 999px 999px 0;
  background: var(--pref-accent);
  opacity: 0;
  transform: scaleY(0.3);
  transition:
    opacity 150ms ease,
    transform 180ms cubic-bezier(0.2, 0.85, 0.2, 1);
}

.preferences-nav-item:hover {
  background: rgba(30, 77, 130, 0.06);
  color: var(--pref-text-primary);
}

.preferences-nav-item:active {
  transform: scale(0.985);
}

.preferences-nav-item.active {
  background: var(--pref-accent-light);
  color: var(--pref-accent);
  font-weight: 650;
}

.preferences-nav-item.active::before {
  opacity: 1;
  transform: scaleY(1);
}

.preferences-nav-item span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* =====================================================
 * 右侧内容区
 * ===================================================== */
.preferences-pane {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  min-width: 0;
  min-height: 0;
  background: var(--pref-bg-pane);
}

.pane-header {
  box-sizing: border-box;
  overflow: hidden;
  padding: 22px 30px 18px;
  border-bottom: 1px solid var(--pref-border);
  background: var(--pref-bg-pane);
}

.pane-header-content {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr);
  align-items: center;
  gap: 13px;
}

.pane-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 9px;
  background: var(--pref-accent-light);
  color: var(--pref-accent);
  font-size: 19px;
}

h3 {
  font-size: 21px;
}

.pane-header span {
  display: block;
  margin-top: 4px;
  color: var(--pref-text-secondary);
  font-size: 12.5px;
}

.pane-body {
  position: relative;
  min-width: 0;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
  padding: 24px 30px 32px;
  scrollbar-gutter: stable;
}

.preference-panel {
  display: flex;
  max-width: 600px;
  flex-direction: column;
  gap: 10px;
}

/* =====================================================
 * 过渡动画
 * ===================================================== */
.preferences-header-enter-active {
  transition:
    opacity 170ms ease,
    transform 190ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.preferences-header-leave-active {
  transition:
    opacity 100ms ease,
    transform 100ms ease;
}

.preferences-header-enter-from {
  opacity: 0;
  transform: translateY(6px);
}

.preferences-header-leave-to {
  opacity: 0;
  transform: translateY(-4px);
}

.preferences-panel-enter-active {
  transition:
    opacity 180ms ease,
    transform 210ms cubic-bezier(0.2, 0.8, 0.2, 1),
    filter 180ms ease;
}

.preferences-panel-leave-active {
  transition:
    opacity 95ms ease,
    transform 95ms ease,
    filter 95ms ease;
}

.preferences-panel-enter-from {
  opacity: 0;
  filter: blur(1px);
  transform: translateY(10px);
}

.preferences-panel-leave-to {
  opacity: 0;
  filter: blur(1px);
  transform: translateY(-6px);
}

.preferences-panel-enter-active .server-overview,
.preferences-panel-enter-active .server-config-form,
.preferences-panel-enter-active .connection-alert,
.preferences-panel-enter-active .connection-diagnostic,
.preferences-panel-enter-active .preference-row,
.preferences-panel-enter-active .metadata-panel {
  animation: preference-card-rise 220ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

@keyframes preference-card-rise {
  from {
    opacity: 0;
    transform: translateY(8px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* =====================================================
 * 卡片通用样式
 * ===================================================== */
.server-overview,
.preference-row,
.metadata-panel {
  border: 1px solid var(--pref-border-card);
  border-radius: 10px;
  background: var(--pref-bg-card);
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.05), 0 1px 1px rgba(15, 23, 42, 0.03);
}

/* =====================================================
 * 连接状态卡片
 * ===================================================== */
.server-overview {
  display: grid;
  grid-template-columns: 8px minmax(0, 1fr);
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
}

.server-status-dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--pref-amber);
  flex-shrink: 0;
}

.server-status-dot.connected {
  background: var(--pref-green);
  box-shadow: 0 0 0 3px rgba(34, 160, 107, 0.18);
}

/* =====================================================
 * 服务器配置表单卡片
 * ===================================================== */
.server-config-form {
  padding: 16px 16px 12px;
  border: 1px solid var(--pref-border-card);
  border-radius: 10px;
  background: var(--pref-bg-card);
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.05), 0 1px 1px rgba(15, 23, 42, 0.03);
}

.connection-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--pref-border);
}

.preference-panel :deep(.el-button) {
  transition:
    background-color 150ms ease,
    border-color 150ms ease,
    color 150ms ease,
    box-shadow 150ms ease,
    opacity 150ms ease,
    transform 120ms ease;
}

.preference-panel :deep(.el-button:not(.is-disabled):active) {
  transform: scale(0.982);
}

.connection-actions :deep(.el-button) {
  min-width: 100px;
  font-size: 13px;
}

.connection-alert {
  --el-alert-padding: 10px 12px;
  border-radius: 8px;
  transition:
    background-color 180ms ease,
    border-color 180ms ease,
    color 180ms ease,
    opacity 180ms ease,
    transform 180ms ease;
}

/* =====================================================
 * 连接诊断信息展示
 * ===================================================== */
.connection-diagnostic {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 16px;
  border: 1px solid var(--pref-border);
  border-radius: 8px;
  background: #f7f9fc;
  transition:
    background-color 180ms ease,
    border-color 180ms ease,
    box-shadow 180ms ease,
    opacity 180ms ease,
    transform 180ms ease;
}

.connection-feedback-enter-active {
  transition:
    opacity 180ms ease,
    transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.connection-feedback-leave-active {
  transition:
    opacity 120ms ease,
    transform 120ms ease;
}

.connection-feedback-enter-from,
.connection-feedback-leave-to {
  opacity: 0;
  transform: translateY(6px);
}

.diagnostic-grid {
  display: grid;
  grid-template-columns: 66px minmax(0, 1fr);
  gap: 7px 12px;
  min-width: 0;
}

.diagnostic-grid span {
  color: var(--pref-text-muted);
  font-size: 11.5px;
}

.diagnostic-grid strong {
  min-width: 0;
  color: var(--pref-text-primary);
  font-size: 11.5px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* =====================================================
 * 偏好行（通知、外观、更新等）
 * ===================================================== */
.preference-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 15px 16px;
}

.row-title {
  color: var(--pref-text-primary);
  font-size: 13.5px;
  font-weight: 640;
}

.row-desc {
  margin-top: 3px;
  color: var(--pref-text-secondary);
  font-size: 12px;
}

.row-control {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

/* =====================================================
 * 主题切换器
 * ===================================================== */
.theme-segmented {
  display: inline-grid;
  grid-template-columns: repeat(2, minmax(72px, 1fr));
  gap: 3px;
  padding: 3px;
  border: 1px solid var(--pref-border-card);
  border-radius: 8px;
  background: #eef1f6;
}

.theme-option {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 72px;
  height: 28px;
  padding: 0 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--pref-text-muted);
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  transition:
    background-color 140ms ease,
    color 140ms ease,
    box-shadow 140ms ease;
}

.theme-option:hover {
  color: var(--pref-text-primary);
}

.theme-option.active {
  background: #ffffff;
  color: var(--pref-accent);
  box-shadow: 0 1px 4px rgba(15, 23, 42, 0.1);
}

/* =====================================================
 * 诊断信息面板
 * ===================================================== */
.metadata-panel {
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: flex-start;
  padding: 16px;
}

dl {
  display: grid;
  grid-template-columns: 76px minmax(0, 1fr);
  width: 100%;
  gap: 8px 12px;
  margin: 0;
}

dt {
  color: var(--pref-text-muted);
  font-size: 12px;
}

dd {
  margin: 0;
  color: var(--pref-text-primary);
  font-size: 12px;
  overflow-wrap: anywhere;
}

code {
  display: block;
  margin-top: 3px;
  overflow-wrap: anywhere;
  color: var(--pref-text-primary);
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
  font-size: 11.5px;
}

/* =====================================================
 * 深色主题
 * ===================================================== */
:global([data-ctms-theme="dark"] .desktop-preferences-dialog .el-dialog__body) {
  background: #0f1721;
}

:global([data-ctms-theme="dark"] .desktop-preferences) {
  --pref-accent: #7ab3f0;
  --pref-accent-light: #172540;
  --pref-accent-active: #1e3254;
  --pref-bg-sidebar: #131b28;
  --pref-bg-pane: #161f2e;
  --pref-bg-card: #1c2840;
  --pref-sidebar-border: rgba(255, 255, 255, 0.06);
  --pref-border: #243144;
  --pref-border-card: #243144;
  --pref-text-primary: #f1f5f9;
  --pref-text-secondary: #94a3b8;
  --pref-text-muted: #5a6c82;
  --pref-green: #34d399;
  --pref-amber: #fbbf24;

  background: var(--pref-bg-pane);
  color: var(--pref-text-primary);
}

:global([data-ctms-theme="dark"] .desktop-preferences .preferences-sidebar) {
  border-right-color: var(--pref-sidebar-border);
  background: var(--pref-bg-sidebar);
}

:global([data-ctms-theme="dark"] .desktop-preferences .preferences-pane) {
  background: var(--pref-bg-pane);
}

:global([data-ctms-theme="dark"] .desktop-preferences .pane-header) {
  border-bottom-color: var(--pref-border);
  background: var(--pref-bg-pane);
}

:global([data-ctms-theme="dark"] .desktop-preferences .pane-icon) {
  background: var(--pref-accent-light);
  color: var(--pref-accent);
}

:global([data-ctms-theme="dark"] .desktop-preferences .preferences-nav-item) {
  color: var(--pref-text-secondary);
}

:global([data-ctms-theme="dark"] .desktop-preferences .preferences-nav-item:hover) {
  background: rgba(122, 179, 240, 0.07);
  color: var(--pref-text-primary);
}

:global([data-ctms-theme="dark"] .desktop-preferences .preferences-nav-item.active) {
  background: var(--pref-accent-light);
  color: var(--pref-accent);
}

:global([data-ctms-theme="dark"] .desktop-preferences .preferences-nav-item.active::before) {
  background: var(--pref-accent);
}

:global([data-ctms-theme="dark"] .desktop-preferences .sidebar-heading h2),
:global([data-ctms-theme="dark"] .desktop-preferences h3),
:global([data-ctms-theme="dark"] .desktop-preferences .row-title),
:global([data-ctms-theme="dark"] .desktop-preferences dd),
:global([data-ctms-theme="dark"] .desktop-preferences code) {
  color: var(--pref-text-primary);
}

:global([data-ctms-theme="dark"] .desktop-preferences .sidebar-heading p),
:global([data-ctms-theme="dark"] .desktop-preferences .pane-header p),
:global([data-ctms-theme="dark"] .desktop-preferences .pane-header span),
:global([data-ctms-theme="dark"] .desktop-preferences .pane-header h3),
:global([data-ctms-theme="dark"] .desktop-preferences .row-desc),
:global([data-ctms-theme="dark"] .desktop-preferences dt) {
  color: var(--pref-text-secondary);
}

:global([data-ctms-theme="dark"] .desktop-preferences .server-overview),
:global([data-ctms-theme="dark"] .desktop-preferences .server-config-form),
:global([data-ctms-theme="dark"] .desktop-preferences .connection-diagnostic),
:global([data-ctms-theme="dark"] .desktop-preferences .preference-row),
:global([data-ctms-theme="dark"] .desktop-preferences .metadata-panel) {
  border-color: var(--pref-border-card);
  background: var(--pref-bg-card);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}

:global([data-ctms-theme="dark"] .desktop-preferences .connection-diagnostic) {
  background: #0d1520;
}

:global([data-ctms-theme="dark"] .desktop-preferences .diagnostic-grid span) {
  color: var(--pref-text-muted);
}

:global([data-ctms-theme="dark"] .desktop-preferences .diagnostic-grid strong) {
  color: var(--pref-text-primary);
}

:global([data-ctms-theme="dark"] .desktop-preferences .connection-actions) {
  border-top-color: var(--pref-border);
}

:global([data-ctms-theme="dark"] .desktop-preferences .theme-segmented) {
  border-color: var(--pref-border-card);
  background: #0d1420;
}

:global([data-ctms-theme="dark"] .desktop-preferences .theme-option) {
  color: var(--pref-text-muted);
}

:global([data-ctms-theme="dark"] .desktop-preferences .theme-option:hover) {
  color: var(--pref-text-primary);
}

:global([data-ctms-theme="dark"] .desktop-preferences .theme-option.active) {
  background: var(--pref-accent-light);
  color: var(--pref-accent);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

:global([data-ctms-theme="dark"] .desktop-preferences .server-status-dot.connected) {
  box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.2);
}

/* =====================================================
 * 减少动画模式
 * ===================================================== */
@media (prefers-reduced-motion: reduce) {
  .preferences-nav-item,
  .preferences-nav-item::before,
  .theme-option,
  .preference-panel :deep(.el-button),
  .connection-alert,
  .connection-diagnostic,
  .connection-feedback-enter-active,
  .connection-feedback-leave-active,
  .preferences-header-enter-active,
  .preferences-header-leave-active,
  .preferences-panel-enter-active,
  .preferences-panel-leave-active {
    animation: none;
    transition: none;
  }

  .preferences-header-enter-from,
  .preferences-header-leave-to,
  .connection-feedback-enter-from,
  .connection-feedback-leave-to,
  .preferences-panel-enter-from,
  .preferences-panel-leave-to {
    opacity: 1;
    filter: none;
    transform: none;
  }
}
</style>
