import { FormEvent, startTransition, useEffect, useState } from "react";

type PageKey = "overview" | "requests" | "profiles" | "rules" | "logs";
type Tone = "" | "success" | "error";
type ReasoningMatchMode = "formula_518n_minus_2" | "manual";
type AuthMode = "passthrough" | "fixed_bearer" | "manual_bearer" | "auth_json";

type GatewayConfig = {
  profile_name?: string;
  image_profile_name?: string;
  listen_host?: string;
  listen_port?: number;
  management_access_key?: string;
  management_access_key_configured?: boolean;
  upstream_base_url?: string;
  upstream_auth_mode?: string;
  upstream_auth_env?: string | null;
  upstream_auth_json_key?: string | null;
  image_base_url?: string;
  image_auth_mode?: string;
  image_auth_env?: string | null;
  image_auth_json_key?: string | null;
  request_history_limit?: number;
  model_remap?: string;
  endpoints?: string[];
  reasoning_match_mode?: ReasoningMatchMode;
  reasoning_equals?: number[];
  retryable_status_codes?: number[];
  retryable_error_messages?: string[];
  upstream_fetch_retry_attempts?: number;
  upstream_fetch_retry_backoff_ms?: number;
  non_stream_status_code?: number;
  log_match?: boolean;
};

type Metrics = {
  started_at?: string;
  persistent_since?: string | null;
  total_proxy_request_count?: number;
  inspected_response_count?: number;
  matched_response_count?: number;
  reasoning_516_count?: number;
  reasoning_516_ratio?: number;
  token_totals?: {
    input_tokens?: number;
    output_tokens?: number;
    total_tokens?: number;
    reasoning_tokens?: number;
    cached_tokens?: number;
  };
  observed_reasoning_counts?: Record<string, number>;
  observed_reasoning_counts_total_keys?: number;
  observed_reasoning_counts_omitted?: number;
};

type StatusPayload = {
  ok: boolean;
  listen?: string;
  config?: GatewayConfig;
  state?: {
    provider_name?: string;
    codex_current_base_url?: string;
    latest_backup_path?: string;
  };
  paths?: {
    config_path?: string;
    profiles_dir?: string;
    image_profiles_dir?: string;
    log_path?: string;
    thread_rules_path?: string;
  };
  metrics?: Metrics;
};

type Usage = {
  input_tokens?: number | null;
  output_tokens?: number | null;
  total_tokens?: number | null;
  reasoning_tokens?: number | null;
  cached_tokens?: number | null;
};

type RequestEntry = {
  seq: number;
  request_id?: string | null;
  response_id?: string | null;
  thread_id?: string | null;
  lifecycle_state?: string | null;
  started_at?: string;
  first_response_at?: string | null;
  first_response_delay_ms?: number | null;
  finished_at?: string | null;
  last_activity_at?: string | null;
  duration_ms?: number | null;
  profile_name?: string;
  method?: string;
  path?: string;
  request_body_bytes?: number | null;
  response_bytes_received?: number | null;
  model?: string | null;
  requested_model?: string | null;
  forwarded_model?: string | null;
  reasoning_effort?: string | null;
  reasoning_summary?: string | null;
  response_stream?: boolean;
  stream_chunk_count?: number | null;
  usage_last_updated_at?: string | null;
  upstream_attempt_count?: number | null;
  reasoning_guard_enabled?: boolean;
  reasoning_guard_thread_override?: string | null;
  reasoning_retry_enabled?: boolean;
  reasoning_retry_query_count?: number | null;
  reasoning_retry_round_count?: number | null;
  reasoning_retry_current_round?: number | null;
  reasoning_retry_current_width?: number | null;
  reasoning_retry_current_firsts?: Array<{
    round?: number | null;
    slot?: number | null;
    first_response_at?: string | null;
    first_response_delay_ms?: number | null;
    outcome?: string | null;
    status_code?: number | null;
    upstream_status_code?: number | null;
    reasoning_tokens?: number | null;
    matched?: boolean | null;
  }> | null;
  reasoning_retry_winner_round?: number | null;
  reasoning_retry_winner_slot?: number | null;
  reasoning_retry_stop_reason?: string | null;
  reasoning_retry_thread_mode?: string | null;
  reasoning_retry_extra_inspected_count?: number | null;
  reasoning_retry_extra_matched_count?: number | null;
  reasoning_retry_extra_usage?: Usage | null;
  reasoning_retry_extra_reasoning_counts?: Record<string, number> | null;
  matched?: boolean;
  status_code?: number | null;
  upstream_status_code?: number | null;
  upstream?: {
    origin?: string;
    path?: string;
    route?: "default" | "images" | string;
    auth_mode?: string;
    auth_source?: string;
    authorization_configured?: boolean;
    status?: number;
    content_type?: string;
  } | null;
  reasoning_tokens?: number | null;
  usage?: Usage | null;
  error?: string | null;
};

type RequestsPayload = {
  total_entries?: number;
  latest_seq?: number;
  entries?: RequestEntry[];
};

type ThreadRuleEntry = {
  thread_id: string;
  reasoning_intercept_enabled: boolean;
  updated_at?: string | null;
};

type ThreadRulesPayload = {
  ok: boolean;
  thread_rules_path?: string;
  rules?: ThreadRuleEntry[];
  saved_rule?: ThreadRuleEntry | null;
  removed_rule?: ThreadRuleEntry | null;
  message?: string;
};

type ProfileFormModel = {
  listen_host?: string;
  listen_port?: string;
  upstream_base_url?: string;
  auth_mode?: string;
  auth_env?: string;
  auth_file?: string;
  manual_secret_file?: string;
  manual_secret_configured?: boolean;
  auth_json_path?: string;
  auth_json_key?: string;
  request_history_limit?: string;
  model_remap?: string;
  reasoning_match_mode?: ReasoningMatchMode;
  reasoning_equals?: string;
  retryable_status_codes?: string;
  retryable_error_messages?: string[];
  upstream_fetch_retry_attempts?: string;
  upstream_fetch_retry_backoff_ms?: string;
  endpoints?: string[];
};

type Profile = {
  name: string;
  active: boolean;
  file_path?: string;
  summary?: {
    listen_host?: string;
    listen_port?: string;
    upstream_base_url?: string;
    auth_mode?: string;
    auth_source?: string;
    request_history_limit?: string;
    model_remap?: string;
    reasoning_match_mode?: ReasoningMatchMode;
    reasoning_equals?: string;
  };
  form?: ProfileFormModel;
};

type ImageProfileFormModel = {
  base_url?: string;
  auth_mode?: string;
  auth_env?: string;
  auth_file?: string;
  manual_secret_file?: string;
  manual_secret_configured?: boolean;
  auth_json_path?: string;
  auth_json_key?: string;
};

type ImageProfile = {
  name: string;
  active: boolean;
  file_path?: string;
  summary?: {
    base_url?: string;
    auth_mode?: string;
    auth_source?: string;
  };
  form?: ImageProfileFormModel;
};

type ProfilesPayload = {
  profiles_dir?: string;
  active_profile?: string;
  applied_profile?: {
    profile?: string;
    hot_swapped?: boolean;
    listen?: string;
    upstream_base_url?: string;
  } | null;
  profiles?: Profile[];
};

type ImageProfilesPayload = {
  image_profiles_dir?: string;
  active_image_profile?: string;
  applied_image_profile?: {
    image_profile?: string;
    hot_swapped?: boolean;
    image_base_url?: string;
  } | null;
  image_profiles?: ImageProfile[];
};

type LogEntry = {
  seq: number;
  at?: string;
  message?: string;
};

type LogsPayload = {
  total_entries?: number;
  latest_seq?: number;
  entries?: LogEntry[];
};

type MessageState = {
  text: string;
  tone: Tone;
};

type ProfileFormState = {
  name: string;
  listen_host: string;
  listen_port: string;
  upstream_base_url: string;
  auth_mode: AuthMode;
  auth_env: string;
  auth_file: string;
  manual_secret: string;
  manual_secret_file: string;
  manual_secret_configured: boolean;
  auth_json_path: string;
  auth_json_key: string;
  request_history_limit: string;
  model_remap: string;
  reasoning_match_mode: ReasoningMatchMode;
  reasoning_equals: string;
  retryable_status_codes: string;
  retryable_error_messages: string;
  upstream_fetch_retry_attempts: string;
  upstream_fetch_retry_backoff_ms: string;
  endpoints: string;
};

type ImageProfileFormState = {
  name: string;
  base_url: string;
  auth_mode: AuthMode;
  auth_env: string;
  auth_file: string;
  manual_secret: string;
  manual_secret_file: string;
  manual_secret_configured: boolean;
  auth_json_path: string;
  auth_json_key: string;
};

type RuleFormState = {
  reasoning_match_mode: ReasoningMatchMode;
  reasoning_equals: string;
  retryable_status_codes: string;
  retryable_error_messages: string;
  upstream_fetch_retry_attempts: string;
  upstream_fetch_retry_backoff_ms: string;
  endpoints: string;
  non_stream_status_code: string;
  log_match: boolean;
};

const api = {
  status: "/__codex_retry_gateway/api/status",
  config: "/__codex_retry_gateway/api/config",
  logs: "/__codex_retry_gateway/api/logs",
  requests: "/__codex_retry_gateway/api/requests",
  threadRules: "/__codex_retry_gateway/api/thread-rules",
  profiles: "/__codex_retry_gateway/api/profiles",
  profileProbe: "/__codex_retry_gateway/api/profiles/probe",
  profileSwitch: "/__codex_retry_gateway/api/profiles/switch",
  imageProfiles: "/__codex_retry_gateway/api/image-profiles",
  imageProfileProbe: "/__codex_retry_gateway/api/image-profiles/probe",
  imageProfileSwitch: "/__codex_retry_gateway/api/image-profiles/switch",
  restore: "/__codex_retry_gateway/api/restore",
};

const ACCESS_KEY_STORAGE_KEY = "codex_retry_gateway_access_key";

const REQUEST_PAGE_SIZE = 20;
const LOG_PAGE_SIZE = 200;

const zhNumberFormatter = new Intl.NumberFormat("zh-CN");
const zhTimestampFormatter = new Intl.DateTimeFormat("zh-CN", {
  year: "numeric",
  month: "2-digit",
  day: "2-digit",
  hour: "2-digit",
  minute: "2-digit",
  second: "2-digit",
  hourCycle: "h23",
});

type ProfileProbePayload = {
  ok: boolean;
  profile?: string;
  image_profile?: string;
  upstream_base_url?: string;
  image_base_url?: string;
  auth_mode?: string;
  auth_source?: string;
  authorization_configured?: boolean;
  model_remap?: string;
  probes?: Array<{
    kind?: string;
    target?: string;
    status?: number;
    requested_model?: string;
    forwarded_model?: string;
    content_type?: string;
    body_preview?: string;
  }>;
};

const pageCopy: Record<PageKey, { title: string; subtitle: string; index: string }> = {
  overview: {
    title: "概览",
    subtitle: "当前 gateway 运行态，以及基于持久请求记录回灌的累计统计。",
    index: "01",
  },
  requests: {
    title: "请求",
    subtitle: "每次请求的时间戳、状态、token usage 和命中情况。",
    index: "02",
  },
  profiles: {
    title: "Profiles",
    subtitle: "文本和图片上游分别保存、探测与热切换，互不改写对方配置。",
    index: "03",
  },
  rules: {
    title: "规则",
    subtitle: "热更新当前拦截规则；profile 默认规则请在 Profiles 页保存。",
    index: "04",
  },
  logs: {
    title: "日志",
    subtitle: "当前进程的实时日志，不含请求正文或响应正文。",
    index: "05",
  },
};

const defaultProfileForm: ProfileFormState = {
  name: "",
  listen_host: "100.115.235.115",
  listen_port: "4610",
  upstream_base_url: "",
  auth_mode: "passthrough",
  auth_env: "CODEX_RETRY_GATEWAY_UPSTREAM_API_KEY",
  auth_file: "",
  manual_secret: "",
  manual_secret_file: "",
  manual_secret_configured: false,
  auth_json_path: "",
  auth_json_key: "OPENAI_API_KEY",
  request_history_limit: "0",
  model_remap: "",
  reasoning_match_mode: "formula_518n_minus_2",
  reasoning_equals: "516,1034,1552",
  retryable_status_codes: "429,503",
  retryable_error_messages: "Selected model is at capacity. Please try a different model.\nstream disconnected before completion: Concurrency limit exceeded for account, please retry later",
  upstream_fetch_retry_attempts: "5",
  upstream_fetch_retry_backoff_ms: "350",
  endpoints: "/responses\n/chat/completions\n/v1/responses\n/v1/chat/completions",
};

const defaultImageProfileForm: ImageProfileFormState = {
  name: "",
  base_url: "",
  auth_mode: "fixed_bearer",
  auth_env: "CODEX_RETRY_GATEWAY_IMAGE_API_KEY",
  auth_file: "",
  manual_secret: "",
  manual_secret_file: "",
  manual_secret_configured: false,
  auth_json_path: "",
  auth_json_key: "OPENAI_API_KEY",
};

function numberFormat(value: unknown) {
  return typeof value === "number" && Number.isFinite(value)
    ? zhNumberFormatter.format(value)
    : "-";
}

function timestamp(value?: string | null) {
  if (!value) {
    return "-";
  }
  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? value : zhTimestampFormatter.format(date);
}

function durationSeconds(value?: number | null) {
  return typeof value === "number" && Number.isFinite(value) ? `${(value / 1000).toFixed(2)} s` : "-";
}

function compactDurationSeconds(value?: number | null) {
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
    return "?";
  }
  const seconds = value / 1000;
  if (seconds >= 100) {
    return `${seconds.toFixed(0)}s`;
  }
  return `${seconds.toFixed(1).replace(/\\.0$/, "")}s`;
}

function secondsSince(startedAt: string | null | undefined, updatedAt: string | null | undefined) {
  if (!startedAt || !updatedAt) {
    return "-";
  }
  const startedMs = new Date(startedAt).getTime();
  const updatedMs = new Date(updatedAt).getTime();
  if (Number.isNaN(startedMs) || Number.isNaN(updatedMs)) {
    return "-";
  }
  return `${Math.max(0, (updatedMs - startedMs) / 1000).toFixed(1)} s`;
}

function bytesFormat(value?: number | null) {
  if (typeof value !== "number" || !Number.isFinite(value)) {
    return "-";
  }
  if (value < 1024) {
    return `${value} B`;
  }
  if (value < 1024 * 1024) {
    return `${(value / 1024).toFixed(1)} KB`;
  }
  return `${(value / 1024 / 1024).toFixed(2)} MB`;
}

function lifecycleTone(value?: string | null) {
  if (value === "finish") {
    return "";
  }
  if (value === "receive_first") {
    return "warn";
  }
  return "pending";
}

function percent(value?: number) {
  return typeof value === "number" && Number.isFinite(value) ? `${(value * 100).toFixed(2)}%` : "0.00%";
}

function effectiveInputTokens(inputTokens?: number | null, cachedTokens?: number | null) {
  if (typeof inputTokens !== "number" || !Number.isFinite(inputTokens)) {
    return null;
  }
  const cached = typeof cachedTokens === "number" && Number.isFinite(cachedTokens) ? cachedTokens : 0;
  return Math.max(0, inputTokens - cached);
}

function cachedRatio(inputTokens?: number | null, cachedTokens?: number | null) {
  if (typeof inputTokens !== "number" || !Number.isFinite(inputTokens) || inputTokens <= 0) {
    return null;
  }
  const cached = typeof cachedTokens === "number" && Number.isFinite(cachedTokens) ? cachedTokens : 0;
  return Math.max(0, Math.min(1, cached / inputTokens));
}

function requestPrimaryId(entry: RequestEntry) {
  return entry.response_id || entry.request_id || "-";
}

function hasReasoningRetryInfo(entry: RequestEntry) {
  return Boolean(
    entry.reasoning_retry_enabled ||
      entry.reasoning_retry_thread_mode ||
      entry.reasoning_retry_query_count ||
      entry.reasoning_retry_round_count ||
      entry.reasoning_retry_stop_reason,
  );
}

function retryRoundWidthText(entry: RequestEntry) {
  const round = entry.reasoning_retry_current_round || entry.reasoning_retry_round_count || 0;
  const width = entry.reasoning_retry_current_width || 0;
  if (!round) {
    return "-";
  }
  return width ? `${numberFormat(round)}(${numberFormat(width)})` : numberFormat(round);
}

function retryFirstLabel(first: NonNullable<RequestEntry["reasoning_retry_current_firsts"]>[number], fallbackRound?: number | null) {
  const round = first.round ?? fallbackRound ?? null;
  const slot = first.slot ?? null;
  if (typeof round === "number" && Number.isInteger(round)) {
    if (typeof slot === "number" && Number.isInteger(slot) && slot > 1) {
      return `${numberFormat(round)}-${numberFormat(slot)}`;
    }
    return numberFormat(round);
  }
  if (typeof slot === "number" && Number.isInteger(slot)) {
    return `#${numberFormat(slot)}`;
  }
  return "?";
}

function retryFirstText(
  first: NonNullable<RequestEntry["reasoning_retry_current_firsts"]>[number],
  fallbackRound?: number | null,
) {
  const delay = durationSeconds(first.first_response_delay_ms);
  const outcome = first.outcome || "pending";
  const reasoning = typeof first.reasoning_tokens === "number" ? ` r${numberFormat(first.reasoning_tokens)}` : "";
  const status = typeof first.status_code === "number" ? ` ${first.status_code}` : "";
  return `${retryFirstLabel(first, fallbackRound)} ${delay} ${outcome}${status}${reasoning}`;
}

function retryFirstCompactText(
  first: NonNullable<RequestEntry["reasoning_retry_current_firsts"]>[number],
) {
  return compactDurationSeconds(first.first_response_delay_ms);
}

function formatRetryStopReason(value?: string | null) {
  const reason = `${value || ""}`.trim();
  if (!reason) {
    return "-";
  }
  const labels: Record<string, string> = {
    success: "成功",
    missing_thread_id: "缺少 thread_id",
    thread_guard_disabled: "thread 已关闭拦截",
    completed_without_retry: "未触发调度",
    reasoning_guard: "reasoning 命中",
    retryable_upstream_error: "上游可重试错误",
    fatal: "致命错误",
    exhausted_without_winner: "无赢家",
  };
  return labels[reason] || reason;
}

function formatRetryThreadMode(value?: string | null) {
  const mode = `${value || ""}`.trim();
  if (!mode || mode === "disabled") {
    return "未启用";
  }
  if (mode === "thread_guard_disabled") {
    return "thread 已关闭拦截";
  }
  if (mode === "thread_id") {
    return "按 thread_id";
  }
  if (mode === "missing_thread_id") {
    return "缺少 thread_id";
  }
  return mode;
}

function sortedReasoningCounts(value?: Record<string, number> | null) {
  return Object.entries(value || {}).sort((left, right) => {
    const countDelta = Number(right[1] || 0) - Number(left[1] || 0);
    if (countDelta !== 0) {
      return countDelta;
    }
    return Number(left[0] || 0) - Number(right[0] || 0);
  });
}

function formatThreadGuardOverride(value?: string | null) {
  const override = `${value || ""}`.trim();
  if (override === "disabled") {
    return "已关闭";
  }
  if (override === "enabled") {
    return "强制开启";
  }
  return "默认开启";
}

function threadGuardBadgeTone(value?: string | null) {
  const override = `${value || ""}`.trim();
  if (override === "disabled") {
    return "warn";
  }
  if (override === "enabled") {
    return "success";
  }
  return "";
}

function splitList(value: string) {
  return value
    .split(/[\s,]+/)
    .map((item) => item.trim())
    .filter(Boolean);
}

function splitLines(value: string) {
  return value
    .split(/\r?\n/)
    .map((item) => item.trim())
    .filter(Boolean);
}

function normalizeReasoningMode(value: unknown): ReasoningMatchMode {
  return value === "manual" ? "manual" : "formula_518n_minus_2";
}

function formatReasoningMode(mode: ReasoningMatchMode) {
  return mode === "manual" ? "manual" : "518n-2";
}

function formatReasoningRule(mode: ReasoningMatchMode, reasoningEquals?: string) {
  if (mode === "manual") {
    return reasoningEquals || "-";
  }
  return "516, 1034, 1552, ...";
}

async function fetchJson<T>(url: string, options?: RequestInit): Promise<T> {
  const headers = new Headers(options?.headers || {});
  const accessKey = window.localStorage.getItem(ACCESS_KEY_STORAGE_KEY)?.trim();
  if (accessKey && !headers.has("x-codex-retry-gateway-key")) {
    headers.set("x-codex-retry-gateway-key", accessKey);
  }
  const response = await fetch(url, { cache: "no-store", ...options, headers });
  const payload = await response.json();
  if (!response.ok) {
    throw new Error(payload?.error?.message || "请求失败");
  }
  return payload as T;
}

function profileFormFromStatus(status: StatusPayload | null): ProfileFormState {
  const config = status?.config || {};
  return {
    ...defaultProfileForm,
    listen_host: config.listen_host || defaultProfileForm.listen_host,
    listen_port: String(config.listen_port || defaultProfileForm.listen_port),
    upstream_base_url: config.upstream_base_url || "",
    auth_mode: (config.upstream_auth_mode as ProfileFormState["auth_mode"]) || "passthrough",
    auth_env: config.upstream_auth_env || defaultProfileForm.auth_env,
    manual_secret: "",
    manual_secret_file: "",
    manual_secret_configured: false,
    auth_json_key: config.upstream_auth_json_key || defaultProfileForm.auth_json_key,
    request_history_limit: String(config.request_history_limit ?? defaultProfileForm.request_history_limit),
    model_remap: config.model_remap || "",
    reasoning_match_mode: normalizeReasoningMode(config.reasoning_match_mode),
    reasoning_equals: Array.isArray(config.reasoning_equals)
      ? config.reasoning_equals.join(",")
      : defaultProfileForm.reasoning_equals,
    retryable_status_codes: Array.isArray(config.retryable_status_codes)
      ? config.retryable_status_codes.join(",")
      : defaultProfileForm.retryable_status_codes,
    retryable_error_messages: Array.isArray(config.retryable_error_messages)
      ? config.retryable_error_messages.join("\n")
      : defaultProfileForm.retryable_error_messages,
    upstream_fetch_retry_attempts: String(
      config.upstream_fetch_retry_attempts ?? defaultProfileForm.upstream_fetch_retry_attempts,
    ),
    upstream_fetch_retry_backoff_ms: String(
      config.upstream_fetch_retry_backoff_ms ?? defaultProfileForm.upstream_fetch_retry_backoff_ms,
    ),
    endpoints: Array.isArray(config.endpoints) ? config.endpoints.join("\n") : defaultProfileForm.endpoints,
  };
}

function profileFormFromProfile(profile: Profile): ProfileFormState {
  const form = profile.form || {};
  return {
    ...defaultProfileForm,
    name: profile.name,
    listen_host: form.listen_host || "",
    listen_port: form.listen_port || "",
    upstream_base_url: form.upstream_base_url || "",
    auth_mode: (form.auth_mode as ProfileFormState["auth_mode"]) || "passthrough",
    auth_env: form.auth_env || "",
    auth_file: form.auth_file || "",
    manual_secret: "",
    manual_secret_file: form.manual_secret_file || "",
    manual_secret_configured: Boolean(form.manual_secret_configured),
    auth_json_path: form.auth_json_path || "",
    auth_json_key: form.auth_json_key || "OPENAI_API_KEY",
    request_history_limit: form.request_history_limit || defaultProfileForm.request_history_limit,
    model_remap: form.model_remap || "",
    reasoning_match_mode: normalizeReasoningMode(form.reasoning_match_mode),
    reasoning_equals: form.reasoning_equals || defaultProfileForm.reasoning_equals,
    retryable_status_codes: form.retryable_status_codes || defaultProfileForm.retryable_status_codes,
    retryable_error_messages: Array.isArray(form.retryable_error_messages)
      ? form.retryable_error_messages.join("\n")
      : defaultProfileForm.retryable_error_messages,
    upstream_fetch_retry_attempts:
      form.upstream_fetch_retry_attempts || defaultProfileForm.upstream_fetch_retry_attempts,
    upstream_fetch_retry_backoff_ms:
      form.upstream_fetch_retry_backoff_ms || defaultProfileForm.upstream_fetch_retry_backoff_ms,
    endpoints: Array.isArray(form.endpoints) ? form.endpoints.join("\n") : "",
  };
}

function imageProfileFormFromStatus(status: StatusPayload | null): ImageProfileFormState {
  const config = status?.config || {};
  return {
    ...defaultImageProfileForm,
    name: config.image_profile_name || "",
    base_url: config.image_base_url || "",
    auth_mode: (config.image_auth_mode as AuthMode) || defaultImageProfileForm.auth_mode,
    auth_env: config.image_auth_env || defaultImageProfileForm.auth_env,
    auth_json_key: config.image_auth_json_key || defaultImageProfileForm.auth_json_key,
  };
}

function imageProfileFormFromProfile(profile: ImageProfile): ImageProfileFormState {
  const form = profile.form || {};
  return {
    ...defaultImageProfileForm,
    name: profile.name,
    base_url: form.base_url || "",
    auth_mode: (form.auth_mode as AuthMode) || defaultImageProfileForm.auth_mode,
    auth_env: form.auth_env || defaultImageProfileForm.auth_env,
    auth_file: form.auth_file || "",
    manual_secret: "",
    manual_secret_file: form.manual_secret_file || "",
    manual_secret_configured: Boolean(form.manual_secret_configured),
    auth_json_path: form.auth_json_path || "",
    auth_json_key: form.auth_json_key || defaultImageProfileForm.auth_json_key,
  };
}

function ruleFormFromStatus(status: StatusPayload | null): RuleFormState {
  const config = status?.config || {};
  return {
    reasoning_match_mode: normalizeReasoningMode(config.reasoning_match_mode),
    reasoning_equals: Array.isArray(config.reasoning_equals) ? config.reasoning_equals.join(", ") : "",
    retryable_status_codes: Array.isArray(config.retryable_status_codes)
      ? config.retryable_status_codes.join(", ")
      : defaultProfileForm.retryable_status_codes,
    retryable_error_messages: Array.isArray(config.retryable_error_messages)
      ? config.retryable_error_messages.join("\n")
      : defaultProfileForm.retryable_error_messages,
    upstream_fetch_retry_attempts: String(
      config.upstream_fetch_retry_attempts ?? defaultProfileForm.upstream_fetch_retry_attempts,
    ),
    upstream_fetch_retry_backoff_ms: String(
      config.upstream_fetch_retry_backoff_ms ?? defaultProfileForm.upstream_fetch_retry_backoff_ms,
    ),
    endpoints: Array.isArray(config.endpoints) ? config.endpoints.join("\n") : "",
    non_stream_status_code: String(config.non_stream_status_code || 502),
    log_match: Boolean(config.log_match),
  };
}

export default function App() {
  useEffect(() => {
    const url = new URL(window.location.href);
    const accessKey = url.searchParams.get("key")?.trim();
    if (!accessKey) {
      return;
    }
    window.localStorage.setItem(ACCESS_KEY_STORAGE_KEY, accessKey);
    url.searchParams.delete("key");
    window.history.replaceState({}, "", url.toString());
  }, []);

  const [page, setPage] = useState<PageKey>(() => {
    const hash = window.location.hash.replace("#", "") as PageKey;
    return pageCopy[hash] ? hash : "overview";
  });
  const [status, setStatus] = useState<StatusPayload | null>(null);
  const [requests, setRequests] = useState<RequestEntry[]>([]);
  const [requestsTotal, setRequestsTotal] = useState(0);
  const [requestsMeta, setRequestsMeta] = useState("正在读取请求记录...");
  const [threadRules, setThreadRules] = useState<ThreadRuleEntry[]>([]);
  const [threadRulesMeta, setThreadRulesMeta] = useState("正在读取 thread 规则...");
  const [profiles, setProfiles] = useState<Profile[]>([]);
  const [profilesMeta, setProfilesMeta] = useState("正在读取 profiles...");
  const [imageProfiles, setImageProfiles] = useState<ImageProfile[]>([]);
  const [imageProfilesMeta, setImageProfilesMeta] = useState("正在读取图片 profiles...");
  const [logs, setLogs] = useState<string>("正在读取日志...");
  const [logsMeta, setLogsMeta] = useState("正在读取日志...");
  const [latestLogSeq, setLatestLogSeq] = useState(0);
  const [requestQuery, setRequestQuery] = useState("");
  const [requestFilter, setRequestFilter] = useState("all");
  const [requestLimit, setRequestLimit] = useState(REQUEST_PAGE_SIZE);
  const [ruleForm, setRuleForm] = useState<RuleFormState>(ruleFormFromStatus(null));
  const [profileForm, setProfileForm] = useState<ProfileFormState>(defaultProfileForm);
  const [imageProfileForm, setImageProfileForm] = useState<ImageProfileFormState>(defaultImageProfileForm);
  const [ruleMessage, setRuleMessage] = useState<MessageState>({ text: "", tone: "" });
  const [threadRuleMessage, setThreadRuleMessage] = useState<MessageState>({ text: "", tone: "" });
  const [profileMessage, setProfileMessage] = useState<MessageState>({ text: "", tone: "" });
  const [profileProbeResult, setProfileProbeResult] = useState<ProfileProbePayload | null>(null);
  const [imageProfileMessage, setImageProfileMessage] = useState<MessageState>({ text: "", tone: "" });
  const [imageProfileProbeResult, setImageProfileProbeResult] = useState<ProfileProbePayload | null>(null);
  const [probingProfile, setProbingProfile] = useState("");
  const [deletingProfile, setDeletingProfile] = useState("");
  const [switchingTo, setSwitchingTo] = useState("");
  const [probingImageProfile, setProbingImageProfile] = useState("");
  const [deletingImageProfile, setDeletingImageProfile] = useState("");
  const [switchingImageTo, setSwitchingImageTo] = useState("");
  const [restoreRequested, setRestoreRequested] = useState(false);
  const [shouldSyncProfileFormToActive, setShouldSyncProfileFormToActive] = useState(true);
  const [shouldSyncImageProfileFormToActive, setShouldSyncImageProfileFormToActive] = useState(true);
  const [threadRuleBusyThreadId, setThreadRuleBusyThreadId] = useState("");

  const metrics = status?.metrics || {};
  const tokens = metrics.token_totals || {};
  const currentPage = pageCopy[page];
  const effectiveInputTotal = effectiveInputTokens(tokens.input_tokens, tokens.cached_tokens);
  const cachedTotalRatio = cachedRatio(tokens.input_tokens, tokens.cached_tokens);

  const reasoningChips = Object.entries(metrics.observed_reasoning_counts || {}).sort(
    (left, right) => Number(right[1]) - Number(left[1]),
  );

  async function loadStatus(refreshRuleForm = false) {
    const payload = await fetchJson<StatusPayload>(api.status);
    startTransition(() => {
      setStatus(payload);
      if (refreshRuleForm) {
        setRuleForm(ruleFormFromStatus(payload));
        setProfileForm(profileFormFromStatus(payload));
        setImageProfileForm(imageProfileFormFromStatus(payload));
      }
    });
  }

  async function loadRequests(limitOverride = requestLimit) {
    const url = new URL(api.requests, window.location.origin);
    url.searchParams.set("limit", String(limitOverride));
    if (requestQuery.trim()) {
      url.searchParams.set("query", requestQuery.trim());
    }
    if (requestFilter !== "all") {
      url.searchParams.set("filter", requestFilter);
    }
    const payload = await fetchJson<RequestsPayload>(url.toString());
    const entries = payload.entries || [];
    startTransition(() => {
      setRequests(entries);
      setRequestsTotal(payload.total_entries ?? entries.length);
      setRequestsMeta(
        `查询命中 ${payload.total_entries ?? entries.length} 条，当前展示最近 ${entries.length} 条，最新序号 ${payload.latest_seq ?? 0}。`,
      );
    });
  }

  async function loadThreadRules() {
    const payload = await fetchJson<ThreadRulesPayload>(api.threadRules);
    const rules = payload.rules || [];
    startTransition(() => {
      setThreadRules(rules);
      setThreadRulesMeta(`当前覆盖 ${rules.length} 个 thread；文件：${payload.thread_rules_path || "-"}`);
    });
  }

  async function loadProfiles() {
    const [payload, imagePayload] = await Promise.all([
      fetchJson<ProfilesPayload>(api.profiles),
      fetchJson<ImageProfilesPayload>(api.imageProfiles),
    ]);
    const items = payload.profiles || [];
    const imageItems = imagePayload.image_profiles || [];
    startTransition(() => {
      setProfiles(items);
      setProfilesMeta(`目录：${payload.profiles_dir || "-"}；当前运行：${payload.active_profile || "-"}。`);
      setImageProfiles(imageItems);
      setImageProfilesMeta(`目录：${imagePayload.image_profiles_dir || "-"}；当前运行：${imagePayload.active_image_profile || "未配置"}。`);
    });
  }

  async function loadLogs(incremental = false) {
    const url = new URL(api.logs, window.location.origin);
    if (incremental && latestLogSeq > 0) {
      url.searchParams.set("since_seq", String(latestLogSeq));
    } else {
      url.searchParams.set("limit", String(LOG_PAGE_SIZE));
    }
    const payload = await fetchJson<LogsPayload>(url.toString());
    const rendered = (payload.entries || [])
      .map((entry) => `${entry.at || "-"} ${entry.message || ""}`)
      .join("\n");
    startTransition(() => {
      setLogs((current) => {
        if (!incremental || latestLogSeq === 0) {
          return rendered || "当前还没有日志。";
        }
        return rendered ? `${current.trim()}\n${rendered}` : current;
      });
      setLogsMeta(`已载入 ${payload.total_entries ?? payload.entries?.length ?? 0} 条日志，最新序号 ${payload.latest_seq ?? latestLogSeq}。`);
      if (typeof payload.latest_seq === "number") {
        setLatestLogSeq(payload.latest_seq);
      }
    });
  }

  async function loadPageData(targetPage: PageKey, { incrementalLogs = true } = {}) {
    if (targetPage === "requests") {
      await loadRequests();
      return;
    }
    if (targetPage === "rules") {
      await loadThreadRules();
      return;
    }
    if (targetPage === "profiles") {
      await loadProfiles();
      return;
    }
    if (targetPage === "logs") {
      await loadLogs(incrementalLogs && latestLogSeq > 0);
    }
  }

  async function refreshLiveData(targetPage: PageKey = page) {
    if (restoreRequested || switchingTo || switchingImageTo) {
      return;
    }
    const tasks: Array<Promise<unknown>> = [loadStatus(false)];
    if (targetPage !== "overview") {
      tasks.push(loadPageData(targetPage, { incrementalLogs: true }));
    }
    await Promise.all(tasks);
  }

  useEffect(() => {
    window.location.hash = page;
  }, [page]);

  useEffect(() => {
    if (page === "profiles") {
    setShouldSyncProfileFormToActive(true);
    setShouldSyncImageProfileFormToActive(true);
    }
  }, [page]);

  useEffect(() => {
    const onHashChange = () => {
      const next = window.location.hash.replace("#", "") as PageKey;
      if (pageCopy[next]) {
        setPage(next);
      }
    };
    window.addEventListener("hashchange", onHashChange);
    return () => window.removeEventListener("hashchange", onHashChange);
  }, []);

  useEffect(() => {
    loadStatus(true).catch((error) => {
      setRuleMessage({ text: error?.message || String(error), tone: "error" });
    });
  }, []);

  useEffect(() => {
    const timer = window.setInterval(() => {
      loadStatus(false).catch((error) => {
        setRuleMessage({ text: error?.message || String(error), tone: "error" });
      });
    }, 10000);
    return () => window.clearInterval(timer);
  }, [restoreRequested, switchingTo, switchingImageTo]);

  useEffect(() => {
    if (page !== "requests" && page !== "logs" && page !== "rules") {
      return;
    }
    const timer = window.setInterval(() => {
      loadPageData(page, { incrementalLogs: true }).catch((error) => {
        if (page === "rules") {
          setThreadRuleMessage({ text: error?.message || String(error), tone: "error" });
          return;
        }
        setRuleMessage({ text: error?.message || String(error), tone: "error" });
      });
    }, 2500);
    return () => window.clearInterval(timer);
  }, [page, requestQuery, requestFilter, latestLogSeq, requestLimit]);

  useEffect(() => {
    if (page === "overview" || page === "requests") {
      return;
    }
    loadPageData(page, { incrementalLogs: false }).catch((error) => {
      if (page === "rules") {
        setThreadRuleMessage({ text: error?.message || String(error), tone: "error" });
        return;
      }
      setRuleMessage({ text: error?.message || String(error), tone: "error" });
    });
  }, [page]);

  useEffect(() => {
    if (page !== "profiles" || !shouldSyncProfileFormToActive) {
      return;
    }
    const activeProfile = profiles.find((profile) => profile.active);
    if (!activeProfile) {
      return;
    }
    setProfileForm(profileFormFromProfile(activeProfile));
    setProfileMessage({ text: "", tone: "" });
    setShouldSyncProfileFormToActive(false);
  }, [page, profiles, shouldSyncProfileFormToActive]);

  useEffect(() => {
    if (page !== "profiles" || !shouldSyncImageProfileFormToActive) {
      return;
    }
    const activeProfile = imageProfiles.find((profile) => profile.active);
    if (!activeProfile) {
      return;
    }
    setImageProfileForm(imageProfileFormFromProfile(activeProfile));
    setImageProfileMessage({ text: "", tone: "" });
    setShouldSyncImageProfileFormToActive(false);
  }, [page, imageProfiles, shouldSyncImageProfileFormToActive]);

  async function saveRules(event: FormEvent) {
    event.preventDefault();
    setRuleMessage({ text: "正在保存配置...", tone: "" });
    try {
      const payload = await fetchJson<StatusPayload>(api.config, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          reasoning_match_mode: ruleForm.reasoning_match_mode,
          reasoning_equals: splitList(ruleForm.reasoning_equals)
            .map((value) => Number.parseInt(value, 10))
            .filter((value) => Number.isInteger(value)),
          retryable_status_codes: splitList(ruleForm.retryable_status_codes)
            .map((value) => Number.parseInt(value, 10))
            .filter((value) => Number.isInteger(value)),
          retryable_error_messages: splitLines(ruleForm.retryable_error_messages),
          upstream_fetch_retry_attempts: Number.parseInt(ruleForm.upstream_fetch_retry_attempts, 10),
          upstream_fetch_retry_backoff_ms: Number.parseInt(ruleForm.upstream_fetch_retry_backoff_ms, 10),
          endpoints: splitLines(ruleForm.endpoints),
          non_stream_status_code: Number.parseInt(ruleForm.non_stream_status_code, 10),
          log_match: ruleForm.log_match,
        }),
      });
      setStatus(payload);
      setRuleForm(ruleFormFromStatus(payload));
      setRuleMessage({ text: "配置已保存，并已对当前 gateway 立即生效。", tone: "success" });
    } catch (error) {
      setRuleMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    }
  }

  async function updateThreadRule(threadId: string, reasoningInterceptEnabled: boolean) {
    if (!threadId.trim()) {
      return;
    }
    setThreadRuleBusyThreadId(threadId);
    setThreadRuleMessage({
      text: reasoningInterceptEnabled ? `正在为 ${threadId} 开启拦截...` : `正在为 ${threadId} 关闭拦截...`,
      tone: "",
    });
    try {
      const payload = await fetchJson<ThreadRulesPayload>(api.threadRules, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          thread_id: threadId,
          reasoning_intercept_enabled: reasoningInterceptEnabled,
        }),
      });
      setThreadRules(payload.rules || []);
      setThreadRulesMeta(`当前覆盖 ${(payload.rules || []).length} 个 thread；文件：${payload.thread_rules_path || "-"}`);
      setThreadRuleMessage({
        text: payload.message || (reasoningInterceptEnabled ? "thread 已开启拦截" : "thread 已关闭拦截"),
        tone: "success",
      });
      await Promise.all([loadStatus(false), loadRequests(requestLimit), loadThreadRules()]);
    } catch (error) {
      setThreadRuleMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    } finally {
      setThreadRuleBusyThreadId("");
    }
  }

  async function restoreThreadRule(threadId: string) {
    if (!threadId.trim()) {
      return;
    }
    setThreadRuleBusyThreadId(threadId);
    setThreadRuleMessage({ text: `正在恢复 ${threadId} 的默认策略...`, tone: "" });
    try {
      const payload = await fetchJson<ThreadRulesPayload>(`${api.threadRules}/${encodeURIComponent(threadId)}`, {
        method: "DELETE",
      });
      setThreadRules(payload.rules || []);
      setThreadRulesMeta(`当前覆盖 ${(payload.rules || []).length} 个 thread；文件：${payload.thread_rules_path || "-"}`);
      setThreadRuleMessage({ text: payload.message || "thread 已恢复默认拦截策略", tone: "success" });
      await Promise.all([loadStatus(false), loadRequests(requestLimit), loadThreadRules()]);
    } catch (error) {
      setThreadRuleMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    } finally {
      setThreadRuleBusyThreadId("");
    }
  }

  useEffect(() => {
    setRequestLimit(REQUEST_PAGE_SIZE);
  }, [requestQuery, requestFilter]);

  useEffect(() => {
    if (page !== "requests") {
      return;
    }
    const timer = window.setTimeout(() => {
      loadRequests(requestLimit).catch((error) => {
        setRuleMessage({ text: error?.message || String(error), tone: "error" });
      });
    }, 180);
    return () => window.clearTimeout(timer);
  }, [page, requestQuery, requestFilter, requestLimit]);

  async function saveProfile(event: FormEvent) {
    event.preventDefault();
    setProfileMessage({ text: "正在保存 profile...", tone: "" });
    try {
      const payload = await fetchJson<ProfilesPayload>(api.profiles, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          name: profileForm.name,
          listen_host: profileForm.listen_host,
          listen_port: Number.parseInt(profileForm.listen_port, 10),
          upstream_base_url: profileForm.upstream_base_url,
          auth_mode: profileForm.auth_mode,
          auth_env: profileForm.auth_env,
          auth_file: profileForm.auth_file,
          manual_secret: profileForm.manual_secret,
          manual_secret_file: profileForm.manual_secret_file,
          manual_secret_configured: profileForm.manual_secret_configured,
          auth_json_path: profileForm.auth_json_path,
          auth_json_key: profileForm.auth_json_key,
          request_history_limit: Number.parseInt(profileForm.request_history_limit, 10),
          model_remap: profileForm.model_remap,
          reasoning_match_mode: profileForm.reasoning_match_mode,
          reasoning_equals: splitList(profileForm.reasoning_equals),
          retryable_status_codes: splitList(profileForm.retryable_status_codes),
          retryable_error_messages: splitLines(profileForm.retryable_error_messages),
          upstream_fetch_retry_attempts: Number.parseInt(profileForm.upstream_fetch_retry_attempts, 10),
          upstream_fetch_retry_backoff_ms: Number.parseInt(profileForm.upstream_fetch_retry_backoff_ms, 10),
          endpoints: splitLines(profileForm.endpoints),
        }),
      });
      setProfiles(payload.profiles || []);
      setProfilesMeta(`目录：${payload.profiles_dir || "-"}；当前运行：${payload.active_profile || "-"}。`);
      setProfileForm((current) => ({
        ...current,
        manual_secret: "",
        manual_secret_configured: current.auth_mode === "manual_bearer",
      }));
      setProfileMessage({
        text: payload.applied_profile ? "profile 已保存，并已对当前运行实例后台热应用。" : "profile 已保存。需要运行它时，点击左侧卡片里的“切换”。",
        tone: "success",
      });
    } catch (error) {
      setProfileMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    }
  }

  function editProfile(profile: Profile) {
    setShouldSyncProfileFormToActive(false);
    setProfileForm(profileFormFromProfile(profile));
    setProfileMessage({ text: "编辑后保存 profile env；如果保存的是当前运行 profile，后端会直接后台热应用。", tone: "" });
  }

  async function switchProfile(profile: Profile) {
    if (profile.active) {
      return;
    }
    if (!window.confirm(`切换到 profile "${profile.name}" 会尝试后台热切换，不重启当前 gateway。确定继续吗？`)) {
      return;
    }
    setSwitchingTo(profile.name);
    try {
      await fetchJson(api.profileSwitch, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ profile: profile.name }),
      });
      waitForProfile(profile.name);
    } catch (error) {
      setSwitchingTo("");
      setProfileMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    }
  }

  async function probeProfile(profile: Profile) {
    setProbingProfile(profile.name);
    setProfileMessage({ text: `正在探测 profile ${profile.name}...`, tone: "" });
    try {
      const payload = await fetchJson<ProfileProbePayload>(api.profileProbe, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          profile: profile.name,
          model: "gpt-5.5-fast",
          input: "ping",
        }),
      });
      setProfileProbeResult(payload);
      setProfileMessage({ text: `profile ${profile.name} 探针已完成，不影响当前运行实例。`, tone: "success" });
    } catch (error) {
      setProfileMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    } finally {
      setProbingProfile("");
    }
  }

  async function removeProfile(profile: Profile) {
    if (profile.active) {
      setProfileMessage({ text: "当前运行的 profile 不能直接删除；请先切换到其他 profile。", tone: "error" });
      return;
    }
    if (!window.confirm(`删除 profile "${profile.name}" 会移除对应 env 文件。确定继续吗？`)) {
      return;
    }
    setDeletingProfile(profile.name);
    try {
      const payload = await fetchJson<ProfilesPayload>(`${api.profiles}/${encodeURIComponent(profile.name)}`, {
        method: "DELETE",
      });
      setProfiles(payload.profiles || []);
      setProfilesMeta(`目录：${payload.profiles_dir || "-"}；当前运行：${payload.active_profile || "-"}。`);
      if (profileForm.name === profile.name) {
        setProfileForm(defaultProfileForm);
      }
      if (profileProbeResult?.profile === profile.name) {
        setProfileProbeResult(null);
      }
      setProfileMessage({ text: `profile ${profile.name} 已删除。`, tone: "success" });
    } catch (error) {
      setProfileMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    } finally {
      setDeletingProfile("");
    }
  }

  async function saveImageProfile(event: FormEvent) {
    event.preventDefault();
    setImageProfileMessage({ text: "正在保存图片 profile...", tone: "" });
    try {
      const payload = await fetchJson<ImageProfilesPayload>(api.imageProfiles, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          name: imageProfileForm.name,
          base_url: imageProfileForm.base_url,
          auth_mode: imageProfileForm.auth_mode,
          auth_env: imageProfileForm.auth_env,
          auth_file: imageProfileForm.auth_file,
          manual_secret: imageProfileForm.manual_secret,
          manual_secret_file: imageProfileForm.manual_secret_file,
          manual_secret_configured: imageProfileForm.manual_secret_configured,
          auth_json_path: imageProfileForm.auth_json_path,
          auth_json_key: imageProfileForm.auth_json_key,
        }),
      });
      setImageProfiles(payload.image_profiles || []);
      setImageProfilesMeta(`目录：${payload.image_profiles_dir || "-"}；当前运行：${payload.active_image_profile || "未配置"}。`);
      setImageProfileForm((current) => ({
        ...current,
        manual_secret: "",
        manual_secret_configured: current.auth_mode === "manual_bearer",
      }));
      setImageProfileMessage({
        text: payload.applied_image_profile
          ? "图片 profile 已保存，并已对当前运行实例后台热应用。"
          : "图片 profile 已保存。需要运行它时，点击左侧卡片里的“切换”。",
        tone: "success",
      });
    } catch (error) {
      setImageProfileMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    }
  }

  function editImageProfile(profile: ImageProfile) {
    setShouldSyncImageProfileFormToActive(false);
    setImageProfileForm(imageProfileFormFromProfile(profile));
    setImageProfileMessage({ text: "编辑后保存图片 profile env；如果保存的是当前运行 profile，后端会直接后台热应用。", tone: "" });
  }

  async function switchImageProfile(profile: ImageProfile) {
    if (profile.active) {
      return;
    }
    if (!window.confirm(`切换到图片 profile "${profile.name}" 会立即改写 /images/* 的上游，不重启 gateway。确定继续吗？`)) {
      return;
    }
    setSwitchingImageTo(profile.name);
    try {
      await fetchJson(api.imageProfileSwitch, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ profile: profile.name }),
      });
      waitForImageProfile(profile.name);
    } catch (error) {
      setSwitchingImageTo("");
      setImageProfileMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    }
  }

  async function probeImageProfile(profile: ImageProfile) {
    setProbingImageProfile(profile.name);
    setImageProfileMessage({ text: `正在探测图片 profile ${profile.name}...`, tone: "" });
    try {
      const payload = await fetchJson<ProfileProbePayload>(api.imageProfileProbe, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ profile: profile.name }),
      });
      setImageProfileProbeResult(payload);
      setImageProfileMessage({ text: `图片 profile ${profile.name} 探针已完成，不影响当前运行实例。`, tone: "success" });
    } catch (error) {
      setImageProfileMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    } finally {
      setProbingImageProfile("");
    }
  }

  async function removeImageProfile(profile: ImageProfile) {
    if (profile.active) {
      setImageProfileMessage({ text: "当前运行的图片 profile 不能直接删除；请先切换到其他图片 profile。", tone: "error" });
      return;
    }
    if (!window.confirm(`删除图片 profile "${profile.name}" 会移除对应 env 文件。确定继续吗？`)) {
      return;
    }
    setDeletingImageProfile(profile.name);
    try {
      const payload = await fetchJson<ImageProfilesPayload>(`${api.imageProfiles}/${encodeURIComponent(profile.name)}`, {
        method: "DELETE",
      });
      setImageProfiles(payload.image_profiles || []);
      setImageProfilesMeta(`目录：${payload.image_profiles_dir || "-"}；当前运行：${payload.active_image_profile || "未配置"}。`);
      if (imageProfileForm.name === profile.name) {
        setImageProfileForm(defaultImageProfileForm);
      }
      if (imageProfileProbeResult?.image_profile === profile.name) {
        setImageProfileProbeResult(null);
      }
      setImageProfileMessage({ text: `图片 profile ${profile.name} 已删除。`, tone: "success" });
    } catch (error) {
      setImageProfileMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    } finally {
      setDeletingImageProfile("");
    }
  }

  function waitForProfile(profileName: string) {
    const deadline = Date.now() + 12000;
    const tick = async () => {
      if (Date.now() > deadline) {
        setSwitchingTo("");
        setProfileMessage({ text: "热切换已提交，但暂时没有看到当前实例切到目标 profile。", tone: "error" });
        return;
      }
      try {
        const payload = await fetchJson<StatusPayload>(api.status);
        if (payload.config?.profile_name === profileName) {
          setSwitchingTo("");
          setShouldSyncProfileFormToActive(true);
          Promise.all([loadStatus(true), loadProfiles()]).catch(() => {});
          return;
        }
      } catch {
        // keep polling
      }
      window.setTimeout(tick, 500);
    };
    window.setTimeout(tick, 300);
  }

  function waitForImageProfile(profileName: string) {
    const deadline = Date.now() + 12000;
    const tick = async () => {
      if (Date.now() > deadline) {
        setSwitchingImageTo("");
        setImageProfileMessage({ text: "图片热切换已提交，但暂时没有看到当前实例切到目标 profile。", tone: "error" });
        return;
      }
      try {
        const payload = await fetchJson<StatusPayload>(api.status);
        if (payload.config?.image_profile_name === profileName) {
          setSwitchingImageTo("");
          setShouldSyncImageProfileFormToActive(true);
          Promise.all([loadStatus(true), loadProfiles()]).catch(() => {});
          return;
        }
      } catch {
        // keep polling
      }
      window.setTimeout(tick, 500);
    };
    window.setTimeout(tick, 300);
  }

  async function restoreConfig() {
    if (!window.confirm("恢复后会关闭当前 gateway，并把 Codex 配置切回原上游。确定继续吗？")) {
      return;
    }
    setRestoreRequested(true);
    setRuleMessage({ text: "正在触发恢复，页面很快会失联...", tone: "" });
    try {
      await fetchJson(api.restore, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({}),
      });
      setRuleMessage({ text: "恢复脚本已启动，等待 gateway 关闭。", tone: "success" });
    } catch (error) {
      setRestoreRequested(false);
      setRuleMessage({ text: error instanceof Error ? error.message : String(error), tone: "error" });
    }
  }

  return (
    <>
      <div className="app-shell">
        <aside className="sidebar">
          <div className="brand">
            <span className="eyebrow">Gateway Console</span>
            <h1>Codex Retry Gateway</h1>
            <p>分页面管理 profile、请求 token、拦截规则和运行日志。页面只记录元数据，不保存请求正文或响应正文。</p>
          </div>

          <nav className="nav" aria-label="Gateway pages">
            {(Object.keys(pageCopy) as PageKey[]).map((key) => (
              <button
                key={key}
                type="button"
                data-active={page === key}
                onClick={() => setPage(key)}
              >
                {pageCopy[key].title} <span>{pageCopy[key].index}</span>
              </button>
            ))}
          </nav>

          <SideCard label="当前 Profile" value={status?.config?.profile_name || "-"} strong />
          <SideCard label="当前图片 Profile" value={status?.config?.image_profile_name || "未配置"} />
          <SideCard label="监听地址" value={status?.listen || "-"} />
          <SideCard label="Auth 模式" value={status?.config?.upstream_auth_mode || "-"} />
        </aside>

        <main className="content">
          <header className="topbar">
            <div>
              <h2>{currentPage.title}</h2>
              <p>{currentPage.subtitle}</p>
            </div>
            <div className="toolbar">
              <button className="ghost" type="button" onClick={() => refreshLiveData()}>
                刷新
              </button>
              <a className="badge" href={api.status}>
                status api
              </a>
            </div>
          </header>

          {page === "overview" && (
            <section className="page" data-active="true">
              <div className="grid">
                <Card
                  title="运行态"
                  hint="配置、provider、Codex 当前 base_url 和 profile 状态。"
                  badge={status?.ok ? "online" : "读取中"}
                >
                  <div className="stat-grid">
                    <Stat label="Profile" value={status?.config?.profile_name || "-"} />
                    <Stat label="代理请求" value={numberFormat(metrics.total_proxy_request_count || 0)} />
                    <Stat label="检查响应" value={numberFormat(metrics.inspected_response_count || 0)} />
                    <Stat label="规则命中" value={numberFormat(metrics.matched_response_count || 0)} />
                  </div>
                </Card>

                <Card title="Token Totals" hint="按持久请求记录累计；重启后会从 requests history 回灌。">
                  <div className="token-strip">
                    <TokenCard label="Input" value={numberFormat(effectiveInputTotal ?? 0)} />
                    <TokenCard label="Output" value={numberFormat(tokens.output_tokens || 0)} />
                    <TokenCard label="Total" value={numberFormat(tokens.total_tokens || 0)} />
                    <TokenCard label="Reasoning" value={numberFormat(tokens.reasoning_tokens || 0)} />
                    <TokenCard
                      label="Cached"
                      value={`${numberFormat(tokens.cached_tokens || 0)}${cachedTotalRatio !== null ? ` (${percent(cachedTotalRatio)})` : ""}`}
                    />
                  </div>
                </Card>

                <div className="grid two">
                  <Card title="连接信息">
                    <InfoList
                      rows={[
                        ["监听地址", status?.listen || "-"],
                        ["文本上游", status?.config?.upstream_base_url || "-"],
                        ["文本认证", status?.config?.upstream_auth_mode || "passthrough"],
                        ["图片 Profile", status?.config?.image_profile_name || "未配置"],
                        ["图片上游", status?.config?.image_base_url || "未配置"],
                        ["图片认证", status?.config?.image_base_url ? status?.config?.image_auth_mode || "fixed_bearer" : "未启用"],
                        ["Provider", status?.state?.provider_name || "未检测到安装状态"],
                        ["Codex Base URL", status?.state?.codex_current_base_url || "-"],
                      ]}
                    />
                  </Card>

                  <Card title="规则观察">
                    <InfoList
                      rows={[
                        ["516 命中", numberFormat(metrics.reasoning_516_count || 0)],
                        ["516 占比", percent(metrics.reasoning_516_ratio || 0)],
                        ["累计起点", timestamp(metrics.persistent_since || metrics.started_at)],
                        ["本次启动", timestamp(metrics.started_at)],
                        ["Config", status?.paths?.config_path || "-"],
                        ["备份", status?.state?.latest_backup_path || "-"],
                      ]}
                    />
                    <div className="chips">
                      {reasoningChips.length === 0 ? (
                        <span className="chip">还没有 reasoning 观测</span>
                      ) : (
                        <>
                          {reasoningChips.map(([reasoning, count]) => (
                            <span className="chip" key={reasoning}>
                              reasoning {reasoning}: {count}
                            </span>
                          ))}
                          {(metrics.observed_reasoning_counts_omitted || 0) > 0 ? (
                            <span className="chip">
                              其余 {metrics.observed_reasoning_counts_omitted} 项未展开
                            </span>
                          ) : null}
                        </>
                      )}
                    </div>
                  </Card>
                </div>
              </div>
            </section>
          )}

          {page === "requests" && (
            <section className="page" data-active="true">
              <Card
                title="请求记录"
                hint={requestsMeta}
                actions={
                  <div className="toolbar compact-toolbar">
                    <input
                      type="search"
                      value={requestQuery}
                      placeholder="搜索模型 / 路径 / 状态"
                      onChange={(event) => setRequestQuery(event.target.value)}
                    />
                    <select value={requestFilter} onChange={(event) => setRequestFilter(event.target.value)}>
                      <option value="all">全部请求</option>
                      <option value="matched">规则命中</option>
                      <option value="stream">流式响应</option>
                      <option value="error">有错误</option>
                    </select>
                    <button className="secondary" type="button" onClick={() => loadRequests()}>
                      刷新请求
                    </button>
                    <button
                      className="ghost"
                      type="button"
                      disabled={requests.length >= requestsTotal && requestsTotal > 0}
                      onClick={() => setRequestLimit((current) => current + REQUEST_PAGE_SIZE)}
                    >
                      更多
                    </button>
                  </div>
                }
              >
                <div className="request-list">
                  {requests.length === 0 ? (
                    <div className="empty-state">没有匹配的请求记录。</div>
                  ) : (
                    requests.map((entry) => {
                      const usage = entry.usage || {};
                      const upstream = entry.upstream || {};
                      const statusTone = entry.error ? "error" : entry.matched ? "warn" : "";
                      const effectiveInput = effectiveInputTokens(usage.input_tokens, usage.cached_tokens);
                      const cachedHitRatio = cachedRatio(usage.input_tokens, usage.cached_tokens);
                      const showReasoningRetry = hasReasoningRetryInfo(entry);
                      const retryReasoningCounts = sortedReasoningCounts(entry.reasoning_retry_extra_reasoning_counts);
                      const retryCurrentFirsts = Array.isArray(entry.reasoning_retry_current_firsts)
                        ? entry.reasoning_retry_current_firsts
                        : [];
                      const threadRuleBusy = Boolean(entry.thread_id) && threadRuleBusyThreadId === entry.thread_id;
                      return (
                        <article className="request-card" key={entry.seq}>
                          <div className="request-head">
                            <div>
                              <div className="request-title">
                                <span className={`badge ${statusTone}`}>{entry.status_code ?? "-"}</span>
                                <strong>{entry.profile_name || status?.config?.profile_name || "-"}</strong>
                                <span className={`badge ${lifecycleTone(entry.lifecycle_state)}`}>{entry.lifecycle_state || "sent"}</span>
                                <span className="meta-pill meta-sent">
                                  <span className="meta-key">发</span>
                                  {timestamp(entry.started_at)}
                                </span>
                                {retryCurrentFirsts.length > 0 ? (
                                  <span className="meta-pill meta-first meta-first-list-pill">
                                    <span className="meta-key">首</span>
                                    {retryCurrentFirsts.map((first, index) => (
                                      <span className="meta-first-mini" key={`${first.round || entry.reasoning_retry_current_round || "r"}-${first.slot || index}`}>
                                        {retryFirstCompactText(first)}
                                      </span>
                                    ))}
                                  </span>
                                ) : (
                                  <span className="meta-pill meta-first">
                                    <span className="meta-key">首</span>
                                    {durationSeconds(entry.first_response_delay_ms)}
                                  </span>
                                )}
                                <span className="meta-pill meta-total">
                                  <span className="meta-key">总</span>
                                  {durationSeconds(entry.duration_ms)}
                                </span>
                                <span className="meta-pill meta-progress">
                                  <span className="meta-key">更</span>
                                  {timestamp(entry.last_activity_at || entry.usage_last_updated_at)}
                                </span>
                                <span className="meta-pill meta-body">
                                  <span className="meta-key">体</span>
                                  {bytesFormat(entry.request_body_bytes)}
                                </span>
                                <span className="meta-pill meta-stream">
                                  <span className="meta-key">流</span>
                                  {entry.response_stream
                                    ? `${numberFormat(entry.stream_chunk_count)} chunk / ${bytesFormat(entry.response_bytes_received)} / ${secondsSince(entry.started_at, entry.last_activity_at || entry.usage_last_updated_at || entry.finished_at)}`
                                    : "-"}
                                </span>
                                {showReasoningRetry ? (
                                  <span className="meta-pill meta-retry">
                                    <span className="meta-key">重打</span>
                                    {`${retryRoundWidthText(entry)} / ${numberFormat(entry.reasoning_retry_query_count || 0)}q`}
                                  </span>
                                ) : null}
                                <span className="meta-pill meta-received">
                                  <span className="meta-key">收</span>
                                  {timestamp(entry.finished_at)}
                                </span>
                              </div>
                              <div className="request-subtitle">
                                <code>{`${entry.method || "-"} ${entry.path || "-"}`}</code>
                                <span className="hint">{entry.response_stream ? "stream" : "non-stream"}</span>
                                <span className="hint">id {requestPrimaryId(entry)}</span>
                                <span className="hint">thread {entry.thread_id || "-"}</span>
                              </div>
                            </div>
                            <div className="request-badges">
                              {entry.matched ? <span className="badge warn">matched</span> : <span className="badge">pass</span>}
                              {entry.error ? <span className="badge error">error</span> : null}
                              <span className={`badge ${threadGuardBadgeTone(entry.reasoning_guard_thread_override)}`}>
                                guard {formatThreadGuardOverride(entry.reasoning_guard_thread_override)}
                              </span>
                              {showReasoningRetry ? (
                                <span className={`badge ${entry.reasoning_retry_stop_reason === "success" ? "success" : "warn"}`}>
                                  retry {formatRetryStopReason(entry.reasoning_retry_stop_reason)}
                                </span>
                              ) : null}
                            </div>
                          </div>

                          <div className="request-grid">
                            <div className="request-block">
                              <label>模型</label>
                              <code>{entry.requested_model || entry.model || "-"}</code>
                              <span className="hint">
                                {entry.forwarded_model && entry.forwarded_model !== entry.requested_model
                                  ? `转发为 ${entry.forwarded_model}`
                                  : entry.forwarded_model || "-"}
                              </span>
                              <span className="hint">
                                {entry.reasoning_effort
                                  ? `强度 ${entry.reasoning_effort}`
                                  : "强度 -"}
                                {entry.reasoning_summary ? ` / summary ${entry.reasoning_summary}` : ""}
                              </span>
                            </div>

                            <div className="request-block">
                              <label>{upstream.route === "images" ? "图片上游" : "文本上游"}</label>
                              <code>{`${upstream.origin || "-"}${upstream.path || ""}`}</code>
                              <span className="hint">
                                {upstream.auth_mode || "-"} / {upstream.auth_source || "-"}
                                {upstream.authorization_configured === false ? " / no auth" : ""}
                              </span>
                              <span className="hint">
                                重试 {numberFormat(entry.upstream_attempt_count)} 次
                                {entry.upstream_status_code ? ` / upstream ${entry.upstream_status_code}` : ""}
                              </span>
                            </div>

                              <div className="request-block">
                                <label>Token</label>
                                <div className="request-token-row">
                                  <span className="token-pill token-in">in {numberFormat(effectiveInput)}</span>
                                  <span className="token-pill token-out">out {numberFormat(usage.output_tokens)}</span>
                                  <span className="token-pill token-cached">cached {numberFormat(usage.cached_tokens)}{cachedHitRatio !== null ? ` (${percent(cachedHitRatio)})` : ""}</span>
                                  <span className="token-pill token-reasoning">reasoning {numberFormat(entry.reasoning_tokens ?? usage.reasoning_tokens)}</span>
                                  <span className="token-pill token-total">total {numberFormat(usage.total_tokens)}</span>
                                </div>
                              </div>

                              <div className="request-block">
                                <label>IDs</label>
                                <code>{`response ${entry.response_id || "-"}`}</code>
                                <span className="hint">{`request ${entry.request_id || "-"}`}</span>
                                <span className="hint">{`thread ${entry.thread_id || "-"}`}</span>
                                {entry.thread_id ? (
                                  <div className="thread-rule-actions">
                                    <button
                                      className="ghost"
                                      type="button"
                                      disabled={threadRuleBusy || entry.reasoning_guard_thread_override === "enabled"}
                                      onClick={() => updateThreadRule(entry.thread_id || "", true)}
                                    >
                                      {threadRuleBusy && entry.reasoning_guard_thread_override !== "enabled" ? "处理中" : "开启拦截"}
                                    </button>
                                    <button
                                      className="ghost"
                                      type="button"
                                      disabled={threadRuleBusy || entry.reasoning_guard_thread_override === "disabled"}
                                      onClick={() => updateThreadRule(entry.thread_id || "", false)}
                                    >
                                      {threadRuleBusy && entry.reasoning_guard_thread_override !== "disabled" ? "处理中" : "关闭拦截"}
                                    </button>
                                    {entry.reasoning_guard_thread_override && entry.reasoning_guard_thread_override !== "default" ? (
                                      <button
                                        className="secondary"
                                        type="button"
                                        disabled={threadRuleBusy}
                                        onClick={() => restoreThreadRule(entry.thread_id || "")}
                                      >
                                        {threadRuleBusy ? "处理中" : "恢复默认"}
                                      </button>
                                    ) : null}
                                  </div>
                                ) : null}
                              </div>

                              {showReasoningRetry ? (
                                <div className="request-block reasoning-retry-block">
                                  <label>Reasoning Retry</label>
                                  <code>{formatRetryThreadMode(entry.reasoning_retry_thread_mode)}</code>
                                  <span className="hint">
                                    guard {formatThreadGuardOverride(entry.reasoning_guard_thread_override)}
                                  </span>
                                  <span className="hint">
                                    schedule 1,1,2,2,4,4... / query {numberFormat(entry.reasoning_retry_query_count || 0)}
                                    {" / "}
                                    round {retryRoundWidthText(entry)}
                                  </span>
                                  <span className="hint">
                                    winner {entry.reasoning_retry_winner_round && entry.reasoning_retry_winner_slot
                                      ? `round ${entry.reasoning_retry_winner_round} slot ${entry.reasoning_retry_winner_slot}`
                                      : "-"}
                                    {" / "}
                                    stop {formatRetryStopReason(entry.reasoning_retry_stop_reason)}
                                  </span>
                                  <span className="hint">
                                    extra matched {numberFormat(entry.reasoning_retry_extra_matched_count || 0)}
                                    {" / "}
                                    inspected {numberFormat(entry.reasoning_retry_extra_inspected_count || 0)}
                                    {" / "}
                                    extra reasoning {numberFormat(entry.reasoning_retry_extra_usage?.reasoning_tokens)}
                                  </span>
                                  {retryReasoningCounts.length > 0 ? (
                                    <div className="retry-chip-row">
                                      {retryReasoningCounts.slice(0, 4).map(([reasoning, count]) => (
                                        <span className="chip compact-chip" key={reasoning}>
                                          {reasoning}: {numberFormat(count)}
                                        </span>
                                      ))}
                                    </div>
                                  ) : null}
                                  {retryCurrentFirsts.length > 0 ? (
                                    <div className="retry-first-list" aria-label="current retry wave first responses">
                                      {retryCurrentFirsts.map((first, index) => (
                                        <span
                                          className={`retry-first-chip ${first.first_response_delay_ms == null ? "pending" : ""}`}
                                          key={`${first.round || entry.reasoning_retry_current_round || "r"}-${first.slot || index}`}
                                          title={`first ${timestamp(first.first_response_at)} / upstream ${first.upstream_status_code ?? "-"}`}
                                        >
                                          {retryFirstText(first, entry.reasoning_retry_current_round)}
                                        </span>
                                      ))}
                                    </div>
                                  ) : null}
                                </div>
                              ) : null}
                          </div>
                        </article>
                      );
                    })
                  )}
                </div>
              </Card>
            </section>
          )}

          {page === "profiles" && (
            <section className="page" data-active="true">
              <div className="grid profile-layout">
                <Card
                  title="文本 Profile 列表"
                  hint={profilesMeta}
                  actions={
                    <button
                      className="secondary"
                      type="button"
                      onClick={() => {
                        setShouldSyncProfileFormToActive(false);
                        setProfileForm(profileFormFromStatus(status));
                        setProfileMessage({ text: "", tone: "" });
                      }}
                    >
                      新建文本 Profile
                    </button>
                  }
                >
                  <div className="grid profile-list-grid">
                    {profiles.length === 0 ? (
                      <div className="profile-card">当前还没有 profile env 文件。</div>
                    ) : (
                      profiles.map((profile) => (
                        <article className="profile-card compact-profile-card" data-active-profile={profile.active} key={profile.name}>
                          <div className="profile-head">
                            <div className="profile-meta">
                              <h3>{profile.name}</h3>
                              <div className="profile-path hint">{profile.file_path || ""}</div>
                            </div>
                            <div className="profile-actions">
                              {profile.active ? <span className="badge">当前运行</span> : <span className="badge warn">可切换</span>}
                              <button className="ghost" type="button" onClick={() => editProfile(profile)}>
                                编辑
                              </button>
                              <button className="ghost" type="button" disabled={probingProfile === profile.name} onClick={() => probeProfile(profile)}>
                                {probingProfile === profile.name ? "探测中" : "探针"}
                              </button>
                              <button className="danger" type="button" disabled={deletingProfile === profile.name} onClick={() => removeProfile(profile)}>
                                {deletingProfile === profile.name ? "删除中" : "删除"}
                              </button>
                              <button className="primary" type="button" disabled={profile.active} onClick={() => switchProfile(profile)}>
                                切换
                              </button>
                            </div>
                          </div>
                          <div className="mini-stats">
                            <MiniStat label="监听" value={`${profile.summary?.listen_host || "-"}:${profile.summary?.listen_port || "-"}`} />
                            <MiniStat label="文本上游" value={profile.summary?.upstream_base_url || "-"} />
                            <MiniStat label="文本认证" value={`${profile.summary?.auth_mode || "passthrough"} / ${profile.summary?.auth_source || "-"}`} />
                            <MiniStat label="History" value={profile.summary?.request_history_limit || "0"} />
                            <MiniStat label="Model Remap" value={profile.summary?.model_remap || "-"} />
                            <MiniStat
                              label="Reasoning"
                              value={formatReasoningRule(
                                normalizeReasoningMode(profile.summary?.reasoning_match_mode),
                                profile.summary?.reasoning_equals,
                              )}
                            />
                            <MiniStat
                              label="Rule Mode"
                              value={formatReasoningMode(normalizeReasoningMode(profile.summary?.reasoning_match_mode))}
                            />
                          </div>
                        </article>
                      ))
                    )}
                  </div>
                </Card>

                <Card title={profileForm.name ? `编辑文本 ${profileForm.name}` : "编辑文本 Profile"} hint="手动 token/password 只能写入系统 secret 文件，保存后不会从后端读回或展示。">
                  <form onSubmit={saveProfile}>
                    <Field label="Profile 名称">
                      <input value={profileForm.name} placeholder="default 或 sub2api-auth-json" onChange={(event) => setProfileForm({ ...profileForm, name: event.target.value })} />
                    </Field>
                    <div className="field-row">
                      <Field label="监听 Host">
                        <input value={profileForm.listen_host} placeholder="100.115.235.115" onChange={(event) => setProfileForm({ ...profileForm, listen_host: event.target.value })} />
                      </Field>
                      <Field label="监听 Port">
                        <input type="number" min={1} max={65535} value={profileForm.listen_port} onChange={(event) => setProfileForm({ ...profileForm, listen_port: event.target.value })} />
                      </Field>
                    </div>
                    <Field label="文本上游 Base URL">
                      <input value={profileForm.upstream_base_url} placeholder="https://example.com/v1" onChange={(event) => setProfileForm({ ...profileForm, upstream_base_url: event.target.value })} />
                    </Field>
                    <Field label="Model Remap" hint="每行一个 from=to，也支持用分号分隔；例如 gpt-5.5-fast=gpt-5.5;gpt-5.4-fast=gpt-5.4。切换 profile 后会自动把请求模型改写为目标 provider 可用模型。">
                      <textarea value={profileForm.model_remap} onChange={(event) => setProfileForm({ ...profileForm, model_remap: event.target.value })} />
                    </Field>
                    <Field label="文本 Auth 模式">
                      <select value={profileForm.auth_mode} onChange={(event) => setProfileForm({ ...profileForm, auth_mode: event.target.value as ProfileFormState["auth_mode"] })}>
                        <option value="passthrough">passthrough：透传 Codex Authorization</option>
                        <option value="manual_bearer">manual_bearer：手动写入系统 secret 文件</option>
                        <option value="fixed_bearer">fixed_bearer：从 env/file 读取 Bearer</option>
                        <option value="auth_json">auth_json：从 ~/.codex/auth.json 读取字段</option>
                      </select>
                    </Field>
                    {profileForm.auth_mode === "manual_bearer" && (
                      <Field
                        label="文本 Token / Password"
                        hint={profileForm.manual_secret_configured ? "系统中已有 secret；留空表示保留现有值，填入新值会覆盖写入。" : "只写入系统 secret 文件，保存后不会显示或返回。"}
                      >
                        <input
                          type="password"
                          value={profileForm.manual_secret}
                          autoComplete="new-password"
                          placeholder={profileForm.manual_secret_configured ? "已配置；留空保留" : "输入一次 token/password"}
                          onChange={(event) => setProfileForm({ ...profileForm, manual_secret: event.target.value })}
                        />
                      </Field>
                    )}
                    {profileForm.auth_mode === "fixed_bearer" && (
                      <>
                        <Field label="文本 Token 环境变量名" hint="只填变量名，不填 token 值。">
                          <input value={profileForm.auth_env} placeholder="CODEX_RETRY_GATEWAY_UPSTREAM_API_KEY" onChange={(event) => setProfileForm({ ...profileForm, auth_env: event.target.value })} />
                        </Field>
                        <Field label="文本 Token 文件路径">
                          <input value={profileForm.auth_file} placeholder="/run/secrets/provider-token" onChange={(event) => setProfileForm({ ...profileForm, auth_file: event.target.value })} />
                        </Field>
                      </>
                    )}
                    {profileForm.auth_mode === "auth_json" && (
                      <>
                        <Field label="文本 auth.json 路径">
                          <input value={profileForm.auth_json_path} placeholder="留空表示 ~/.codex/auth.json" onChange={(event) => setProfileForm({ ...profileForm, auth_json_path: event.target.value })} />
                        </Field>
                        <Field label="文本 auth.json 字段名" hint="这里填 JSON key，例如 OPENAI_API_KEY，不粘贴 sk- 开头的密钥值。">
                          <input value={profileForm.auth_json_key} placeholder="OPENAI_API_KEY" onChange={(event) => setProfileForm({ ...profileForm, auth_json_key: event.target.value })} />
                        </Field>
                      </>
                    )}
                    <Field label="History Limit" hint="0 表示不裁剪；正整数表示后台持久化记录的保留上限。">
                      <input
                        type="number"
                        min={0}
                        value={profileForm.request_history_limit}
                        onChange={(event) => setProfileForm({ ...profileForm, request_history_limit: event.target.value })}
                      />
                    </Field>
                    <Field label="reasoning_match_mode" hint="`518n-2` 会命中 516、1034、1552 等；`manual` 则只按下面的列表拦截。">
                      <select
                        value={profileForm.reasoning_match_mode}
                        onChange={(event) =>
                          setProfileForm({ ...profileForm, reasoning_match_mode: normalizeReasoningMode(event.target.value) })
                        }
                      >
                        <option value="formula_518n_minus_2">formula_518n_minus_2</option>
                        <option value="manual">manual</option>
                      </select>
                    </Field>
                    <Field label="reasoning_equals">
                      <input value={profileForm.reasoning_equals} placeholder="516,1034,1552" onChange={(event) => setProfileForm({ ...profileForm, reasoning_equals: event.target.value })} />
                    </Field>
                    <Field label="retryable_status_codes">
                      <input value={profileForm.retryable_status_codes} placeholder="429,503" onChange={(event) => setProfileForm({ ...profileForm, retryable_status_codes: event.target.value })} />
                    </Field>
                    <Field label="retryable_error_messages" hint="每行一条错误文案；上游 JSON 错误包含任一行时会转成本地重试状态码。">
                      <textarea value={profileForm.retryable_error_messages} onChange={(event) => setProfileForm({ ...profileForm, retryable_error_messages: event.target.value })} />
                    </Field>
                    <div className="field-row">
                      <Field label="upstream_fetch_retry_attempts">
                        <input type="number" min={1} value={profileForm.upstream_fetch_retry_attempts} onChange={(event) => setProfileForm({ ...profileForm, upstream_fetch_retry_attempts: event.target.value })} />
                      </Field>
                      <Field label="upstream_fetch_retry_backoff_ms">
                        <input type="number" min={0} step={50} value={profileForm.upstream_fetch_retry_backoff_ms} onChange={(event) => setProfileForm({ ...profileForm, upstream_fetch_retry_backoff_ms: event.target.value })} />
                      </Field>
                    </div>
                    <Field label="endpoints">
                      <textarea value={profileForm.endpoints} onChange={(event) => setProfileForm({ ...profileForm, endpoints: event.target.value })} />
                    </Field>
                    <div className="toolbar">
                      <button className="primary" type="submit">
                        保存 Profile
                      </button>
                      <button className="ghost" type="button" onClick={() => setProfileForm(profileFormFromStatus(status))}>
                        清空表单
                      </button>
                    </div>
                    <Message message={profileMessage} />
                  </form>
                </Card>

                <Card title="文本 Profile 探针" hint="不切换当前 gateway，只对选定文本 profile 做上游连通性与最小请求探测。默认测试模型是 gpt-5.5-fast，若配置了 remap，会显示实际转发模型。">
                  {profileProbeResult ? (
                    <div className="grid">
                      <InfoList
                        rows={[
                          ["Profile", profileProbeResult.profile || "-"],
                          ["上游", profileProbeResult.upstream_base_url || "-"],
                          ["Auth", `${profileProbeResult.auth_mode || "-"} / ${profileProbeResult.auth_source || "-"}`],
                          ["Model Remap", profileProbeResult.model_remap || "-"],
                        ]}
                      />
                      {(profileProbeResult.probes || []).map((probe) => (
                        <div className="profile-card" key={`${probe.kind}-${probe.target}`}>
                          <div className="profile-head">
                            <div>
                              <h3>{probe.kind || "probe"}</h3>
                              <div className="hint">{probe.target || "-"}</div>
                            </div>
                            <span className="badge">{probe.status ?? "-"}</span>
                          </div>
                          <div className="mini-stats">
                            <MiniStat label="请求模型" value={probe.requested_model || "-"} />
                            <MiniStat label="转发模型" value={probe.forwarded_model || "-"} />
                            <MiniStat label="Content-Type" value={probe.content_type || "-"} />
                          </div>
                          <pre className="log-output">{probe.body_preview || "无返回摘要"}</pre>
                        </div>
                      ))}
                    </div>
                  ) : (
                    <div className="hint">先在左侧 profile 卡片点击“探针”，这里会显示不切换情况下的探测结果。</div>
                  )}
                </Card>

                <Card
                  title="图片 Profile 列表"
                  hint={imageProfilesMeta}
                  actions={
                    <button
                      className="secondary"
                      type="button"
                      onClick={() => {
                        setShouldSyncImageProfileFormToActive(false);
                        setImageProfileForm(defaultImageProfileForm);
                        setImageProfileMessage({ text: "", tone: "" });
                      }}
                    >
                      新建图片 Profile
                    </button>
                  }
                >
                  <div className="grid profile-list-grid">
                    {imageProfiles.length === 0 ? (
                      <div className="profile-card">当前还没有图片 profile env 文件。</div>
                    ) : (
                      imageProfiles.map((profile) => (
                        <article className="profile-card compact-profile-card" data-active-profile={profile.active} key={profile.name}>
                          <div className="profile-head">
                            <div className="profile-meta">
                              <h3>{profile.name}</h3>
                              <div className="profile-path hint">{profile.file_path || ""}</div>
                            </div>
                            <div className="profile-actions">
                              {profile.active ? <span className="badge">当前运行</span> : <span className="badge warn">可切换</span>}
                              <button className="ghost" type="button" onClick={() => editImageProfile(profile)}>
                                编辑
                              </button>
                              <button className="ghost" type="button" disabled={probingImageProfile === profile.name} onClick={() => probeImageProfile(profile)}>
                                {probingImageProfile === profile.name ? "探测中" : "探针"}
                              </button>
                              <button className="danger" type="button" disabled={deletingImageProfile === profile.name} onClick={() => removeImageProfile(profile)}>
                                {deletingImageProfile === profile.name ? "删除中" : "删除"}
                              </button>
                              <button className="primary" type="button" disabled={profile.active} onClick={() => switchImageProfile(profile)}>
                                切换
                              </button>
                            </div>
                          </div>
                          <div className="mini-stats">
                            <MiniStat label="图片上游" value={profile.summary?.base_url || "回退文本上游"} />
                            <MiniStat label="图片认证" value={`${profile.summary?.auth_mode || "fixed_bearer"} / ${profile.summary?.auth_source || "-"}`} />
                          </div>
                        </article>
                      ))
                    )}
                  </div>
                </Card>

                <Card title={imageProfileForm.name ? `编辑图片 ${imageProfileForm.name}` : "编辑图片 Profile"} hint="图片 API key 只写入系统 secret 文件，保存后不会从后端读回或展示。">
                  <form onSubmit={saveImageProfile}>
                    <Field label="图片 Profile 名称">
                      <input value={imageProfileForm.name} placeholder="image-primary" onChange={(event) => setImageProfileForm({ ...imageProfileForm, name: event.target.value })} />
                    </Field>
                    <Field label="图片上游 Base URL" hint="留空时 /images/* 和 /v1/images/* 回退到当前文本上游。">
                      <input value={imageProfileForm.base_url} placeholder="https://image-provider.example/v1" onChange={(event) => setImageProfileForm({ ...imageProfileForm, base_url: event.target.value })} />
                    </Field>
                    <Field label="图片 Auth 模式">
                      <select value={imageProfileForm.auth_mode} onChange={(event) => setImageProfileForm({ ...imageProfileForm, auth_mode: event.target.value as AuthMode })}>
                        <option value="passthrough">passthrough：透传 Codex Authorization</option>
                        <option value="manual_bearer">manual_bearer：手动写入系统 secret 文件</option>
                        <option value="fixed_bearer">fixed_bearer：从 env/file 读取 Bearer</option>
                        <option value="auth_json">auth_json：从 ~/.codex/auth.json 读取字段</option>
                      </select>
                    </Field>
                    {imageProfileForm.auth_mode === "manual_bearer" && (
                      <Field
                        label="图片 API Key"
                        hint={imageProfileForm.manual_secret_configured ? "系统中已有图片 key；留空表示保留，填入新值会覆盖写入。" : "只写入系统 secret 文件，保存后不会显示或返回。"}
                      >
                        <input
                          type="password"
                          value={imageProfileForm.manual_secret}
                          autoComplete="new-password"
                          placeholder={imageProfileForm.manual_secret_configured ? "已配置；留空保留" : "输入一次图片 API key"}
                          onChange={(event) => setImageProfileForm({ ...imageProfileForm, manual_secret: event.target.value })}
                        />
                      </Field>
                    )}
                    {imageProfileForm.auth_mode === "fixed_bearer" && (
                      <>
                        <Field label="图片 Key 环境变量名" hint="只填变量名，不填 key 值。">
                          <input value={imageProfileForm.auth_env} placeholder="CODEX_RETRY_GATEWAY_IMAGE_API_KEY" onChange={(event) => setImageProfileForm({ ...imageProfileForm, auth_env: event.target.value })} />
                        </Field>
                        <Field label="图片 Key 文件路径">
                          <input value={imageProfileForm.auth_file} placeholder="/run/secrets/image-provider-token" onChange={(event) => setImageProfileForm({ ...imageProfileForm, auth_file: event.target.value })} />
                        </Field>
                      </>
                    )}
                    {imageProfileForm.auth_mode === "auth_json" && (
                      <>
                        <Field label="图片 auth.json 路径">
                          <input value={imageProfileForm.auth_json_path} placeholder="留空表示 ~/.codex/auth.json" onChange={(event) => setImageProfileForm({ ...imageProfileForm, auth_json_path: event.target.value })} />
                        </Field>
                        <Field label="图片 auth.json 字段名">
                          <input value={imageProfileForm.auth_json_key} placeholder="OPENAI_API_KEY" onChange={(event) => setImageProfileForm({ ...imageProfileForm, auth_json_key: event.target.value })} />
                        </Field>
                      </>
                    )}
                    <div className="toolbar">
                      <button className="primary" type="submit">
                        保存图片 Profile
                      </button>
                      <button className="ghost" type="button" onClick={() => setImageProfileForm(defaultImageProfileForm)}>
                        清空表单
                      </button>
                    </div>
                    <Message message={imageProfileMessage} />
                  </form>
                </Card>

                <Card title="图片 Profile 探针" hint="不切换当前 gateway，只对选定图片 profile 请求 /v1/models 检查连通性和认证。">
                  {imageProfileProbeResult ? (
                    <div className="grid">
                      <InfoList
                        rows={[
                          ["图片 Profile", imageProfileProbeResult.image_profile || "-"],
                          ["上游", imageProfileProbeResult.image_base_url || "-"],
                          ["Auth", `${imageProfileProbeResult.auth_mode || "-"} / ${imageProfileProbeResult.auth_source || "-"}`],
                        ]}
                      />
                      {(imageProfileProbeResult.probes || []).map((probe) => (
                        <div className="profile-card" key={`${probe.kind}-${probe.target}`}>
                          <div className="profile-head">
                            <div>
                              <h3>{probe.kind || "probe"}</h3>
                              <div className="hint">{probe.target || "-"}</div>
                            </div>
                            <span className="badge">{probe.status ?? "-"}</span>
                          </div>
                          <div className="mini-stats">
                            <MiniStat label="Content-Type" value={probe.content_type || "-"} />
                          </div>
                          <pre className="log-output">{probe.body_preview || "无返回摘要"}</pre>
                        </div>
                      ))}
                    </div>
                  ) : (
                    <div className="hint">先在图片 profile 卡片点击“探针”，这里会显示不切换情况下的探测结果。</div>
                  )}
                </Card>
              </div>
            </section>
          )}

          {page === "rules" && (
            <section className="page" data-active="true">
              <div className="grid rules-layout">
                <Card title="当前运行规则" hint="保存后会热生效，只影响当前正在运行的 gateway config；长期 profile 默认值请去 Profiles 页保存。">
                  <form onSubmit={saveRules}>
                    <Field label="reasoning_match_mode" hint="`518n-2` 会命中 516、1034、1552 等；切到 `manual` 时才只按手写列表判断。">
                      <select
                        value={ruleForm.reasoning_match_mode}
                        onChange={(event) =>
                          setRuleForm({ ...ruleForm, reasoning_match_mode: normalizeReasoningMode(event.target.value) })
                        }
                      >
                        <option value="formula_518n_minus_2">formula_518n_minus_2</option>
                        <option value="manual">manual</option>
                      </select>
                    </Field>
                    <Field label="reasoning_equals">
                      <input value={ruleForm.reasoning_equals} placeholder="例如：516, 1034, 1552" onChange={(event) => setRuleForm({ ...ruleForm, reasoning_equals: event.target.value })} />
                    </Field>
                    <Field label="retryable_status_codes">
                      <input value={ruleForm.retryable_status_codes} placeholder="例如：429, 503" onChange={(event) => setRuleForm({ ...ruleForm, retryable_status_codes: event.target.value })} />
                    </Field>
                    <Field label="retryable_error_messages" hint="每行一条错误文案；命中后会转成本地 non_stream_status_code。">
                      <textarea value={ruleForm.retryable_error_messages} onChange={(event) => setRuleForm({ ...ruleForm, retryable_error_messages: event.target.value })} />
                    </Field>
                    <div className="field-row">
                      <Field label="upstream_fetch_retry_attempts">
                        <input type="number" min={1} value={ruleForm.upstream_fetch_retry_attempts} onChange={(event) => setRuleForm({ ...ruleForm, upstream_fetch_retry_attempts: event.target.value })} />
                      </Field>
                      <Field label="upstream_fetch_retry_backoff_ms">
                        <input type="number" min={0} step={50} value={ruleForm.upstream_fetch_retry_backoff_ms} onChange={(event) => setRuleForm({ ...ruleForm, upstream_fetch_retry_backoff_ms: event.target.value })} />
                      </Field>
                    </div>
                    <Field label="endpoints">
                      <textarea value={ruleForm.endpoints} onChange={(event) => setRuleForm({ ...ruleForm, endpoints: event.target.value })} />
                    </Field>
                    <div className="field-row">
                      <Field label="non_stream_status_code">
                        <input type="number" min={100} max={599} value={ruleForm.non_stream_status_code} onChange={(event) => setRuleForm({ ...ruleForm, non_stream_status_code: event.target.value })} />
                      </Field>
                      <label className="inline-toggle">
                        <input type="checkbox" checked={ruleForm.log_match} onChange={(event) => setRuleForm({ ...ruleForm, log_match: event.target.checked })} />
                        命中时写日志
                      </label>
                    </div>
                    <div className="toolbar">
                      <button className="primary" type="submit">
                        保存并立即生效
                      </button>
                      <button className="danger" type="button" onClick={restoreConfig}>
                        恢复 Codex 原设置并关闭网关
                      </button>
                    </div>
                    <Message message={ruleMessage} />
                  </form>
                </Card>

                <Card title="Thread 覆盖" hint={threadRulesMeta}>
                  <div className="thread-rule-list">
                    {threadRules.length === 0 ? (
                      <div className="empty-state">当前没有按 thread_id 的拦截覆盖，全部走默认规则。</div>
                    ) : (
                      threadRules.map((rule) => {
                        const busy = threadRuleBusyThreadId === rule.thread_id;
                        return (
                          <article className="thread-rule-card" key={rule.thread_id}>
                            <div className="thread-rule-head">
                              <div>
                                <strong>{rule.thread_id}</strong>
                                <div className="hint">更新于 {timestamp(rule.updated_at)}</div>
                              </div>
                              <span className={`badge ${threadGuardBadgeTone(rule.reasoning_intercept_enabled ? "enabled" : "disabled")}`}>
                                {rule.reasoning_intercept_enabled ? "强制开启" : "已关闭"}
                              </span>
                            </div>
                            <div className="thread-rule-actions">
                              <button className="ghost" type="button" disabled={busy || rule.reasoning_intercept_enabled} onClick={() => updateThreadRule(rule.thread_id, true)}>
                                {busy && !rule.reasoning_intercept_enabled ? "处理中" : "开启拦截"}
                              </button>
                              <button className="ghost" type="button" disabled={busy || !rule.reasoning_intercept_enabled} onClick={() => updateThreadRule(rule.thread_id, false)}>
                                {busy && rule.reasoning_intercept_enabled ? "处理中" : "关闭拦截"}
                              </button>
                              <button className="secondary" type="button" disabled={busy} onClick={() => restoreThreadRule(rule.thread_id)}>
                                {busy ? "处理中" : "恢复默认"}
                              </button>
                            </div>
                          </article>
                        );
                      })
                    )}
                  </div>
                  <Message message={threadRuleMessage} />
                </Card>
              </div>
            </section>
          )}

          {page === "logs" && (
            <section className="page" data-active="true">
              <Card
                title="实时日志"
                hint={logsMeta}
                actions={
                  <button className="secondary" type="button" onClick={() => loadLogs(false)}>
                    刷新日志
                  </button>
                }
              >
                <pre className="log-output">{logs}</pre>
              </Card>
            </section>
          )}
        </main>
      </div>

      <div className="switch-overlay" data-active={Boolean(switchingTo)}>
        <div className="switch-dialog">
          <span className="eyebrow">Profile Switch</span>
          <h2>正在切换到 {switchingTo}</h2>
          <p className="muted">gateway 正在后台热切换当前运行配置，页面会自动等待目标 profile 生效。</p>
          <div className="progress-line">
            <span />
          </div>
        </div>
      </div>
    </>
  );
}

function Card({
  title,
  hint,
  badge,
  actions,
  children,
}: {
  title: string;
  hint?: string;
  badge?: string;
  actions?: React.ReactNode;
  children: React.ReactNode;
}) {
  return (
    <div className="card">
      <div className="card-inner">
        <div className="card-head">
          <div>
            <h3>{title}</h3>
            {hint ? <div className="hint">{hint}</div> : null}
          </div>
          {badge ? <span className="badge">{badge}</span> : actions}
        </div>
        {children}
      </div>
    </div>
  );
}

function Field({ label, hint, children }: { label: string; hint?: string; children: React.ReactNode }) {
  return (
    <label className="field">
      <span>{label}</span>
      {children}
      {hint ? <span className="hint">{hint}</span> : null}
    </label>
  );
}

function Stat({ label, value }: { label: string; value: string }) {
  return (
    <div className="stat">
      <label>{label}</label>
      <strong>{value}</strong>
    </div>
  );
}

function TokenCard({ label, value }: { label: string; value: string }) {
  return (
    <div className="token-card">
      <label>{label}</label>
      <strong>{value}</strong>
    </div>
  );
}

function InfoList({ rows }: { rows: Array<[string, string]> }) {
  return (
    <div className="info-list">
      {rows.map(([label, value]) => (
        <div className="info-row" key={label}>
          <label>{label}</label>
          <span>{value}</span>
        </div>
      ))}
    </div>
  );
}

function MiniStat({ label, value }: { label: string; value: string }) {
  return (
    <div className="mini-stat">
      <label>{label}</label>
      <span>{value}</span>
    </div>
  );
}

function SideCard({ label, value, strong = false }: { label: string; value: string; strong?: boolean }) {
  return (
    <div className="side-card">
      <label>{label}</label>
      {strong ? <strong>{value}</strong> : <span>{value}</span>}
    </div>
  );
}

function Message({ message }: { message: MessageState }) {
  return (
    <div className="message" data-tone={message.tone}>
      {message.text}
    </div>
  );
}
