<template>
  <div class="desktop-settings-page">
    <section class="settings-panel">
      <div class="panel-header">
        <p class="eyebrow">CTMS Desktop</p>
        <h1>服务器设置</h1>
        <p class="description">配置桌面客户端要连接的 CTMS 服务端入口。业务数据仍由服务端统一保存和裁决。</p>
      </div>

      <div v-if="currentServerUrl" class="current-server">
        <span>当前服务器</span>
        <code>{{ currentServerUrl }}</code>
      </div>

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

      <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>

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

        <div class="actions">
          <el-button v-if="canCancel" size="large" @click="goBack">取消</el-button>
          <el-button type="primary" size="large" :loading="saving" :disabled="!serverUrl.trim()" @click="save">
            保存并检查连接
          </el-button>
        </div>
      </el-form>
    </section>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
import { useRouter } from "vue-router";
import { ElMessageBox } from "element-plus";
import { useAuthStore } from "../store/auth";
import { useStudyStore } from "../store/study";
import { getDesktopServerUrl, normalizeDesktopServerUrl, setDesktopServerUrl } from "../runtime";

const router = useRouter();
const auth = useAuthStore();
const studyStore = useStudyStore();

const currentServerUrl = getDesktopServerUrl();
const serverUrl = ref(currentServerUrl || "");
const urlError = ref("");
const saving = ref(false);
const connectionStatus = ref<{ type: "success" | "warning" | "error"; title: string; message: string } | null>(null);
const connectionDiagnostic = ref<{
  healthUrl: string;
  checkedAt: string;
  durationMs: number;
  httpStatus?: number;
} | null>(null);
const canCancel = computed(() => Boolean(currentServerUrl));
const HEALTH_TIMEOUT_MS = 10_000;

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 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 save = async () => {
  urlError.value = "";
  connectionStatus.value = null;
  const normalized = normalizeDesktopServerUrl(serverUrl.value);
  if (!normalized.ok) {
    urlError.value = normalized.reason;
    return;
  }

  saving.value = true;
  try {
    const health = await checkHealth(normalized.url);
    const previous = getDesktopServerUrl();
    if (!(await confirmServerChange(previous, normalized.url))) return;
    const result = setDesktopServerUrl(normalized.url);
    if (!result.ok) {
      urlError.value = result.reason;
      return;
    }
    if (previous !== result.url) {
      await clearSessionForServerChange();
    }
    connectionStatus.value = {
      type: "success",
      title: "连接已确认",
      message: result.url,
    };
    connectionDiagnostic.value = {
      healthUrl: health.healthUrl,
      checkedAt: formatDiagnosticTime(),
      durationMs: health.durationMs,
      httpStatus: health.httpStatus,
    };
    router.replace("/login");
  } catch (error) {
    const message = error instanceof Error ? error.message : "无法连接服务器的 /health";
    const details = error as Error & {
      serverUrl?: string;
      healthUrl?: string;
      durationMs?: number;
      httpStatus?: number;
    };
    connectionStatus.value = {
      type: "error",
      title: "连接检查失败",
      message,
    };
    connectionDiagnostic.value = {
      healthUrl: details.healthUrl || new URL("health", normalized.url).toString(),
      checkedAt: formatDiagnosticTime(),
      durationMs: details.durationMs ?? 0,
      httpStatus: details.httpStatus,
    };
    urlError.value = message;
  } finally {
    saving.value = false;
  }
};

const goBack = () => {
  router.back();
};
</script>

<style scoped>
.desktop-settings-page {
  box-sizing: border-box;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  background: #f6f8fb;
}

.settings-panel {
  width: min(100%, 520px);
  max-height: calc(100vh - 64px);
  padding: 32px;
  overflow: auto;
  border: 1px solid #d9e2ef;
  border-radius: 8px;
  background: #ffffff;
  box-shadow: 0 18px 48px rgba(15, 23, 42, 0.08);
}

.panel-header {
  margin-bottom: 28px;
}

.eyebrow {
  margin: 0 0 8px;
  color: #2563eb;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

h1 {
  margin: 0;
  color: #0f172a;
  font-size: 28px;
  line-height: 1.3;
}

.description {
  margin: 12px 0 0;
  color: #475569;
  font-size: 14px;
  line-height: 1.7;
}

.current-server {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  align-items: center;
  margin-bottom: 20px;
  padding: 10px 12px;
  border: 1px solid #dbe6f2;
  border-radius: 6px;
  background: #f8fafc;
  color: #64748b;
  font-size: 13px;
}

.current-server code {
  overflow-wrap: anywhere;
  color: #1e293b;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}

.connection-alert {
  margin-bottom: 18px;
}

.connection-diagnostic {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 18px;
  padding: 12px;
  border: 1px solid #dbe6f2;
  border-radius: 8px;
  background: #fbfdff;
}

.diagnostic-grid {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr);
  gap: 6px 10px;
  min-width: 0;
  color: #64748b;
  font-size: 12px;
}

.diagnostic-grid strong {
  min-width: 0;
  color: #1e293b;
  font-weight: 700;
}

.diagnostic-grid code {
  min-width: 0;
  overflow-wrap: anywhere;
}

.actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 28px;
}
</style>
