// ═══════════════════════════════════════════════════════════════
// tax-bullet-parser — renders the SSOT-driven tax-visual bullet
// strings used by MezzanineTaxFlow. Three features:
//
//   1. **bold**            becomes <b>bold</b>
//   2. {placeholder}       becomes a live-calculated value from `calc`
//   3. | label | value |   line attaches to the previous bullet as a
//                          sub-grid row (rendered as label | value with
//                          space-between). Inline **bold** and
//                          {placeholders} still work inside cells.
//
// Unknown placeholders render literally (defensive: bad seed data
// must not crash the page).
//
// Exposed as globals:
//   window.renderTaxBullet(str, calc)    -> ReactNode
//   window.groupTaxBullets(strings)      -> [{ text, rows }]
//   window.__tokenizeTaxBullet(str)      -> Array (for tests)
// ═══════════════════════════════════════════════════════════════

(function () {
  const PLACEHOLDERS = {
    principal:     (c) => fmtCurrency(c.principal),
    grossInterest: (c) => fmtCurrency(c.gross),
    wht:           (c) => fmtCurrency(c.wht),
    netToLlc:      (c) => fmtCurrency(c.netToLlc),
    usGrossTax:    (c) => fmtCurrency(c.usGrossTax),
    ftc:           (c) => fmtCurrency(c.ftc),
    netUsTax:      (c) => fmtCurrency(c.netUsTax),
    totalTax:      (c) => fmtCurrency(c.totalTax),
    moic:          (c) => c.moic != null ? `${c.moic.toFixed(1)}×` : "—",
    whtRatePct:    (c) => `${Math.round(c.whtRate * 100)}%`,
    usRatePct:     (c) => `${Math.round(c.usRate * 100)}%`,
  };

  function fmtCurrency(n) {
    if (typeof window.formatCurrency === "function") return window.formatCurrency(n);
    if (n == null || isNaN(n)) return "—";
    return "$" + Math.round(n).toLocaleString();
  }

  // Splits a string on **...** + {...} markers, keeping the markers as
  // tokens so the caller can rebuild a React fragment.
  // Returns: Array<{ kind: "text" | "bold" | "ph", value: string }>
  function tokenize(str) {
    const out = [];
    const re = /\*\*([^*]+)\*\*|\{([a-zA-Z]+)\}/g;
    let last = 0;
    let m;
    while ((m = re.exec(str)) !== null) {
      if (m.index > last) out.push({ kind: "text", value: str.slice(last, m.index) });
      if (m[1] !== undefined) out.push({ kind: "bold", value: m[1] });
      else out.push({ kind: "ph", value: m[2] });
      last = m.index + m[0].length;
    }
    if (last < str.length) out.push({ kind: "text", value: str.slice(last) });
    return out;
  }

  // Substitutes {placeholder} markers in a plain string (no ** handling).
  // Used both at the top level and recursively inside bold content so that
  // authors can write **{grossInterest}** and have the value substituted.
  function substitutePlaceholders(str, calc) {
    const re = /\{([a-zA-Z]+)\}/g;
    const out = [];
    let last = 0;
    let m;
    let i = 0;
    while ((m = re.exec(str)) !== null) {
      if (m.index > last) out.push(str.slice(last, m.index));
      const name = m[1];
      const fn = PLACEHOLDERS[name];
      if (!fn) {
        out.push(m[0]);
      } else {
        let rendered;
        try { rendered = fn(calc); }
        catch (err) { rendered = m[0]; }
        out.push(React.createElement(React.Fragment, { key: "ph-" + (i++) }, rendered));
      }
      last = m.index + m[0].length;
    }
    if (last < str.length) out.push(str.slice(last));
    return out;
  }

  window.renderTaxBullet = function (str, calc) {
    const tokens = tokenize(str);
    return tokens.map(function(tok, i) {
      if (tok.kind === "text") return substitutePlaceholders(tok.value, calc);
      if (tok.kind === "bold") return React.createElement("b", { key: i }, substitutePlaceholders(tok.value, calc));
      var fn = PLACEHOLDERS[tok.value];
      if (!fn) return "{" + tok.value + "}";
      try {
        return React.createElement(React.Fragment, { key: i }, fn(calc));
      } catch (err) {
        return "{" + tok.value + "}";
      }
    });
  };

  // Groups a flat list of bullet strings into parent bullets + child rows.
  // A line matching ^| ... | ... |$ attaches as a row to the previous parent.
  // If the first line is a sub-row (no parent above), it gets promoted to a
  // standalone bullet so it doesn't disappear silently.
  //
  // Returns: Array<{ text: string, rows: Array<string[]> | null }>
  window.groupTaxBullets = function (bullets) {
    const out = [];
    (bullets || []).forEach(function (raw) {
      const line = (raw || "").trim();
      if (!line) return;
      const isSubRow = /^\|.*\|$/.test(line);
      if (isSubRow && out.length > 0) {
        const cells = line.slice(1, -1).split("|").map(function (s) { return s.trim(); });
        out[out.length - 1].rows = (out[out.length - 1].rows || []).concat([cells]);
      } else {
        out.push({ text: line, rows: null });
      }
    });
    return out;
  };

  window.__tokenizeTaxBullet = tokenize;
})();
