async function loadDashboard(force = false, background = false, showOverlay = true) {
  const requestedDate = elements.tradeDate.value;
  if (state.dashboardLoading && state.dashboardRequestDate === requestedDate) return;
  state.dashboardLoading = true;
  state.dashboardRequestDate = requestedDate;
  const requestSequence = ++state.dashboardRequestSequence;
  if (force) stockPreviewCache.clear();
  if (!background && showOverlay) {
    setLoading(true, "正在加载市场数据");
    setStatus("正在加载市场数据");
  } else if (!background) {
    setStatus("正在刷新行情");
  }
  try {
    const query = new URLSearchParams({ trade_date: elements.tradeDate.value });
    if (force) query.set("force", "1");
    const payload = await apiRequest(`/api/dashboard?${query}`);
    if (
      requestSequence !== state.dashboardRequestSequence
      || requestedDate !== elements.tradeDate.value
    ) return;
    applyDashboard(payload, background);
  } catch (error) {
    if (background) {
      setStatus("实时刷新暂时中断，正在等待重试");
    } else {
      showToast(error.message || "无法连接本地服务");
      setStatus("加载失败");
    }
  } finally {
    if (requestSequence === state.dashboardRequestSequence) {
      state.dashboardLoading = false;
      state.dashboardRequestDate = "";
      if (!background && showOverlay) setLoading(false);
      updateDateButtons();
    }
  }
}

async function startAdminRefresh() {
  const buttons = [document.querySelector("#syncButton"), document.querySelector("#adminRefreshButton")].filter(Boolean);
  buttons.forEach((button) => { button.disabled = true; });
  const requestedDate = elements.tradeDate.value;
  setAdminRefreshStatus("running", `正在刷新 ${requestedDate} 的行情，请稍候…`, "loader-circle");
  try {
    const payload = await apiRequest("/api/admin/refresh", "POST", { trade_date: requestedDate });
    if (!payload.started || !payload.job_key) {
      setAdminRefreshStatus("warning", "已有刷新任务正在运行，请稍后再试。", "clock-3");
      showToast(payload.message || "已有后台刷新任务正在运行");
      return;
    }
    setStatus(`正在刷新 ${requestedDate} 的行情`);
    const job = await waitForAdminRefresh(payload.job_key);
    if (job.status === "failed") {
      const reason = job.message || job.error_code || "数据源未返回结果";
      setAdminRefreshStatus("failure", `刷新失败：${reason}`, "circle-x");
      setStatus("后台刷新失败");
      showToast("后台刷新失败");
      return;
    }
    const query = new URLSearchParams({ trade_date: requestedDate });
    const dashboard = await apiRequest(`/api/dashboard?${query}`);
    applyDashboard(dashboard);
    const meta = dashboard.meta || {};
    const actualDate = String(meta.trade_date || "").slice(0, 10);
    const requestedCompact = requestedDate.replaceAll("-", "");
    const actualCompact = actualDate.replaceAll("-", "");
    const updated = formatTimestamp(meta.updated_at);
    const freshness = dashboardFreshnessMessage(meta);
    if (freshness || actualCompact !== requestedCompact || meta.carried_forward || meta.limit_data_source === "derived") {
      setAdminRefreshStatus("warning", freshness || `部分正式数据尚未到齐，当前展示 ${actualDate || "最近可用数据"}`, "triangle-alert");
      setStatus(freshness || "部分正式数据尚未到齐，当前展示最近可用数据");
      return;
    }
    if (meta.notice) {
      setAdminRefreshStatus("warning", `已刷新到 ${actualDate}（${updated}），但数据源提示：${meta.notice}`, "triangle-alert");
      showToast(`已刷新到 ${actualDate}，请留意数据源提示`);
    } else {
      setAdminRefreshStatus("success", `刷新成功：已获取 ${actualDate} 的最新行情，更新时间 ${updated}`, "circle-check");
      showToast(`刷新成功：已获取 ${actualDate} 的最新行情`);
    }
  } catch (error) {
    const message = error.message || "后台刷新失败";
    setAdminRefreshStatus("failure", `刷新失败：${message}`, "circle-x");
    setStatus("后台刷新失败");
    showToast(message);
  } finally {
    buttons.forEach((button) => { button.disabled = false; });
  }
}

function setAdminRefreshStatus(tone, message, icon = "circle-dot") {
  const status = document.querySelector("#adminRefreshStatus");
  if (!status) return;
  status.dataset.tone = tone;
  status.innerHTML = `<i data-lucide="${icon}"></i><span>${escapeHtml(message)}</span>`;
  refreshIcons();
}

async function waitForAdminRefresh(jobKey) {
  for (let attempt = 0; attempt < 120; attempt += 1) {
    const payload = await apiRequest("/api/admin/settings");
    const job = (payload.data?.jobs || []).find((item) => item.idempotency_key === jobKey);
    if (job && ["success", "failed"].includes(job.status)) return job;
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
  throw new Error("刷新等待超时，请稍后重试");
}

let dashboardCatchupTimer = 0;

function chineseMonthDay(value) {
  const compact = String(value || "").replaceAll("-", "").replaceAll("/", "");
  if (!/^\d{8}/.test(compact)) return "";
  return `${Number(compact.slice(4, 6))} 月 ${Number(compact.slice(6, 8))} 日`;
}

function dashboardFreshnessMessage(meta = {}) {
  if (meta.display_notice) return String(meta.display_notice);
  const requested = String(meta.requested_date || "").replaceAll("-", "");
  const actual = String(meta.trade_date || "").replaceAll("-", "");
  const shown = chineseMonthDay(actual);
  if (meta.data_status === "preparing" || (meta.carried_forward && actual && requested && actual !== requested)) {
    return shown ? `今日数据正在准备，当前展示 ${shown}` : "今日数据正在准备，当前展示最近可用数据";
  }
  if (meta.data_status === "partial" || meta.limit_data_source === "derived") {
    return meta.notice || "部分正式数据尚未到齐，当前展示日线推算结果";
  }
  return "";
}

function scheduleDashboardCatchup(meta = {}) {
  window.clearTimeout(dashboardCatchupTimer);
  const status = String(meta.data_status || "");
  if (status !== "preparing" && status !== "partial") return;
  dashboardCatchupTimer = window.setTimeout(() => {
    loadDashboard(false, true, false);
  }, 60000);
}

function applyDashboard(payload, background = false) {
  state.dashboard = payload;
  const selectedDate = payload.meta.requested_date || payload.meta.trade_date;
  elements.tradeDate.value = selectedDate;
  document.querySelector("#qiObservationDate").value = selectedDate;
  document.querySelector("#journalDate").value = selectedDate;
  renderDashboard();
  const freshness = dashboardFreshnessMessage(payload.meta || {});
  setStatus(freshness || `${dashboardSourceLabel(payload.meta)} · 数据已更新`);
  const updatedAt = document.querySelector("#updatedAt");
  if (updatedAt) updatedAt.dataset.tone = freshness ? "warning" : "ok";
  scheduleDashboardCatchup(payload.meta || {});
  if (!background) {
    if (state.activeView === "dragonView") loadDragonTiger();
    if (state.activeView === "screenerView") loadScreenerSetup();
    if (state.activeView === "screenerTrackingView") loadScreenerTracking(true);
    if (state.activeView === "mentorView") loadMentorSetup(true);
    if (state.activeView === "heavenView") loadHeavenSetup(true);
    if (state.activeView === "sentimentCycleView") loadSentimentHistory(true);
    if (state.activeView === "rotationView") loadRotationHistory(true);
    if (state.activeView === "auctionView") loadAuctionCenter(true);
    if (state.activeView === "themeLibraryView") loadThemeLibrary(true);
    if (state.activeView === "popularityView") loadPopularity(true);
  }
  const requestedStock = new URLSearchParams(window.location.search).get("stock");
  if (!state.initialStockOpened && /^\d{6}$/.test(requestedStock || "")) {
    state.initialStockOpened = true;
    openStock(requestedStock);
  }
}

function dashboardSourceLabel(meta = {}) {
  if (meta.realtime && !["closed", "after_hours"].includes(String(meta.market_status || ""))) return "盘中行情";
  if (meta.carried_forward) return "最近收盘行情";
  if (meta.market_status === "historical") return "历史行情";
  return "收盘行情";
}

function renderDashboard() {
  const { meta, overview, ladders, sectors } = state.dashboard;
  animateMetric("tapeUp", overview.up_count, (value) => Math.round(value));
  animateMetric("tapeDown", overview.down_count, (value) => Math.round(value));
  animateMetric("tapeLimit", overview.limit_up_count, (value) => `${Math.round(value)} 家`);
  animateMetric("tapeLimitDown", overview.limit_down_count, (value) => `${Math.round(value)} 家`);
  animateMetric("detailBroken", overview.broken_count, (value) => `${Math.round(value)} 家`);
  animateMetric("detailSealRate", overview.seal_rate, (value) => `${formatNumber(value, 1)}%`);
  animateMetric("tapeAmount", overview.amount_billion, (value) => `${formatNumber(value, 1)} 亿`);
  animateMetric("limitUpMetric", overview.limit_up_count, (value) => `${Math.round(value)} 家`);
  animateMetric("limitDownMetric", overview.limit_down_count, (value) => `${Math.round(value)} 家`);
  animateMetric("brokenMetric", overview.broken_count, (value) => `${Math.round(value)} 家`);
  animateMetric("sealRateMetric", overview.seal_rate, (value) => `${formatNumber(value, 1)}%`);
  animateMetric("amountMetric", overview.amount_billion, (value) => `${formatNumber(value, 1)} 亿`);
  setText("dataDateMetric", dashboardDataTimestamp(meta));
  animateMetric("sentimentScore", overview.sentiment_score, (value) => Math.round(value));
  animateMetric("detailSentimentScore", overview.sentiment_score, (value) => Math.round(value));
  const moodLabel = sentimentLabel(overview.sentiment_score);
  setText("sentimentText", moodLabel);
  setText("detailSentimentText", moodLabel);
  document.querySelectorAll("#sentimentText, #detailSentimentText").forEach((sentimentChip) => {
    sentimentChip.classList.toggle("is-hot", moodLabel === "情绪高涨");
    sentimentChip.classList.toggle("is-strong", moodLabel === "情绪偏强");
    sentimentChip.classList.toggle("is-cold", moodLabel === "情绪冰点" || moodLabel === "情绪偏弱");
  });
  const pageSubtitle = document.querySelector("#currentPageSubtitle");
  const activePage = window.XiaobaiPages?.get(state.activeView);
  if (pageSubtitle) {
    const dateText = displayCompactDate(meta.trade_date);
    if (activePage?.id === "mentorView") {
      pageSubtitle.textContent = dateText === "--"
        ? "与不同交易思维模型持续对话 · 数据日期 --"
        : `与不同交易思维模型持续对话 · 数据日期 ${dateText}`;
    } else {
      const groupLabel = activePage?.group === "market"
        ? "市场复盘"
        : activePage?.group === "personal" ? "个人" : "智能工具";
      pageSubtitle.textContent = dateText === "--" ? groupLabel : `${groupLabel} · ${dateText}`;
    }
  }
  updateSentimentGauge(overview.sentiment_score);
  const freshness = dashboardFreshnessMessage(meta);
  setText("updatedAt", freshness
    ? freshness
    : `${dashboardSourceLabel(meta)} · 更新 ${formatTimestamp(meta.updated_at)}`);
  const updatedAt = document.querySelector("#updatedAt");
  if (updatedAt) updatedAt.dataset.tone = freshness ? "warning" : "ok";

  renderLimitTable();
  renderLadderMini(ladders || []);
  renderSectorMini(sectors || []);
  renderBrokenTable(state.dashboard.broken || []);
  renderDownTable(state.dashboard.down_limits || []);
  renderYesterdayTable(state.dashboard.yesterday_limits || []);
  renderPerformance(state.dashboard.limit_performance || []);
  renderLadderBoard(ladders || []);
  renderRotationMembers();
}


function shiftDate(delta) {
  const current = parseLocalDate(elements.tradeDate.value);
  current.setDate(current.getDate() + delta);
  const next = localDateString(current);
  if (next > todayString()) return;
  elements.tradeDate.value = next;
  state.heavenManualData = null;
  document.querySelector("#qiObservationDate").value = next;
  loadDashboard();
}

function updateDateButtons() {
  document.querySelector("#nextDate").disabled = elements.tradeDate.value >= todayString();
}



function sentimentLabel(score) {
  const value = number(score);
  if (value >= 80) return "情绪高涨";
  if (value >= 60) return "情绪偏强";
  if (value >= 40) return "情绪中性";
  if (value >= 20) return "情绪偏弱";
  return "情绪冰点";
}


function dashboardDataTimestamp(meta = {}) {
  const tradeDate = displayCompactDate(meta.trade_date);
  if (tradeDate === "--") return "--";
  const intraday = tradeDate === todayString() && Boolean(meta.realtime) && !["closed", "after_hours"].includes(String(meta.market_status || ""));
  if (intraday) {
    const updated = new Date(meta.updated_at);
    if (!Number.isNaN(updated.getTime())) {
      const dateText = `${updated.getFullYear()}-${String(updated.getMonth() + 1).padStart(2, "0")}-${String(updated.getDate()).padStart(2, "0")}`;
      const timeText = updated.toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit", hour12: false });
      return `${dateText} ${timeText}`;
    }
  }
  return `${tradeDate} 15:00`;
}


function updateSentimentGauge(rawScore) {
  const gauge = document.querySelector("#sentimentGauge");
  if (!gauge) return;
  const score = clamp(rawScore, 0, 100);
  const previous = Number(gauge.dataset.score);
  gauge.dataset.score = String(score);
  gauge.style.setProperty("--score", score);
  if (!motionEnabled() || !Number.isFinite(previous) || Math.abs(previous - score) < 15) return;
  gauge.classList.remove("sentiment-pulse");
  void gauge.offsetWidth;
  gauge.classList.add("sentiment-pulse");
  gauge.addEventListener("animationend", () => gauge.classList.remove("sentiment-pulse"), { once: true });
}

function bindDashboardEvents() {
  document.querySelector("#refreshButton").addEventListener("click", async (event) => {
    const button = event.currentTarget;
    button.disabled = true;
    try {
      await loadDashboard(false, false, false);
    } finally {
      button.disabled = false;
    }
  });
  document.querySelector("#syncButton").addEventListener("click", startAdminRefresh);
  elements.tradeDate.addEventListener("change", () => {
    state.dashboardRequestSequence += 1;
    state.heavenRequestSequence += 1;
    state.heavenManualData = null;
    document.querySelector("#qiObservationDate").value = elements.tradeDate.value;
    loadDashboard();
  });
  document.querySelector("#prevDate").addEventListener("click", () => shiftDate(-1));
  document.querySelector("#nextDate").addEventListener("click", () => shiftDate(1));
}
