/**
 * 中控数据看板:后端 SSE 推送版本号,前端拉快照刷新(无轮询闪烁).
 */
(function () {
  const page = document.getElementById("page-dashboard");
  if (!page) return;

  let dashEventSource = null;
  let dashReconnectTimer = null;
  let localDashVersion = 0;
  let inited = false;
  let loading = false;

  const elStatus = document.getElementById("dash-status");
  const elBanner = document.getElementById("dash-alert-banner");
  const elBannerText = document.getElementById("dash-alert-banner-text");
  const elKpi = document.getElementById("dash-kpi-row");
  const elAccounts = document.getElementById("dash-accounts");
  const elTrades = document.getElementById("dash-trades-body");
  const elUpdated = document.getElementById("dash-updated-at");
  const elDay = document.getElementById("dash-trading-day");
  const btnRefresh = document.getElementById("dash-btn-refresh");

  function fmt(n, d) {
    if (n == null || n === "" || !Number.isFinite(Number(n))) return "—";
    return Number(n).toFixed(d == null ? 2 : d);
  }

  function pnlClass(v) {
    const n = Number(v);
    if (!Number.isFinite(n) || Math.abs(n) < 1e-9) return "";
    return n > 0 ? "pos" : "neg";
  }

  function pnlSigned(v, digits) {
    const n = Number(v);
    if (!Number.isFinite(n)) return "—";
    const abs = fmt(Math.abs(n), digits);
    if (Math.abs(n) < 1e-9) return `${abs}U`;
    return `${n > 0 ? "+" : "-"}${abs}U`;
  }

  function esc(s) {
    return String(s == null ? "" : s)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;");
  }

  function setStatus(msg, isErr) {
    if (!elStatus) return;
    elStatus.textContent = msg || "";
    elStatus.className = "dash-status" + (isErr ? " err" : "");
  }

  function renderKpi(totals) {
    if (!elKpi || !totals) return;
    const closed = Number(totals.total_pnl_u);
    const floating = Number(totals.float_pnl_u);
    const funding = totals.total_funding_usdt;
    const trading = totals.total_trading_usdt;
    const optionsFloat = Number(totals.options_float_pnl_u);
    const optionsPos = totals.options_open_position_count;
    elKpi.innerHTML = [
      kpiCard("交易日", esc(totals.trading_day || "—"), ""),
      kpiCard("平仓盈亏", pnlSigned(closed, 2), pnlClass(closed)),
      kpiCard(
        "平仓笔数",
        `${totals.closed_count || 0}`,
        "",
        `胜 ${totals.win_count || 0} / 负 ${totals.loss_count || 0}`
      ),
      kpiCard("浮盈亏", pnlSigned(floating, 2), pnlClass(floating)),
      kpiCard(
        "资金合计",
        funding != null && trading != null
          ? `${fmt(Number(funding) + Number(trading), 2)}U`
          : "—",
        "",
        `永续+期权 USDT 等价 · 资金 ${fmt(funding, 2)} + 交易 ${fmt(trading, 2)}`
      ),
      kpiCard(
        "实盘持仓",
        `${totals.open_position_count || 0} 仓`,
        "",
        Number.isFinite(optionsPos) && optionsPos > 0 ? `含期权 ${optionsPos} 仓` : ""
      ),
      Number.isFinite(optionsFloat) && Math.abs(optionsFloat) > 1e-9
        ? kpiCard("期权浮盈", pnlSigned(optionsFloat, 2), pnlClass(optionsFloat))
        : "",
    ]
      .filter(Boolean)
      .join("");
  }

  function kpiCard(label, value, valCls, sub) {
    return `<div class="dash-kpi">
      <div class="dash-kpi-label">${esc(label)}</div>
      <div class="dash-kpi-value ${valCls || ""}">${value}</div>
      ${sub ? `<div class="dash-kpi-sub">${esc(sub)}</div>` : ""}
    </div>`;
  }

  function renderMonitorCountChips(counts) {
    const mc = counts || {};
    const chips = [];
    const keys = Number(mc.keys) || 0;
    const orders = Number(mc.orders) || 0;
    const trends = Number(mc.trends) || 0;
    const rolls = Number(mc.rolls) || 0;
    if (keys > 0) chips.push(`<span class="dash-monitor-chip dash-monitor-key">关键位 ${keys}</span>`);
    if (orders > 0) {
      chips.push(`<span class="dash-monitor-chip dash-monitor-order">下单监控 ${orders}</span>`);
    }
    if (trends > 0) chips.push(`<span class="dash-monitor-chip dash-monitor-trend">趋势回调 ${trends}</span>`);
    if (rolls > 0) chips.push(`<span class="dash-monitor-chip dash-monitor-roll">顺势加仓 ${rolls}</span>`);
    return chips;
  }

  function dashOptionsExpiryCd(expMs) {
    const ms = expMs != null && expMs !== "" ? String(expMs) : "";
    if (!ms) return "—";
    return `<span class="opt-expiry-cd" data-opt-exp-ms="${esc(ms)}">—</span>`;
  }

  function renderDashboardOptionsTable(positions) {
    const pos = Array.isArray(positions) ? positions : [];
    if (!pos.length) return "";
    const rows = pos
      .map((p) => {
        const optType =
          (p.opt_type || "").toUpperCase() === "C"
            ? "Call"
            : (p.opt_type || "").toUpperCase() === "P"
              ? "Put"
              : p.opt_type || "—";
        return `<tr>
          <td title="${esc(p.inst_id || "")}">${esc(shortDashInst(p.inst_id))}</td>
          <td>${esc(optType)}</td>
          <td>${dashOptionsExpiryCd(p.exp_time_ms != null ? p.exp_time_ms : p.exp_time)}</td>
          <td>${p.idx_px != null ? fmt(p.idx_px, 0) : "—"}</td>
          <td>${p.expiry_be_px != null ? fmt(p.expiry_be_px, 0) : "—"}</td>
          <td>${p.close_be_px != null ? fmt(p.close_be_px, 0) : "—"}</td>
          <td class="${pnlClass(p.upl)}">${p.upl != null ? pnlSigned(p.upl, 2) : "—"}</td>
        </tr>`;
      })
      .join("");
    return `<div class="dash-options-block">
      <div class="dash-ac-section-label">期权持仓</div>
      <div class="dash-table-wrap dash-options-table-wrap">
        <table class="dash-table dash-options-table">
          <thead><tr>
            <th>合约</th><th>类型</th><th>到期倒计时</th><th>指数</th><th>到期平衡</th><th>平掉回本</th><th>浮盈</th>
          </tr></thead>
          <tbody>${rows}</tbody>
        </table>
      </div>
    </div>`;
  }

  function shortDashInst(instId) {
    const s = String(instId || "");
    if (s.length <= 18) return s;
    return s.slice(0, 8) + "…" + s.slice(-6);
  }

  function renderAccountDetail(ac) {
    const counts = (ac && ac.monitor_counts) || {};
    const positions = Array.isArray(ac && ac.position_lines) ? ac.position_lines : [];
    const optionsPositions = Array.isArray(ac && ac.options_positions) ? ac.options_positions : [];
    const issues = Array.isArray(ac && ac.issues) ? ac.issues : [];
    const exId = ac && ac.id != null ? String(ac.id) : "";
    const chips = renderMonitorCountChips(counts);
    const expandBtn = exId
      ? `<button type="button" class="dash-ac-expand-btn" data-dash-ex-id="${esc(exId)}" title="放大查看监控详情" aria-label="放大查看监控详情">` +
        `<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path fill="currentColor" d="M15 3h6v6h-2V6.41l-7.29 7.3-1.42-1.42 7.3-7.29H15V3zM3 9h2v10h10v2H3V9z"/></svg>` +
        `</button>`
      : "";
    const monitorRow =
      chips.length || expandBtn
        ? `<div class="dash-ac-monitor-row">${chips.join("")}${expandBtn}</div>`
        : "";
    let posHtml = "";
    const perpLines = ac && ac.options_layout
      ? positions.filter((ln) => (ln && ln.kind) !== "options")
      : positions;
    if (perpLines.length) {
      posHtml = perpLines
        .map((ln) => {
          const text = esc((ln && ln.text) || "");
          if (ln.pnl != null && Number.isFinite(Number(ln.pnl))) {
            const pnl = Number(ln.pnl);
            return (
              `<div class="dash-ac-remark-line dash-ac-remark-pos">` +
              `${text} 浮<span class="${pnlClass(pnl)}">${pnlSigned(pnl, 2)}</span>` +
              `</div>`
            );
          }
          return `<div class="dash-ac-remark-line dash-ac-remark-pos">${text}</div>`;
        })
        .join("");
    } else if (!chips.length && !issues.length && !(ac && ac.options_layout && optionsPositions.length)) {
      posHtml = `<div class="dash-ac-remark-line dash-ac-remark-empty">无持仓</div>`;
    }
    const issueHtml = issues
      .map((text) => `<div class="dash-ac-remark-line dash-ac-remark-issue">${esc(text)}</div>`)
      .join("");
    const optionsHtml = ac && ac.options_layout ? renderDashboardOptionsTable(optionsPositions) : "";
    return `<div class="dash-ac-remark">${monitorRow}<div class="dash-ac-positions">${posHtml}</div>${optionsHtml}${issueHtml}</div>`;
  }

  function bindDashboardExpand() {
    if (!elAccounts) return;
    elAccounts.querySelectorAll(".dash-ac-expand-btn").forEach((btn) => {
      btn.addEventListener("click", (ev) => {
        ev.preventDefault();
        ev.stopPropagation();
        const id = btn.getAttribute("data-dash-ex-id");
        if (id && window.hubOpenMonitorExpand) window.hubOpenMonitorExpand(id);
      });
    });
  }

  function dashMetric(label, value, valCls) {
    return `<div class="dash-ac-metric"><span>${esc(label)}</span><strong class="${valCls || ""}">${value}</strong></div>`;
  }

  function dashSectionLabel(text) {
    return `<div class="dash-ac-section-label">${esc(text)}</div>`;
  }

  function renderOkxOptionsMetrics(ac) {
    const pnl = Number(ac.pnl_u);
    const perpFloat = Number(
      ac.perpetual_float_pnl_u != null ? ac.perpetual_float_pnl_u : ac.float_pnl_u
    );
    const optFloat = Number(ac.options_float_pnl_u);
    const pf =
      ac.perpetual_funding_usdt != null ? ac.perpetual_funding_usdt : ac.funding_usdt;
    const pt =
      ac.perpetual_trading_usdt != null ? ac.perpetual_trading_usdt : ac.trading_usdt;
    return `<div class="dash-ac-metrics dash-ac-metrics-3col">
      ${dashSectionLabel("永续账户")}
      ${dashMetric("资金账户", `${fmt(pf, 2)}U`)}
      ${dashMetric("交易账户", `${fmt(pt, 2)}U`)}
      ${dashMetric("今日盈亏", pnlSigned(pnl, 2), pnlClass(pnl))}
      ${dashMetric("平仓笔数", String(Number(ac.closed_count) || 0))}
      ${dashMetric("浮盈亏", pnlSigned(perpFloat, 2), pnlClass(perpFloat))}
      <div class="dash-ac-metric dash-ac-metric-empty" aria-hidden="true"></div>
      ${dashSectionLabel("期权账户")}
      ${dashMetric("资金账户", `${fmt(ac.options_funding_usdt, 2)}U`)}
      ${dashMetric("交易账户", `${fmt(ac.options_trading_usdt, 2)}U`)}
      ${dashMetric("期权浮盈亏", pnlSigned(optFloat, 2), pnlClass(optFloat))}
    </div>
    <div class="dash-ac-total-row">
      <span>总资金</span>
      <strong>${fmt(ac.capital_total_usdt, 2)}U</strong>
    </div>`;
  }

  function renderStandardMetrics(ac) {
    const pnl = Number(ac.pnl_u);
    const floatPnl = Number(ac.float_pnl_u);
    return `<div class="dash-ac-metrics">
      ${dashMetric("资金账户", `${fmt(ac.funding_usdt, 2)}U`)}
      ${dashMetric("交易账户", `${fmt(ac.trading_usdt, 2)}U`)}
      ${dashMetric("资金合计", `${fmt(ac.capital_total_usdt, 2)}U`)}
      ${dashMetric("今日盈亏", pnlSigned(pnl, 2), pnlClass(pnl))}
      ${dashMetric("平仓笔数", String(Number(ac.closed_count) || 0))}
      ${dashMetric("浮盈亏", pnlSigned(floatPnl, 2), pnlClass(floatPnl))}
    </div>`;
  }

  function renderAccounts(accounts, threshold) {
    const rows = Array.isArray(accounts) ? accounts : [];
    if (!rows.length) {
      elAccounts.innerHTML = '<div class="dash-empty">暂无账户数据</div>';
      return;
    }
    elAccounts.innerHTML = rows
      .map((ac) => {
        const alert = !!ac.loss_alert;
        const unmon = !ac.monitored;
        const lossPct = Number(ac.daily_loss_pct);
        const barW =
          alert && Number.isFinite(lossPct)
            ? Math.min(100, (lossPct / Math.max(threshold, 1)) * 100)
            : 0;
        const badge = alert
          ? `<span class="dash-ac-badge alert">单日亏损 ≥${threshold}%</span>`
          : `<span class="dash-ac-badge ok">${esc(ac.status || "—")}</span>`;
        const lossBar =
          alert && barW > 0
            ? `<div class="dash-loss-bar" title="占资金合计 ${fmt(lossPct, 2)}%"><i style="width:${barW}%"></i></div>`
            : "";
        const metricsHtml = ac.options_layout
          ? renderOkxOptionsMetrics(ac)
          : renderStandardMetrics(ac);
        const cardCls = ac.options_layout ? " dash-ac-card-options" : "";
        return `<article class="dash-ac-card${cardCls}${alert ? " is-alert" : ""}${unmon ? " is-unmon" : ""}">
          <div class="dash-ac-top">
            <div class="dash-ac-name">${esc(ac.name || "—")}</div>
            ${badge}
          </div>
          ${metricsHtml}
          ${lossBar}
          ${renderAccountDetail(ac)}
        </article>`;
      })
      .join("");
    bindDashboardExpand();
    if (window.OptionsExpiryCountdown && OptionsExpiryCountdown.ensureTimer) {
      OptionsExpiryCountdown.ensureTimer();
    }
  }

  function renderTrades(trades, accounts) {
    if (!elTrades) return;
    const rows = Array.isArray(trades) ? trades : [];
    if (!rows.length) {
      elTrades.innerHTML = '<div class="dash-empty">今日暂无平仓</div>';
      return;
    }
    const alertNames = new Set(
      (accounts || []).filter((a) => a.loss_alert).map((a) => String(a.name || ""))
    );
    const body = rows
      .map((t) => {
        const pnl = Number(t.pnl_amount);
        const rowAlert = alertNames.has(String(t.account_name || ""));
        return `<tr class="${rowAlert ? "is-alert-row" : ""}">
          <td>${esc(t.trading_day || "—")}</td>
          <td>${esc(t.account_name || "—")}</td>
          <td>${esc(t.symbol || "—")}</td>
          <td>${esc(t.direction || "—")}</td>
          <td>${esc(t.result || "—")}</td>
          <td class="${pnlClass(pnl)}">${pnlSigned(pnl, 2)}</td>
          <td>${esc(t.closed_at || "—")}</td>
        </tr>`;
      })
      .join("");
    elTrades.innerHTML = `<div class="dash-table-wrap"><table class="dash-table">
      <thead><tr>
        <th>交易日</th><th>账户</th><th>合约</th><th>方向</th><th>结果</th><th>盈亏</th><th>时间</th>
      </tr></thead>
      <tbody>${body}</tbody>
    </table></div>`;
  }

  function renderPayload(data) {
    const totals = data.totals || {};
    const threshold = Number(data.loss_alert_pct_threshold) || 5;
    const alertCount = Number(data.loss_alert_count) || 0;
    if (elDay) elDay.textContent = totals.trading_day || data.trading_day || "—";
    if (elUpdated) elUpdated.textContent = data.updated_at || "—";
    renderKpi(totals);
    renderAccounts(data.accounts, threshold);
    renderTrades(data.closed_trades, data.accounts);
    if (elBanner && elBannerText) {
      if (alertCount > 0) {
        const names = (data.accounts || [])
          .filter((a) => a.loss_alert)
          .map((a) => a.name)
          .join(",");
        elBanner.classList.add("is-on");
        elBannerText.textContent = `${alertCount} 户单日平仓亏损超过资金合计 ${threshold}%:${names}`;
      } else {
        elBanner.classList.remove("is-on");
        elBannerText.textContent = "";
      }
    }
  }

  async function fetchDashboardSnapshot(opts) {
    const options = opts || {};
    if (loading && !options.force) return;
    loading = true;
    if (!options.silent) setStatus("同步中…");
    try {
      const r = await fetch("/api/dashboard/daily", { credentials: "same-origin" });
      if (r.status === 401) {
        location.href = "/login?next=" + encodeURIComponent(location.pathname);
        return;
      }
      const data = await r.json();
      if (!data.ok) throw new Error(data.detail || data.msg || data.error || "加载失败");
      const ver = Number(data.dashboard_version) || 0;
      if (ver) localDashVersion = ver;
      renderPayload(data);
      const sec = Number(data.poll_interval_sec) || 60;
      setStatus(options.silent ? `SSE 已连接 · 后台每 ${sec}s 聚合` : `已更新 · 后台每 ${sec}s 聚合`);
    } catch (e) {
      setStatus(String(e.message || e), true);
    } finally {
      loading = false;
    }
  }

  function closeDashboardStream() {
    if (dashEventSource) {
      dashEventSource.close();
      dashEventSource = null;
    }
    if (dashReconnectTimer) {
      clearTimeout(dashReconnectTimer);
      dashReconnectTimer = null;
    }
  }

  function connectDashboardStream() {
    closeDashboardStream();
    dashEventSource = new EventSource("/api/dashboard/stream");
    dashEventSource.addEventListener("dashboard", (ev) => {
      try {
        const st = JSON.parse(ev.data || "{}");
        const ver = Number(st.dashboard_version) || 0;
        if (ver && ver !== localDashVersion) {
          void fetchDashboardSnapshot({ silent: true });
        } else if (st.aggregating) {
          setStatus("后台聚合中…");
        }
      } catch (_) {
        /* ignore */
      }
    });
    dashEventSource.onerror = () => {
      closeDashboardStream();
      setStatus("SSE 断开,8s 后重连…", true);
      dashReconnectTimer = setTimeout(() => {
        if (inited) {
          connectDashboardStream();
          void fetchDashboardSnapshot({ silent: true });
        }
      }, 8000);
    };
  }

  async function requestDashboardRefresh() {
    try {
      await fetch("/api/dashboard/refresh", { method: "POST", credentials: "same-origin" });
    } catch (_) {
      /* ignore */
    }
  }

  function startLive() {
    void fetchDashboardSnapshot();
    connectDashboardStream();
  }

  function stopLive() {
    closeDashboardStream();
    setStatus("");
  }

  if (btnRefresh) {
    btnRefresh.addEventListener("click", () => {
      void requestDashboardRefresh();
      void fetchDashboardSnapshot({ force: true });
    });
  }

  window.hubDashboardPage = {
    init() {
      inited = true;
      startLive();
    },
    destroy() {
      inited = false;
      stopLive();
    },
  };
})();
