const $ = (id) => document.getElementById(id);

function fmt(value, digits = 5) {
  const n = Number(value || 0);
  if (!Number.isFinite(n)) return "-";
  if (Math.abs(n) >= 100) return n.toFixed(2);
  if (Math.abs(n) >= 1) return n.toFixed(digits).replace(/0+$/, "").replace(/\.$/, "");
  return n.toFixed(8).replace(/0+$/, "").replace(/\.$/, "");
}

function pct(value) {
  const n = Number(value || 0);
  return `${n.toFixed(2)}%`;
}

function esc(value) {
  return String(value ?? "").replace(/[&<>"']/g, (m) => ({
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#39;",
  }[m]));
}

function jsonBlock(title, value) {
  if (value === undefined || value === null || value === "" || (Array.isArray(value) && value.length === 0)) {
    return "";
  }
  const rendered = typeof value === "string" ? value : JSON.stringify(value, null, 2);
  return `<details><summary>${esc(title)}</summary><pre>${esc(rendered)}</pre></details>`;
}

function tags(items = []) {
  return items.map((item) => {
    const cls = item.startsWith("BAD") ? "bad" : item.startsWith("WARN") || item.startsWith("BORDERLINE") ? "warn" : "good";
    return `<span class="tag ${cls}">${esc(item)}</span>`;
  }).join("");
}

function eventCard(row, opts = {}) {
  const side = row.side ? row.side.toUpperCase() : "-";
  const coin = row.coin || "-";
  const setup = row.setup_type || row.event || "-";
  const full = opts.full ?? false;
  return `
    <div class="event">
      <div class="event-head">
        <span class="tag info">${esc(row.time || "-")}</span>
        <span class="tag">${esc(coin)} ${esc(side)}</span>
        <span class="tag">${esc(setup)}</span>
        ${row.confidence ? `<span class="tag">conf ${esc(row.confidence)}</span>` : ""}
      </div>
      <div class="reason">${esc(row.reason || "")}</div>
      ${full ? `
        ${jsonBlock("analysis_steps", row.analysis_steps)}
        ${jsonBlock("playbook_match", row.playbook_match)}
        ${jsonBlock("trade_plan", row.trade_plan)}
        ${jsonBlock("level_reasoning", row.level_reasoning)}
        ${jsonBlock("exit_plan", row.exit_plan)}
        ${jsonBlock("best_candidate", row.best_candidate)}
        ${jsonBlock("evidence", row.evidence)}
        ${jsonBlock("raw_response", row.raw_response)}
      ` : `
        ${jsonBlock("gate_metrics", row.gate_metrics)}
        ${jsonBlock("gate_reasons", row.gate_reasons)}
      `}
    </div>
  `;
}

function renderMetrics(data) {
  const counts = data.counts || {};
  const age = data.last_event_age_s;
  const metrics = [
    ["Rows", data.row_count ?? 0],
    ["LLM", counts.llm_decision ?? 0],
    ["Submits", counts.submit ?? 0],
    ["Rejects", counts.risk_reject ?? 0],
    ["BE Moves", counts.stop_to_break_even ?? 0],
    ["Last Event", age === null ? "-" : `${age}s`],
  ];
  $("metrics").innerHTML = metrics.map(([label, value]) => `
    <div class="metric"><small>${esc(label)}</small><strong>${esc(value)}</strong></div>
  `).join("");
  $("subtitle").textContent = `${data.journal} | window ${data.hours}h | ${data.coin} | auto-refresh 15s`;
}

function renderLatest(row) {
  if (!row || !row.event) {
    $("latestSubmit").innerHTML = `<div class="muted">No submit in window.</div>`;
    return;
  }
  const d = row.derived || {};
  const m = row.exit_plan_metrics || {};
  const noise = m.noise_state || {};
  $("latestSubmit").innerHTML = `
    <div class="trade-title">
      <div>
        <span class="tag info">${esc(row.time)}</span>
        <span class="tag">${esc(row.coin)} ${esc((row.side || "").toUpperCase())}</span>
        <span class="tag">${esc(row.setup_type)}</span>
      </div>
      <div>${tags(d.verdicts || [])}</div>
    </div>
    <div class="price-grid">
      <div class="price-box"><span>Entry</span><b>${fmt(row.entry)}</b></div>
      <div class="price-box"><span>Stop</span><b>${fmt(row.stop)}</b></div>
      <div class="price-box"><span>Target</span><b>${fmt(row.target)}</b></div>
    </div>
    <div style="height:12px"></div>
    <div class="kv">
      <div>R:R</div><div>${fmt(row.rr, 2)} | after noise ${fmt(d.rr_after_noise, 2)}</div>
      <div>Stop / Target</div><div>${pct(d.stop_pct)} / ${pct(d.target_pct)}</div>
      <div>Noise</div><div>floor ${fmt(noise.dynamic_stop_floor)} | stop/floor ${fmt(d.stop_to_noise_floor, 2)} | ATR ${fmt(noise.atr14)} | stop/ATR ${fmt(d.stop_to_atr, 2)}</div>
      <div>Size</div><div>qty ${esc(row.qty)} | margin $${fmt(row.margin_usd, 2)} | x${esc(row.leverage)}</div>
      <div>Reason</div><div>${esc(row.reason || "")}</div>
    </div>
    <div style="height:12px"></div>
    ${jsonBlock("playbook_match", row.playbook_match)}
    ${jsonBlock("level_reasoning", row.level_reasoning)}
    ${jsonBlock("exit_plan", row.exit_plan)}
    ${jsonBlock("exit_plan_metrics", row.exit_plan_metrics)}
  `;
}

function renderList(id, rows, full = false) {
  const el = $(id);
  if (!rows || rows.length === 0) {
    el.innerHTML = `<div class="muted">none</div>`;
    return;
  }
  el.innerHTML = rows.slice().reverse().map((row) => eventCard(row, { full })).join("");
}

async function load() {
  const hours = $("hours").value;
  const coin = $("coin").value.trim();
  const url = `/api/summary?hours=${encodeURIComponent(hours)}&limit=8&coin=${encodeURIComponent(coin)}`;
  const res = await fetch(url, { cache: "no-store" });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();
  renderMetrics(data);
  renderLatest(data.latest_submit);
  renderList("rejects", data.recent_rejects, false);
  renderList("llmDecisions", data.recent_llm_decisions, true);
  renderList("beMoves", data.recent_be_moves, false);
}

async function safeLoad() {
  try {
    await load();
  } catch (err) {
    $("subtitle").textContent = `load failed: ${err.message}`;
  }
}

$("refresh").addEventListener("click", safeLoad);
$("hours").addEventListener("change", safeLoad);
$("coin").addEventListener("input", () => {
  clearTimeout(window.__coinTimer);
  window.__coinTimer = setTimeout(safeLoad, 450);
});

safeLoad();
setInterval(safeLoad, 15000);
