/**
 * 中控策略计算器:趋势回调 / 滚仓历史测算
 */
(function () {
  const page = document.getElementById("page-calculator");
  if (!page) return;

  let inited = false;
  const marketCache = {};
  let calculatorExchanges = [];

  function $(id) {
    return document.getElementById(id);
  }

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

  function num(id) {
    const el = $(id);
    if (!el) return null;
    const n = Number(el.value);
    return Number.isFinite(n) ? n : null;
  }

  function text(id) {
    const el = $(id);
    if (!el) return "";
    return String(el.value || "").trim();
  }

  function fmt(v, digits) {
    if (v == null || v === "") return "—";
    const n = Number(v);
    if (!Number.isFinite(n)) return esc(v);
    if (digits != null) return n.toFixed(digits);
    return String(n);
  }

  /** 去掉尾部多余 0,用于乘数/精度展示 */
  function fmtTrim(v, maxDigits) {
    if (v == null || v === "") return "—";
    const n = Number(v);
    if (!Number.isFinite(n)) return esc(v);
    let s = maxDigits != null ? n.toFixed(maxDigits) : String(n);
    if (s.includes(".")) s = s.replace(/\.?0+$/, "");
    return s;
  }

  function fmtU(v) {
    if (v == null || v === "") return "—";
    const n = Number(v);
    if (!Number.isFinite(n)) return "—";
    return (n >= 0 ? "+" : "") + n.toFixed(2) + "U";
  }

  function pnlClass(v) {
    const n = Number(v);
    if (!Number.isFinite(n) || n === 0) return "";
    return n > 0 ? "calc-pnl-profit" : "calc-pnl-loss";
  }

  function decimalsFromMarket(data) {
    if (!data || !data.market) return { price: 4, amount: 4 };
    return {
      price: Number(data.market.price_decimals),
      amount: Number(data.market.amount_decimals),
    };
  }

  function fmtMarketInfo(market, err) {
    if (err) {
      return '<span class="calc-market-err">' + esc(err) + "</span>";
    }
    if (!market) return "—";
    const inst = market.exchange_name ? esc(market.exchange_name) + " · " : "";
    const parts = [
      inst + "<strong>" + esc(market.display_symbol || market.base || "") + "</strong> 永续",
      "合约 " + esc(market.exchange_symbol || ""),
      "乘数 " + fmtTrim(market.contract_size, 8),
      "价格精度 " + fmtTrim(market.price_tick != null ? market.price_tick : Math.pow(10, -(market.price_decimals || 0))),
      "张数精度 " + fmtTrim(Math.pow(10, -(market.amount_decimals || 0))),
    ];
    if (market.min_amount != null) {
      parts.push("最小张数 " + fmtTrim(market.min_amount, market.amount_decimals));
    }
    return parts.join(" · ");
  }

  function applyMarketSteps(prefix, market) {
    const pxStep =
      market && market.price_tick != null && Number(market.price_tick) > 0
        ? String(market.price_tick)
        : market && market.price_decimals != null
          ? String(Math.pow(10, -Number(market.price_decimals)))
          : "any";
    const amtStep =
      market && market.amount_decimals != null
        ? String(Math.pow(10, -Number(market.amount_decimals)))
        : "any";
    page.querySelectorAll("#" + prefix + "-form input[type='number']").forEach(function (el) {
      if (el.classList.contains("calc-roll-leg-add") || el.classList.contains("calc-roll-leg-stop")) {
        el.step = pxStep;
        return;
      }
      if (el.id === prefix + "-capital" || el.id === prefix + "-risk" || el.id === prefix + "-leverage") {
        return;
      }
      if (el.id === prefix + "-dca-legs" || el.id === prefix + "-legs-done") {
        return;
      }
      el.step = pxStep;
    });
    page.querySelectorAll(".calc-roll-leg-add, .calc-roll-leg-stop").forEach(function (el) {
      el.step = pxStep;
    });
    void amtStep;
  }

  async function refreshMarket(prefix) {
    const exchangeEl = $(prefix + "-exchange");
    const baseEl = $(prefix + "-base");
    const infoEl = $(prefix + "-market-info");
    if (!exchangeEl || !baseEl || !infoEl) return null;
    const exchangeId = exchangeEl.value || (calculatorExchanges[0] && calculatorExchanges[0].id) || "0";
    const base = text(prefix + "-base") || "ETH";
    const cacheKey = exchangeId + ":" + base.toUpperCase();
    infoEl.innerHTML = "加载合约信息…";
    try {
      const r = await fetch(
        "/api/calculator/market?exchange_id=" +
          encodeURIComponent(exchangeId) +
          "&base=" +
          encodeURIComponent(base),
        { credentials: "same-origin" }
      );
      const j = await r.json();
      if (!j.ok) {
        infoEl.innerHTML = fmtMarketInfo(null, j.msg || "加载失败");
        marketCache[prefix] = null;
        return null;
      }
      marketCache[prefix] = j.data;
      marketCache[cacheKey] = j.data;
      infoEl.innerHTML = fmtMarketInfo(j.data, null);
      applyMarketSteps(prefix, j.data);
      return j.data;
    } catch (err) {
      infoEl.innerHTML = fmtMarketInfo(null, String(err));
      marketCache[prefix] = null;
      return null;
    }
  }

  function fillExchangeSelect(selectEl, selectedId) {
    if (!selectEl) return;
    selectEl.innerHTML = "";
    if (!calculatorExchanges.length) {
      selectEl.innerHTML = '<option value="">无已启用交易所</option>';
      return;
    }
    calculatorExchanges.forEach(function (ex) {
      const opt = document.createElement("option");
      opt.value = String(ex.id);
      opt.textContent = ex.name || ex.key || ex.id;
      selectEl.appendChild(opt);
    });
    const want = selectedId != null ? String(selectedId) : String(calculatorExchanges[0].id);
    if ([].some.call(selectEl.options, function (o) { return o.value === want; })) {
      selectEl.value = want;
    }
  }

  async function loadCalculatorExchanges() {
    try {
      const r = await fetch("/api/calculator/exchanges", { credentials: "same-origin" });
      const j = await r.json();
      calculatorExchanges = (j.ok && j.data) || [];
    } catch (_err) {
      calculatorExchanges = [];
    }
    fillExchangeSelect($("calc-trend-exchange"));
    fillExchangeSelect($("calc-roll-exchange"));
  }

  function fmtRefreshTime() {
    const d = new Date();
    const h = String(d.getHours()).padStart(2, "0");
    const m = String(d.getMinutes()).padStart(2, "0");
    const s = String(d.getSeconds()).padStart(2, "0");
    return h + ":" + m + ":" + s;
  }

  async function refreshPage() {
    const btn = $("calc-btn-refresh");
    const status = $("calc-refresh-status");
    const trendId = $("calc-trend-exchange") && $("calc-trend-exchange").value;
    const rollId = $("calc-roll-exchange") && $("calc-roll-exchange").value;
    if (btn) btn.disabled = true;
    if (status) status.textContent = "刷新中…";
    Object.keys(marketCache).forEach(function (k) {
      delete marketCache[k];
    });
    try {
      await loadCalculatorExchanges();
      fillExchangeSelect($("calc-trend-exchange"), trendId);
      fillExchangeSelect($("calc-roll-exchange"), rollId);
      await Promise.all([refreshMarket("calc-trend"), refreshMarket("calc-roll")]);
      if (status) status.textContent = "已刷新 " + fmtRefreshTime();
    } catch (err) {
      if (status) status.textContent = "刷新失败";
    } finally {
      if (btn) btn.disabled = false;
    }
  }

  function bindMarket(prefix) {
    const exchangeEl = $(prefix + "-exchange");
    const baseEl = $(prefix + "-base");
    if (!exchangeEl || !baseEl) return;
    const run = function () {
      void refreshMarket(prefix);
    };
    if (!exchangeEl._calcMarketBound) {
      exchangeEl._calcMarketBound = true;
      exchangeEl.addEventListener("change", run);
    }
    if (!baseEl._calcMarketBound) {
      baseEl._calcMarketBound = true;
      baseEl.addEventListener("change", run);
      baseEl.addEventListener("blur", run);
    }
    run();
  }

  function syncTrendAddLabel() {
    const dir = ($("calc-trend-direction") && $("calc-trend-direction").value) || "long";
    const lab = $("calc-trend-add-label");
    if (lab) lab.textContent = dir === "short" ? "补仓下沿价" : "补仓上沿价";
  }

  function renderTrendTable(rows, dec) {
    if (!rows || !rows.length) {
      return '<p class="calc-empty">无档位数据</p>';
    }
    const px = dec.price != null ? dec.price : 4;
    const amt = dec.amount != null ? dec.amount : 4;
    let html =
      '<div class="calc-table-wrap"><table class="calc-table"><thead><tr>' +
      "<th>档位</th><th>触发价</th><th>张数</th><th>加仓后均价</th><th>止盈盈利</th><th>止损金额</th><th>盈亏比</th>" +
      "</tr></thead><tbody>";
    rows.forEach(function (r) {
      html +=
        "<tr>" +
        "<td>" +
        esc(r.label) +
        "</td>" +
        "<td>" +
        fmt(r.price, px) +
        "</td>" +
        "<td>" +
        fmt(r.contracts, amt) +
        "</td>" +
        "<td>" +
        fmt(r.avg_entry, px) +
        "</td>" +
        '<td class="' +
        pnlClass(r.profit_u) +
        '">' +
        fmtU(r.profit_u) +
        "</td>" +
        "<td>" +
        fmtU(r.risk_u) +
        "</td>" +
        "<td>" +
        (r.rr != null ? fmt(r.rr, 2) + ":1" : "—") +
        "</td>" +
        "</tr>";
    });
    html += "</tbody></table></div>";
    return html;
  }

  function renderTrendResult(data) {
    const box = $("calc-trend-result");
    if (!box) return;
    const dec = decimalsFromMarket(data);
    box.classList.remove("hidden");
    box.innerHTML =
      '<div class="calc-summary">' +
      "<div><span>合约</span><strong>" +
      esc((data.market && data.market.display_symbol) || "—") +
      "</strong></div>" +
      "<div><span>计划保证金</span><strong>" +
      fmt(data.plan_margin_u, 2) +
      "U</strong></div>" +
      "<div><span>止损预算</span><strong>" +
      fmt(data.risk_budget_u, 2) +
      "U</strong></div>" +
      "<div><span>总张数</span><strong>" +
      fmt(data.target_contracts, dec.amount) +
      "</strong></div>" +
      "<div><span>首仓张数</span><strong>" +
      fmt(data.first_contracts, dec.amount) +
      "</strong></div>" +
      '<div><span>首仓止盈盈利</span><strong class="' +
      pnlClass(data.first_profit_u) +
      '">' +
      fmtU(data.first_profit_u) +
      "</strong></div>" +
      "<div><span>首仓盈亏比</span><strong>" +
      (data.first_rr != null ? fmt(data.first_rr, 2) + ":1" : "—") +
      "</strong></div>" +
      "</div>" +
      renderTrendTable(data.rows, dec);
  }

  function renderRollResult(data) {
    const box = $("calc-roll-result");
    if (!box) return;
    const dec = decimalsFromMarket(data);
    const px = dec.price != null ? dec.price : 4;
    const amt = dec.amount != null ? dec.amount : 4;
    box.classList.remove("hidden");
    let table =
      '<div class="calc-table-wrap"><table class="calc-table"><thead><tr>' +
      "<th>阶段</th><th>入场/加仓价</th><th>统一止损</th><th>本次张数</th><th>累计张数</th><th>均价</th><th>打到止损总亏</th><th>止盈盈利</th><th>盈亏比</th>" +
      "</tr></thead><tbody>";
    (data.rows || []).forEach(function (r) {
      const tag = r.already_done ? ' <span class="calc-done-tag">已完成</span>' : "";
      table +=
        "<tr>" +
        "<td>" +
        esc(r.label) +
        tag +
        "</td>" +
        "<td>" +
        fmt(r.entry_or_add_price, px) +
        "</td>" +
        "<td>" +
        fmt(r.stop_loss, px) +
        "</td>" +
        "<td>" +
        fmt(r.add_contracts, amt) +
        "</td>" +
        "<td>" +
        fmt(r.total_contracts, amt) +
        "</td>" +
        "<td>" +
        fmt(r.avg_entry, px) +
        "</td>" +
        '<td class="calc-pnl-loss">' +
        fmtU(-Math.abs(Number(r.loss_at_sl_u) || 0)) +
        "</td>" +
        '<td class="' +
        pnlClass(r.profit_at_tp_u) +
        '">' +
        fmtU(r.profit_at_tp_u) +
        "</td>" +
        "<td>" +
        (r.rr != null ? fmt(r.rr, 2) + ":1" : "—") +
        "</td>" +
        "</tr>";
    });
    table += "</tbody></table></div>";
    box.innerHTML =
      '<div class="calc-summary">' +
      "<div><span>合约</span><strong>" +
      esc((data.market && data.market.display_symbol) || "—") +
      "</strong></div>" +
      "<div><span>单次风险预算</span><strong>" +
      fmt(data.risk_budget_u, 2) +
      "U</strong></div>" +
      "<div><span>首仓张数(自动)</span><strong>" +
      fmt(data.first_contracts, amt) +
      "</strong></div>" +
      "<div><span>最终累计张数</span><strong>" +
      fmt(data.final_contracts, amt) +
      "</strong></div>" +
      "<div><span>最终均价</span><strong>" +
      fmt(data.final_avg_entry, px) +
      "</strong></div>" +
      '<div><span>最终止盈盈利</span><strong class="' +
      pnlClass(data.final_profit_at_tp_u) +
      '">' +
      fmtU(data.final_profit_at_tp_u) +
      "</strong></div>" +
      "<div><span>最终盈亏比</span><strong>" +
      (data.final_rr != null ? fmt(data.final_rr, 2) + ":1" : "—") +
      "</strong></div>" +
      "</div>" +
      table;
  }

  const MAX_ROLL_LEGS = 3;
  let rollLegCount = 0;

  function maxRollLegsAllowed() {
    const done = num("calc-roll-legs-done") || 0;
    return Math.max(0, MAX_ROLL_LEGS - done);
  }

  function syncRollAddBtn() {
    const btn = $("calc-roll-add-leg");
    if (!btn) return;
    btn.disabled = rollLegCount >= maxRollLegsAllowed();
  }

  function rollLegRowHtml(index) {
    const step = (marketCache["calc-roll"] && marketCache["calc-roll"].price_tick) || "any";
    return (
      '<div class="calc-roll-leg" data-leg-index="' +
      index +
      '">' +
      '<div class="calc-roll-leg-title">滚仓 ' +
      index +
      "</div>" +
      '<div class="calc-roll-leg-grid">' +
      '<label class="calc-field"><span>加仓价</span><input type="number" class="calc-roll-leg-add" min="0" step="' +
      esc(step) +
      '" required /></label>' +
      '<label class="calc-field"><span>新统一止损</span><input type="number" class="calc-roll-leg-stop" min="0" step="' +
      esc(step) +
      '" required /></label>' +
      "</div>" +
      '<button type="button" class="ghost danger calc-roll-leg-remove">删除</button>' +
      "</div>"
    );
  }

  function renumberRollLegs() {
    const list = $("calc-roll-legs-list");
    if (!list) return;
    const rows = list.querySelectorAll(".calc-roll-leg");
    rollLegCount = rows.length;
    rows.forEach(function (row, i) {
      row.setAttribute("data-leg-index", String(i + 1));
      const title = row.querySelector(".calc-roll-leg-title");
      if (title) title.textContent = "滚仓 " + (i + 1);
    });
    syncRollAddBtn();
  }

  function addRollLegRow() {
    if (rollLegCount >= maxRollLegsAllowed()) return;
    const list = $("calc-roll-legs-list");
    if (!list) return;
    list.insertAdjacentHTML("beforeend", rollLegRowHtml(rollLegCount + 1));
    rollLegCount += 1;
    syncRollAddBtn();
  }

  function collectRollLegs() {
    const legs = [];
    document.querySelectorAll(".calc-roll-leg").forEach(function (row) {
      const addEl = row.querySelector(".calc-roll-leg-add");
      const stopEl = row.querySelector(".calc-roll-leg-stop");
      const ap = addEl && addEl.value !== "" ? Number(addEl.value) : null;
      const sl = stopEl && stopEl.value !== "" ? Number(stopEl.value) : null;
      if (ap == null || sl == null || !Number.isFinite(ap) || !Number.isFinite(sl)) return;
      legs.push({ add_price: ap, new_stop_loss: sl });
    });
    return legs;
  }

  function bindRollLegsUI() {
    const addBtn = $("calc-roll-add-leg");
    const list = $("calc-roll-legs-list");
    const doneInput = $("calc-roll-legs-done");
    if (addBtn && !addBtn._bound) {
      addBtn._bound = true;
      addBtn.addEventListener("click", addRollLegRow);
    }
    if (list && !list._bound) {
      list._bound = true;
      list.addEventListener("click", function (e) {
        const btn = e.target.closest(".calc-roll-leg-remove");
        if (!btn) return;
        const row = btn.closest(".calc-roll-leg");
        if (row) row.remove();
        renumberRollLegs();
      });
    }
    if (doneInput && !doneInput._bound) {
      doneInput._bound = true;
      doneInput.addEventListener("change", function () {
        while (rollLegCount > maxRollLegsAllowed()) {
          const rows = list && list.querySelectorAll(".calc-roll-leg");
          if (rows && rows.length) rows[rows.length - 1].remove();
          rollLegCount = list ? list.querySelectorAll(".calc-roll-leg").length : 0;
        }
        syncRollAddBtn();
      });
    }
    syncRollAddBtn();
  }

  function showErr(boxId, msg) {
    const box = $(boxId);
    if (!box) return;
    box.classList.remove("hidden");
    box.innerHTML = '<p class="calc-error">' + esc(msg || "计算失败") + "</p>";
  }

  async function submitTrend(e) {
    e.preventDefault();
    const body = {
      direction: ($("calc-trend-direction") && $("calc-trend-direction").value) || "long",
      exchange_id: ($("calc-trend-exchange") && $("calc-trend-exchange").value) || "0",
      base: text("calc-trend-base") || "ETH",
      capital_usdt: num("calc-trend-capital"),
      risk_percent: num("calc-trend-risk"),
      leverage: num("calc-trend-leverage"),
      entry_price: num("calc-trend-entry"),
      stop_loss: num("calc-trend-sl"),
      add_upper: num("calc-trend-add-upper"),
      take_profit: num("calc-trend-tp"),
      dca_legs: num("calc-trend-dca-legs") || 5,
    };
    try {
      const r = await fetch("/api/calculator/trend", {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      const j = await r.json();
      if (!j.ok) {
        showErr("calc-trend-result", j.msg || "计算失败");
        return;
      }
      renderTrendResult(j.data);
    } catch (err) {
      showErr("calc-trend-result", String(err));
    }
  }

  async function submitRoll(e) {
    e.preventDefault();
    const body = {
      direction: ($("calc-roll-direction") && $("calc-roll-direction").value) || "long",
      exchange_id: ($("calc-roll-exchange") && $("calc-roll-exchange").value) || "0",
      base: text("calc-roll-base") || "ETH",
      capital_usdt: num("calc-roll-capital"),
      risk_percent: num("calc-roll-risk"),
      entry_price: num("calc-roll-entry"),
      stop_loss: num("calc-roll-sl"),
      take_profit: num("calc-roll-tp"),
      add_legs: collectRollLegs(),
      legs_done: num("calc-roll-legs-done") || 0,
    };
    try {
      const r = await fetch("/api/calculator/roll", {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      const j = await r.json();
      if (!j.ok) {
        showErr("calc-roll-result", j.msg || "计算失败");
        return;
      }
      renderRollResult(j.data);
    } catch (err) {
      showErr("calc-roll-result", String(err));
    }
  }

  function applyCalcTab(tab) {
    const t = tab === "roll" ? "roll" : "trend";
    const layout = page.querySelector(".calc-layout");
    if (layout) layout.setAttribute("data-calc-tab", t);
    page.querySelectorAll(".calc-m-tab").forEach(function (btn) {
      const on = (btn.getAttribute("data-calc-tab") || "") === t;
      btn.classList.toggle("is-active", on);
      btn.setAttribute("aria-selected", on ? "true" : "false");
    });
    try {
      sessionStorage.setItem("hub_calc_tab", t);
    } catch (e) {
      /* ignore */
    }
  }

  function bindCalcTabs() {
    page.querySelectorAll(".calc-m-tab").forEach(function (btn) {
      btn.addEventListener("click", function () {
        applyCalcTab(btn.getAttribute("data-calc-tab") || "trend");
      });
    });
    let saved = "trend";
    try {
      saved = sessionStorage.getItem("hub_calc_tab") || "trend";
    } catch (e) {
      saved = "trend";
    }
    applyCalcTab(saved);
  }

  async function bindOnce() {
    if (inited) return;
    inited = true;
    await loadCalculatorExchanges();
    const trendForm = $("calc-trend-form");
    const rollForm = $("calc-roll-form");
    const dirSel = $("calc-trend-direction");
    if (trendForm) trendForm.addEventListener("submit", submitTrend);
    if (rollForm) rollForm.addEventListener("submit", submitRoll);
    if (dirSel) {
      dirSel.addEventListener("change", syncTrendAddLabel);
      syncTrendAddLabel();
    }
    bindRollLegsUI();
    bindMarket("calc-trend");
    bindMarket("calc-roll");
    bindCalcTabs();
    const refreshBtn = $("calc-btn-refresh");
    if (refreshBtn) {
      refreshBtn.addEventListener("click", function () {
        void refreshPage();
      });
    }
  }

  window.hubCalculatorPage = {
    init: function () {
      if (inited) {
        void refreshPage();
        return;
      }
      void bindOnce();
    },
    refresh: refreshPage,
    destroy: function () {},
  };
})();
