(function (global) {
  "use strict";

  function fmt(v, d) {
    if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
    return Number(v).toFixed(d == null ? 2 : d);
  }

  function fmtDisplay(v, fallback) {
    if (v !== null && v !== undefined && String(v).trim() !== "") return String(v);
    if (fallback !== undefined) return fmtDisplay(fallback);
    return "—";
  }

  function fmtOptionPx(v, tickSz) {
    if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
    const n = Number(v);
    const tick = Number(tickSz);
    if (!tickSz || Number.isNaN(tick) || tick <= 0) {
      let s = n.toFixed(4).replace(/\.?0+$/, "");
      return s || "0";
    }
    let decimals = 0;
    if (tick < 1) decimals = Math.max(0, -Math.round(Math.log10(tick)));
    else if (String(tick).indexOf(".") >= 0) decimals = String(tick).split(".")[1].length;
    let s = n.toFixed(decimals);
    // 仅裁小数尾零;整数 tick(BTC=5)时绝不能把 1370 裁成 137
    if (decimals > 0) s = s.replace(/\.?0+$/, "");
    return s || "0";
  }

  function fmtUsdc(v) {
    if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
    return Number(v).toFixed(2);
  }

  function posPremiumCcy(p) {
    const ccy = String((p && p.premium_ccy) || "").trim().toUpperCase();
    if (ccy) return ccy;
    const mode = String((p && p.margin_mode) || "").toLowerCase();
    const inst = String((p && p.inst_id) || "");
    if (mode === "coin" || (inst.indexOf("-USD-") >= 0 && inst.indexOf("_UM") < 0)) {
      return (inst.split("-")[0] || "ETH").toUpperCase() || "ETH";
    }
    return "USDC";
  }

  function fmtPremiumAmt(v, ccy) {
    if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
    const n = Number(v);
    const unit = String(ccy || "USDC").toUpperCase();
    if (unit === "ETH" || unit === "BTC") {
      let s = n.toFixed(8).replace(/\.?0+$/, "");
      return s || "0";
    }
    return fmtUsdc(n);
  }

  function spotPxOf(p) {
    const n = Number(p && (p.idx_px != null ? p.idx_px : p.idxPx != null ? p.idxPx : p.index_px));
    return Number.isFinite(n) && n > 0 ? n : null;
  }

  function fmtCoinUsdtDual(coinAmt, spotPx, ccy, signed) {
    if (coinAmt === null || coinAmt === undefined || Number.isNaN(Number(coinAmt))) return "—";
    const n = Number(coinAmt);
    const unit = String(ccy || "ETH").toUpperCase();
    if (unit !== "ETH" && unit !== "BTC") {
      const sign = signed && n > 0 ? "+" : "";
      return sign + fmtUsdc(n) + "U";
    }
    const absCoin = Math.abs(n).toFixed(8).replace(/\.?0+$/, "") || "0";
    const coinSign = n < 0 ? "-" : signed && n > 0 ? "+" : "";
    const coinTxt = coinSign + absCoin + " " + unit;
    const px = Number(spotPx);
    if (!Number.isFinite(px) || !(px > 0)) return coinTxt;
    const u = n * px;
    const absU = Math.abs(u).toFixed(2);
    const uSign = u < 0 ? "-" : signed && u > 0 ? "+" : "";
    return coinTxt + " / " + uSign + absU + "U";
  }

  function fmtNetPnlDual(net, p) {
    const ccy = posPremiumCcy(p);
    if (ccy === "USDC") {
      if (net == null || Number.isNaN(Number(net))) return "—";
      return fmtUsdc(Number(net)) + "U";
    }
    return fmtCoinUsdtDual(net, spotPxOf(p), ccy, true);
  }

  function optTypeLabel(t) {
    return (t || "").toUpperCase() === "P" ? "看跌 Put" : "看涨 Call";
  }

  function sourceText(p) {
    const lab = (p && p.source_label) || "纯期权";
    const src = (p && p.source) || "option";
    let pid = p && p.source_plan_id;
    if (pid == null && p && p.hedge_plan_target && p.hedge_plan_target.plan_id != null) {
      pid = p.hedge_plan_target.plan_id;
    }
    if (src !== "option" && pid != null && pid !== "") return lab + " #" + pid;
    return lab;
  }

  function sourceBadgeHtml(p) {
    const src = (p && p.source) || "option";
    const cls =
      src === "options_options"
        ? "opt-source-badge opt-source-badge--oo"
        : src === "perp_options"
          ? "opt-source-badge opt-source-badge--po"
          : "opt-source-badge opt-source-badge--plain";
    return '<span class="' + cls + '" title="持仓来源">' + sourceText(p) + "</span>";
  }

  function pnlCls(upl, hub) {
    if (upl > 0) return hub ? "pnl-pos" : "pos-pnl-profit";
    if (upl < 0) return hub ? "pnl-neg" : "pos-pnl-loss";
    return "";
  }

  function fmtPxSz(px, sz, tickSz) {
    if (px === null || px === undefined || Number.isNaN(Number(px))) return "—";
    let price = fmtOptionPx(px, tickSz);
    if (sz === null || sz === undefined || sz === "" || Number.isNaN(Number(sz))) return price;
    const s = Number(sz);
    const size = Math.abs(s - Math.round(s)) < 1e-9 ? String(Math.round(s)) : String(s);
    return price + "/" + size;
  }

  /** 买盘深度:价格/流动性;仅展示平仓所需档位(买一不够才出买二…). */
  function fmtCloseLevels(preview, tickSz) {
    if (preview && preview.bid_invalid) {
      return "暂无有效买盘";
    }
    const levels = ((preview && preview.levels) || []).slice(0, 5);
    if (!levels.length) return "—";
    return levels.map(function (x, idx) {
      const levelNo = x.level != null ? x.level : idx + 1;
      const liq = x.available_sheets != null ? x.available_sheets : x.sz;
      return "买" + levelNo + " " + fmtPxSz(x.px, liq, tickSz);
    }).join(" · ");
  }

  function closeGateHint(preview) {
    if (!preview) return "";
    if (preview.bid_invalid || preview.manual_close_blocked) {
      return preview.bid_invalid_reason || "当前买一无效,禁止买一平仓";
    }
    const gate = preview.close_gate || {};
    if (preview.close_gate_blocked || (gate.ready === false && !gate.passed)) {
      return "目标门控: " + (preview.close_gate_msg || gate.msg || "门控未过(见 env 目标平仓门控)");
    }
    return "";
  }

  function netPnlFromPos(p) {
    const preview = (p && p.close_preview) || {};
    if (preview.bid_invalid) {
      const upl = p && p.upl != null ? Number(p.upl) : NaN;
      return Number.isFinite(upl) ? upl : null;
    }
    if (preview.estimated_pnl != null && !Number.isNaN(Number(preview.estimated_pnl))) {
      return Number(preview.estimated_pnl);
    }
    const covered = Number(preview.covered_sheets);
    const recv = Number(preview.total_received);
    const prem = Number(p && p.premium_paid);
    if (
      preview.total_received != null &&
      Number.isFinite(covered) &&
      covered > 0 &&
      !Number.isNaN(recv) &&
      !Number.isNaN(prem)
    ) {
      return recv - prem;
    }
    const upl = p && p.upl != null ? Number(p.upl) : NaN;
    return Number.isFinite(upl) ? upl : null;
  }

  function netRoiFromPos(p, net) {
    const preview = (p && p.close_preview) || {};
    if (preview.estimated_pnl_ratio_pct != null && !Number.isNaN(Number(preview.estimated_pnl_ratio_pct))) {
      return Number(preview.estimated_pnl_ratio_pct);
    }
    const prem = Number(p && p.premium_paid);
    if (net == null || Number.isNaN(prem) || prem <= 0) return null;
    return (net / prem) * 100;
  }

  function fmtClosePreview(preview, premiumPaid, hub, p) {
    if (!preview || preview.total_received == null) return "—";
    const ccy = posPremiumCcy(p);
    const recvTxt = fmtPremiumAmt(preview.total_received, ccy);
    let cls = "";
    const prem = Number(premiumPaid);
    const recv = Number(preview.total_received);
    if (!Number.isNaN(prem) && !Number.isNaN(recv)) {
      if (recv > prem) cls = " " + pnlCls(1, hub);
      else if (recv < prem) cls = " " + pnlCls(-1, hub);
    }
    return '<span class="opt-close-value' + cls + '">' + recvTxt + " " + ccy + "</span>";
  }

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

  function renderCardInner(p, opts) {
    opts = opts || {};
    const hub = !!opts.hub;
    const readOnly = !!opts.readOnly;
    const hidePnl = !!opts.hidePnl;
    const net = hidePnl ? null : netPnlFromPos(p);
    const roi = hidePnl ? null : netRoiFromPos(p, net);
    const uplCls = hidePnl ? "" : pnlCls(net, hub);
    const sideCls = (p.opt_type || "").toUpperCase() === "P" ? "pos-side-short" : "pos-side-long";
    const expMs = p.exp_time_ms != null ? p.exp_time_ms : p.exp_time;
    const expAttr = expMs != null && expMs !== "" ? String(expMs) : "";
    const closePreview = p.close_preview || {};
    const tickSz = p.tick_sz;
    const premCcy = posPremiumCcy(p);
    const premTxt = fmtDisplay(
      p.premium_paid_fmt,
      p.premium_paid != null ? fmtPremiumAmt(p.premium_paid, premCcy) : null
    );
    const avgTxt = p.avg_px != null ? fmtOptionPx(p.avg_px, tickSz) : fmtDisplay(p.avg_px_fmt);
    const markTxt = p.mark_px != null ? fmtOptionPx(p.mark_px, tickSz) : fmtDisplay(p.mark_px_fmt);
    let headActions = "";
    if (!readOnly) {
      const closeSheets = p.avail_pos != null && Number(p.avail_pos) > 0 ? p.avail_pos : p.pos;
      headActions =
        '<div class="pos-head-actions">' +
        '<button type="button" class="btn-primary opt-close-btn" data-inst="' + (p.inst_id || "") + '" data-sheets="' + closeSheets + '">买一平仓</button>' +
        "</div>";
    }
    const pnlCells = hidePnl
      ? ""
      : '<div class="pos-cell"><span class="pos-label">净盈亏</span><span class="pos-value ' + uplCls + '">' +
        (net == null ? "—" : fmtNetPnlDual(net, p)) + "</span></div>" +
        '<div class="pos-cell"><span class="pos-label">收益率</span><span class="pos-value ' + uplCls + '">' +
        (roi == null ? "—" : fmt(roi, 2) + "%") + "</span></div>";
    return (
      '<div class="pos-card-head">' +
      '<div class="pos-card-symbol"><strong>' + (p.inst_id || "") + "</strong>" +
      '<span class="pos-side-badge ' + sideCls + '">' + optTypeLabel(p.opt_type) + "</span>" +
      sourceBadgeHtml(p) +
      "</div>" +
      headActions +
      "</div>" +
      '<div class="pos-meta">' +
      '<span class="pos-meta-item">持仓来源: ' + sourceText(p) + "</span>" +
      '<span class="pos-meta-item">行权价: ' + fmt(p.strike, 0) + "</span>" +
      '<span class="pos-meta-item">张数: ' + fmt(p.pos, 0) + " · 币量 " + fmt(p.eth_amount, 4) + "</span>" +
      (expAttr
        ? '<span class="pos-meta-item">到期倒计时: ' + expiryCdHtml(expAttr) + "</span>"
        : "") +
      "</div>" +
      '<div class="pos-grid">' +
      '<div class="pos-cell"><span class="pos-label">权利金</span><span class="pos-value">' + premTxt + " " + premCcy + "</span></div>" +
      '<div class="pos-cell"><span class="pos-label">开仓均价</span><span class="pos-value">' + avgTxt + "</span></div>" +
      '<div class="pos-cell"><span class="pos-label">标记价</span><span class="pos-value">' + markTxt + "</span></div>" +
      '<div class="pos-cell"><span class="pos-label">指数价</span><span class="pos-value">' + fmt(p.idx_px, 0) + "</span></div>" +
      '<div class="pos-cell"><span class="pos-label">到期平衡</span><span class="pos-value">' + fmt(p.expiry_be_px, 0) + "</span></div>" +
      '<div class="pos-cell"><span class="pos-label">平掉回本</span><span class="pos-value">' + fmt(p.close_be_px, 0) + "</span></div>" +
      pnlCells +
      '<div class="pos-cell opt-pos-cell--depth"><span class="pos-label">买盘深度</span><span class="pos-value opt-bid-plain">' + fmtCloseLevels(closePreview, tickSz) + "</span></div>" +
      '<div class="pos-cell opt-pos-cell--close"><span class="pos-label">按买盘回收</span><span class="pos-value">' +
      (closePreview.bid_invalid
        ? '<span class="muted">暂无有效买盘</span>'
        : fmtClosePreview(closePreview, hidePnl ? null : p.premium_paid, hub, p)) + "</span></div>" +
      "</div>" +
      (function () {
        const hint = closeGateHint(closePreview);
        return hint ? '<div class="muted opt-bid-invalid-hint">' + hint + "</div>" : "";
      })() +
      (p.target_index != null
        ? (function () {
            const eth = p.eth_amount != null ? Number(p.eth_amount)
              : (Number(p.pos) > 0 ? Number(p.pos) * Number(p.ct_mult || 0.01) : null);
            const strike = Number(p.strike);
            const tgt = Number(p.target_index);
            const prem = Number(p.premium_paid);
            const idx = Number(p.idx_px);
            let profit = null;
            let value = null;
            if (Number.isFinite(tgt) && Number.isFinite(strike) && eth > 0) {
              const o = String(p.opt_type || "").toUpperCase();
              const intrinsic = o === "C" ? Math.max(0, tgt - strike) : o === "P" ? Math.max(0, strike - tgt) : null;
              if (intrinsic != null) {
                value = Math.round(intrinsic * eth * 100) / 100;
                if (!hidePnl && Number.isFinite(prem)) {
                  let premUsd = prem;
                  if (premCcy !== "USDC" && Number.isFinite(idx) && idx > 0) premUsd = prem * idx;
                  profit = Math.round((value - premUsd) * 100) / 100;
                }
              }
            }
            const valueUnit = premCcy !== "USDC" ? " U(估)" : " USDC";
            const profitTxt = profit == null
              ? "—"
              : ((profit > 0 ? "+" : "") + fmtUsdc(profit) + (premCcy !== "USDC" ? " U(估)" : " USDC"));
            const profitCls = profit > 0 ? " pnl-pos" : profit < 0 ? " pnl-neg" : "";
            const hedgeTarget = p.hedge_plan_target || null;
            const managed = hedgeTarget && hedgeTarget.managed_by === "hedge_plan";
            const profitSpan = hidePnl
              ? ""
              : '<span class="pos-value' + profitCls + '">预估盈利 ' + profitTxt + "</span>";
            return (
              '<div class="opt-target-row opt-target-row--ro' + (managed ? " opt-target-row--managed" : "") + '">' +
              '<span class="opt-target-row-label">' + (managed ? "对冲计划 #" + hedgeTarget.plan_id : "委托") + "</span>" +
              '<span class="pos-value">目标 ' + fmt(p.target_index, 1) + "</span>" +
              '<span class="pos-value">价值 ' + (value == null ? "—" : fmtUsdc(value) + valueUnit) + "</span>" +
              profitSpan +
              '<span class="muted opt-target-row-hint">' +
              (managed ? "进行中 · 由对冲计划监控，到位后仅平盈利腿" : "监控中 · 到位按买一限价平") +
              "</span></div>"
            );
          })()
        : "")
    );
  }

  function renderCard(p, opts) {
    opts = opts || {};
    const hub = !!opts.hub;
    const extraCls = hub ? " hub-pos-card hub-opt-pos-card" : " opt-pos-card";
    return (
      '<div class="pos-card' + extraCls + '" data-inst="' + (p.inst_id || "") + '">' +
      renderCardInner(p, opts) +
      "</div>"
    );
  }

  global.OptionsPositionCards = {
    renderCardInner: renderCardInner,
    renderCard: renderCard,
  };
})(typeof window !== "undefined" ? window : globalThis);
