// ============================================================================
// V2 panels: Bridge, Cron, FreshnessGrid, WarningTimeline, OverallStatusHero
// ============================================================================

// Hero status: 첫 화면에서 "정상/경고/위험"을 10초 안에 판단하는 패널
const OverallHero = ({ data }) => {
  const status = data.overallStatus;
  const parityStatus = data.bot.parityStatus || data.bot.parity;
  const cronJobs = Array.isArray(data.cron) ? data.cron : [];
  const gapMonitorJob = cronJobs.find((job) => job?.name === "시장 데이터 gap 감시");
  const gapMonitorStatus = gapMonitorJob?.status;
  const gapMonitorValue = gapMonitorStatus === "ok"
    ? "정상"
    : gapMonitorStatus === "warn"
    ? "경고"
    : gapMonitorStatus === "fail"
    ? "실패"
    : "미확인";
  const tone = status === "fail" ? "danger" : status === "warn" ? "warn" : "success";
  const label = status === "fail" ? "위험" : status === "warn" ? "경고" : "정상";
  const headline = status === "fail"
    ? "운영 위험 — 즉시 확인이 필요합니다"
    : status === "warn"
    ? "주의 — 일부 지표가 임계값을 초과했습니다"
    : "모든 시스템이 정상 동작 중입니다";

  // 코인(Aster) 계정은 MT5 가정의 체크를 코인-네이티브로 치환한다(venue 패스스루 기반).
  const isCoin = data.bridge?.venue === "aster";
  const cb = data.bridge || {};
  const coinSafe = Array.isArray(cb.safeModeMarkets) ? cb.safeModeMarkets : [];
  const checks = isCoin ? [
    { label: "코인봇", value: cb.alive ? "실행 중" : "중단", ok: !!cb.alive },
    { label: "Aster 연결", value: cb.equityAgeSeconds == null ? "미확인" : (cb.equityAgeSeconds <= 1200 ? "신선" : `${cb.equityAgeSeconds}s 지연`), ok: cb.equityAgeSeconds != null && cb.equityAgeSeconds <= 1200 },
    { label: "긴급정지", value: cb.killState ? cb.killState : "없음", ok: !cb.killState },
    { label: "일일 손익", value: cb.dailyLossPct == null ? "미확인" : `${cb.dailyLossPct}%`, ok: cb.dailyLossPct == null || cb.dailyLossPct >= -5 },
    { label: "SAFE MODE", value: coinSafe.length ? coinSafe.join(",") : "없음", ok: coinSafe.length === 0 },
    { label: "활성 부모", value: cb.activeParents == null ? "—" : `${cb.activeParents}개`, ok: true },
  ] : [
    { label: "라이브 프로세스", value: data.bot.status === "running" ? "실행 중" : "중단", ok: data.bot.status === "running" },
    { label: "MT5 브리지", value: data.bridge.stale ? "지연" : data.bridge.status === "connected" ? "연결됨" : "끊김", ok: data.bridge.status === "connected" && !data.bridge.stale },
    { label: "동기화 검증", value: parityStatus === "ok" ? "정상" : parityStatus === "paused" ? "중지됨" : "실패", ok: parityStatus === "ok" },
    { label: "DB 동기화", value: data.kpi.dbStatus === "ok" ? "정상" : data.kpi.dbStatus === "warn" ? "경고" : "위험", ok: data.kpi.dbStatus === "ok" },
    { label: "분리된 포지션", value: data.orphanPositions === 0 ? "없음" : `${data.orphanPositions}건`, ok: data.orphanPositions === 0 },
    { label: "DB gap 감시", value: gapMonitorValue, ok: gapMonitorStatus === "ok" },
  ];

  return (
    <div className={`tb-hero tb-hero-${tone}`}>
      <div className="tb-hero-mark">
        <span className={`tb-hero-dot tone-${status === "fail" ? "fail" : status === "warn" ? "warn" : "ok"}`}>
          <Dot status={status === "fail" ? "fail" : status === "warn" ? "warn" : "ok"} pulse />
        </span>
        <div>
          <div className="tb-hero-label">{label}</div>
          <div className="tb-hero-head">{headline}</div>
        </div>
      </div>
      <div className="tb-hero-checks">
        {checks.map((c) => (
          <div key={c.label} className="tb-hero-check">
            <Icon name={c.ok ? "check" : "alert"} size={12} />
            <span className="tb-hero-check-label">{c.label}</span>
            <span className={`mono tb-hero-check-val ${c.ok ? "pnl-pos" : "pnl-neg"}`}>{String(c.value)}</span>
          </div>
        ))}
      </div>
    </div>
  );
};

// 코인(Aster) 운영 상태 라벨 — shim /status enum 매핑
const COIN_STATUS_LABELS = {
  connected: "연결됨",
  halted_flatten: "전량청산 정지",
  halted_kill: "신규진입 정지",
  safe_mode: "세이프모드",
  disconnected: "끊김",
};

// 코인봇 전용 브리지 패널 — MT5 필드(마진/레버리지/재연결) 대신 코인-네이티브 운영 신호
const CoinBridgePanel = ({ bridge }) => {
  const b = bridge || {};
  const ok = b.status === "connected" && !b.stale;
  const statusLabel = b.stale ? "지연" : (COIN_STATUS_LABELS[b.status] || "끊김");
  const cur = b.currency || "USDT";
  const safe = Array.isArray(b.safeModeMarkets) ? b.safeModeMarkets : [];
  // 데이터 피드 신선도 — 코인은 5m 단일소스→리샘플(DB sync 없음). 마켓별 TF 완성봉/충분여부/매크로 게이트.
  const FEED_TFS = ["15m", "1h", "4h", "1d", "1w"];
  const ageStr = (s) =>
    s == null ? "—" : s < 90 ? `${s}초` : s < 5400 ? `${Math.round(s / 60)}분` : `${(s / 3600).toFixed(1)}시간`;
  const feedRows = Object.entries(b.coinFeeds || {})
    .sort((a, x) => a[0].localeCompare(x[0]))
    .map(([market, m]) => ({ market, m: m || {} }));
  // 최근 거래기록 — 닫힌 grid 부모(가장 최근부터)
  const dealRows = (Array.isArray(b.coinDeals) ? b.coinDeals : []).slice().reverse();
  const dealTime = (t) => (t ? String(t).slice(5, 16).replace("T", " ") : "—");
  const aw = b.coinApiWeight || {};
  const awPct = aw.pct;  // 사용% (한도 2400/분/IP)
  const awClass = awPct == null ? "" : awPct >= 80 ? "pnl-neg" : awPct >= 50 ? "pnl-flat" : "";
  const awText = aw.used == null ? "—" : `${aw.used} / ${aw.limit || 2400}${awPct == null ? "" : ` (${awPct}%)`}`;
  return (
    <Card tone={ok ? "neutral" : "danger"} indicator={!ok} padding={14}>
      <div className="tb-section-head" style={{ paddingBottom: 8 }}>
        <div className="label-caps">코인봇 / Aster</div>
        <Badge kind={ok ? "success" : "danger"}>
          <Dot status={ok ? "ok" : "fail"} pulse={ok} /> {statusLabel}
        </Badge>
      </div>
      <div className="tb-kv-grid">
        <div className="tb-kv"><span className="tb-kv-k">계정</span><span className="mono tb-kv-v">{b.accountDisplay || b.account || "확인중"}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">거래소</span><span className="mono tb-kv-v">Aster {b.botEnv || "testnet"}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">브리지</span><span className="mono tb-kv-v">{b.bridgeUrl || "확인중"}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">자본</span><span className="mono tb-kv-v">{fmt.num(b.equity)} {cur}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">일일 손익</span><span className={`mono tb-kv-v ${b.dailyLossPct == null ? "" : b.dailyLossPct >= 0 ? "pnl-pos" : "pnl-neg"}`}>{b.dailyLossPct == null ? "—" : `${b.dailyLossPct}%`}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">긴급정지</span><span className={`mono tb-kv-v ${b.killState ? "pnl-neg" : ""}`}>{b.killState || "없음"}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">SAFE MODE</span><span className={`mono tb-kv-v ${safe.length ? "pnl-neg" : ""}`}>{safe.length ? safe.join(",") : "없음"}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">활성 부모</span><span className="mono tb-kv-v">{b.activeParents == null ? "—" : `${b.activeParents}개`}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">상태 나이</span><span className="mono tb-kv-v">{b.equityAgeSeconds == null ? "—" : `${b.equityAgeSeconds}초`}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">API weight</span><span className={`mono tb-kv-v ${awClass}`} title={`Aster REST 분당 한도 ${aw.limit || 2400}/IP${aw.age_seconds == null ? "" : ` · ${aw.age_seconds}초 전`}`}>{awText}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">마지막 핑</span><span className="mono tb-kv-v">{fmt.ago(b.lastPingAt)}</span></div>
      </div>
      {feedRows.length > 0 && (
        <div style={{ marginTop: 12 }}>
          <div className="label-caps" style={{ paddingBottom: 6 }}>데이터 피드 · 5m 단일소스 → 리샘플 (DB 동기화 없음)</div>
          <table className="tb-table">
            <thead>
              <tr>
                <th>마켓</th>
                <th className="r">5m 나이</th>
                {FEED_TFS.map((tf) => <th key={tf} className="r">{tf}</th>)}
                <th>매크로</th>
              </tr>
            </thead>
            <tbody>
              {feedRows.map(({ market, m }) => {
                const tfs = m.tfs || {};
                const srcStale = m.src_age_seconds != null && m.src_age_seconds > 900;
                if (m.error) {
                  return <tr key={market}><td className="mono tb-strong">{market}</td><td className="tb-meta pnl-neg" colSpan={FEED_TFS.length + 2}>{m.error}</td></tr>;
                }
                return (
                  <tr key={market}>
                    <td className="mono tb-strong">{market}</td>
                    <td className={`r mono ${srcStale ? "pnl-neg" : ""}`}>{ageStr(m.src_age_seconds)}</td>
                    {FEED_TFS.map((tf) => {
                      const c = tfs[tf] || {};
                      return (
                        <td key={tf} className={`r mono ${c.ready ? "" : "pnl-neg"}`} title={`${c.bars || 0}봉${c.last ? " · " + c.last : ""}`}>
                          {c.ready ? "✓" : "✗"}<span className="tb-meta"> {c.bars == null ? "—" : c.bars}</span>
                        </td>
                      );
                    })}
                    <td><Badge kind={m.macro_gate ? "success" : "warn"}>{m.macro_gate ? "활성" : "휴면"}</Badge></td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          <div className="tb-meta" style={{ marginTop: 6 }}>
            ✓=지표/게이트 충분(15m·1h·4h≥100, 1d·1w≥60봉). 매크로='활성'이려면 1d·1w 둘 다 ≥60주봉(현재 5m피드 ~120일이라 1w 휴면).
          </div>
        </div>
      )}
      {dealRows.length > 0 && (
        <div style={{ marginTop: 12 }}>
          <div className="label-caps" style={{ paddingBottom: 6 }}>최근 거래기록 (닫힌 grid 부모)</div>
          <table className="tb-table">
            <thead>
              <tr>
                <th>시각</th><th>마켓</th><th>방향</th>
                <th className="r">수량</th><th className="r">손익</th><th>사유</th>
              </tr>
            </thead>
            <tbody>
              {dealRows.map((d, i) => (
                <tr key={d.ticket || i}>
                  <td className="mono tb-meta">{dealTime(d.time)}</td>
                  <td className="mono tb-strong">{d.symbol}</td>
                  <td className={`mono ${d.type === 0 ? "pnl-pos" : "pnl-neg"}`}>{d.type === 0 ? "롱" : "숏"}</td>
                  <td className="r mono">{d.volume}</td>
                  <td className={`r mono ${d.profit == null ? "" : d.profit >= 0 ? "pnl-pos" : "pnl-neg"}`}>{d.profit == null ? "—" : fmt.num(d.profit)}</td>
                  <td className="tb-meta" title={d.comment || ""}>{String(d.comment || "").replace(/^cb /, "").split(" ")[0] || "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </Card>
  );
};

const BridgePanel = ({ bridge }) => {
  if (bridge?.venue === "aster") return <CoinBridgePanel bridge={bridge} />;
  const isFreshConnected = bridge.status === "connected" && !bridge.stale;
  const tone = isFreshConnected ? "ok" : "fail";
  const statusLabel = bridge.stale ? "지연" : bridge.status === "connected" ? "연결됨" : "끊김";
  const currency = bridge.currency || "USD";
  return (
    <Card tone={tone === "fail" ? "danger" : "neutral"} indicator={tone === "fail"} padding={14}>
      <div className="tb-section-head" style={{ paddingBottom: 8 }}>
        <div className="label-caps">브로커 / MT5 브리지</div>
        <Badge kind={tone === "ok" ? "success" : "danger"}>
          <Dot status={tone} pulse={tone === "ok"} /> {statusLabel}
        </Badge>
      </div>
      <div className="tb-kv-grid">
        <div className="tb-kv"><span className="tb-kv-k">계정</span><span className="mono tb-kv-v">{bridge.accountDisplay || bridge.account || "확인중"}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">브로커 서버</span><span className="mono tb-kv-v">{bridge.brokerServer || "확인중"}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">브로커</span><span className="mono tb-kv-v">{bridge.company || bridge.accountName || "확인중"}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">브리지</span><span className="mono tb-kv-v">{bridge.bridgeUrl || bridge.server || "확인중"}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">자본</span><span className="mono tb-kv-v">{fmt.num(bridge.equity)} {currency}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">잔고</span><span className="mono tb-kv-v">{fmt.num(bridge.balance)} {currency}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">지연</span><span className="mono tb-kv-v">{bridge.latencyMs == null ? "—" : `${bridge.latencyMs} ms`}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">마지막 핑</span><span className="mono tb-kv-v">{fmt.ago(bridge.lastPingAt)}</span></div>
        {bridge.stale && <div className="tb-kv"><span className="tb-kv-k">캐시 나이</span><span className="mono tb-kv-v">{bridge.staleAgeSeconds == null ? "확인중" : `${fmt.num(bridge.staleAgeSeconds, 0)}초`}</span></div>}
        <div className="tb-kv"><span className="tb-kv-k">24h 재연결</span><span className={`mono tb-kv-v ${bridge.reconnects24h > 0 ? "pnl-flat" : ""}`}>{bridge.reconnects24h == null ? "0회" : `${bridge.reconnects24h}회`}</span></div>
        <div className="tb-kv"><span className="tb-kv-k">레버리지</span><span className="mono tb-kv-v">{bridge.leverage ? `1:${fmt.num(bridge.leverage, 0)}` : "확인중"}</span></div>
      </div>
    </Card>
  );
};

const CronPanel = ({ jobs }) => {
  return (
    <div className="tb-card" style={{ padding: 0 }}>
      <div className="tb-section-head" style={{ padding: "12px 14px 8px" }}>
        <div className="label-caps">Cron · 스케줄러</div>
        <span className="tb-meta">{jobs.length}개 작업</span>
      </div>
      <table className="tb-table">
        <thead>
          <tr>
            <th>작업</th><th>스케줄</th><th>마지막 실행</th>
            <th className="r">소요</th><th>상태</th>
          </tr>
        </thead>
        <tbody>
          {jobs.map((j) => (
            <tr key={j.name}>
              <td className="tb-strong">
                {j.name}
                {j.scope && (
                  <span className="tb-meta" style={{ marginLeft: 8 }}>
                    {j.scope === "shared" ? "공용" : "계정"}
                  </span>
                )}
              </td>
              <td className="tb-meta">{j.schedule}</td>
              <td className="tb-meta">{fmt.ago(j.lastRunAt)}</td>
              <td className="r">{(j.durationMs / 1000).toFixed(2)}s</td>
              <td>
                <Badge kind={j.status === "ok" ? "success" : j.status === "warn" ? "warn" : "danger"}>
                  {j.status === "ok" ? "정상" : j.status === "warn" ? "경고" : "실패"}
                </Badge>
                {j.note && <span className="tb-meta" style={{ marginLeft: 8 }}>{j.note}</span>}
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};

// Data freshness by symbol — grid: rows=symbols, cols=tables
const FreshnessGrid = ({ db }) => {
  const tables = [
    { id: "bars5m",  label: "5분 봉" },
    { id: "bars15m", label: "15분 봉" },
    { id: "bars1h",  label: "1시간 봉" },
  ];
  const symbols = Array.from(
    new Set(tables.flatMap((t) => (db[t.id] || []).map((r) => r.symbol)).filter(Boolean)),
  );
  const lookup = (tbl, sym) => db[tbl].find((r) => r.symbol === sym);
  // Detect weekend: any cell flagged weekendSuppressed by server
  const isWeekend = tables.some((t) => (db[t.id] || []).some((r) => r.weekendSuppressed));

  return (
    <div className="tb-card" style={{ padding: 14 }}>
      <div className="tb-section-head" style={{ paddingBottom: 10 }}>
        <div className="label-caps">DB 동기화 · 심볼별</div>
        <span className="tb-meta">
          UTC · 임계값 대비 경과
          {isWeekend && <Badge kind="neutral" style={{ marginLeft: 8 }}>주말 (FX/지수 휴장)</Badge>}
        </span>
      </div>
      <div className="tb-fresh-grid">
        <div className="tb-fresh-cell tb-fresh-head"></div>
        {tables.map((t) => (
          <div key={t.id} className="tb-fresh-cell tb-fresh-head">{t.label}</div>
        ))}
        {symbols.map((sym) => (
          <React.Fragment key={sym}>
            <div className="tb-fresh-cell tb-fresh-sym mono">{sym}</div>
            {tables.map((t) => {
              const r = lookup(t.id, sym);
              if (!r) return <div key={t.id} className="tb-fresh-cell tb-fresh-na">—</div>;
              const suppressedLabel = r.weekendSuppressed ? "주말" : r.closedMarketSuppressed ? "휴장" : "";
              const isSuppressed = Boolean(suppressedLabel);
              return (
                <div key={t.id} className={`tb-fresh-cell tb-fresh-${r.status}`} title={isSuppressed ? `${suppressedLabel} — 갱신 정지 정상` : ""}>
                  <span className="tb-fresh-time mono">{fmt.time(r.lastTs)}</span>
                  <span className="tb-fresh-ago mono">{fmt.ago(r.lastTs)}{isSuppressed ? ` · ${suppressedLabel}` : ""}</span>
                </div>
              );
            })}
          </React.Fragment>
        ))}
      </div>
    </div>
  );
};

const WarningTimeline = ({ events }) => {
  // 시간 역순으로 24h 분포를 보여주는 가벼운 timeline
  const warnsAndFails = events.filter((e) => e.level !== "info");
  return (
    <div className="tb-card" style={{ padding: 14 }}>
      <div className="tb-section-head" style={{ paddingBottom: 10 }}>
        <div className="label-caps">경고 타임라인 · 24h</div>
        <Badge kind="warn">{warnsAndFails.length}건</Badge>
      </div>
      <div className="tb-timeline">
        {warnsAndFails.map((e, i) => (
          <div key={i} className={`tb-timeline-row tb-timeline-${e.level}`}>
            <span className={`tb-timeline-dot tone-${e.level === "fail" ? "fail" : "warn"}`}>
              <Dot status={e.level === "fail" ? "fail" : "warn"} />
            </span>
            <span className="mono tb-meta tb-timeline-time">{fmt.ago(e.ts)}</span>
            <span className="tb-timeline-msg">{e.msg}</span>
            <span className="mono tb-meta tb-timeline-detail">{e.detail}</span>
          </div>
        ))}
      </div>
    </div>
  );
};

Object.assign(window, { OverallHero, BridgePanel, CoinBridgePanel, CronPanel, FreshnessGrid, WarningTimeline });
