// status.jsx — skintick.io public status page

const { useState, useEffect, useRef, useMemo } = React;

// ─── Data ──────────────────────────────────────────────────────────────────
//
// A component's `history` is a 90-element array, history[0] = today,
// history[89] = 90 days ago. Each cell is one of:
//   "ok" | "minor" | "major" | "maint" | "none"
// Use "none" for days before the component existed.
function noHistory() {
  return new Array(90).fill("none");
}

const DEFAULT_STATUS_ENDPOINT = ["localhost", "127.0.0.1"].includes(window.location.hostname)
  ? "http://127.0.0.1:8080/v1/status"
  : "https://api.skintick.io/v1/status";
const STATUS_ENDPOINT = window.SKINTICK_STATUS_ENDPOINT || DEFAULT_STATUS_ENDPOINT;
const MARKET_NAMES = {
  bitskins: "Bitskins",
  csfloat: "CSFloat",
  dmarket: "DMarket",
  lootfarm: "Loot.farm",
  skinport: "Skinport",
  skinscom: "Skins.com",
  tradeit: "Tradeit",
  waxpeer: "Waxpeer",
  whitemarket: "White.market",
};
const LIVE_STATUS = {
  healthy: "ok",
  stale: "minor",
  degraded: "minor",
  unavailable: "major",
  unknown: "none",
};
const SHOW_STATUS_BANNER = false;

// Headline metrics shown above the components list. Replace `value` with a
// live number string from your metrics backend, or leave as "—".
const METRICS = [
  { label: "requests / min", value: "—", sub: "across all customers" },
  { label: "p50 latency",    value: "—", sub: "median, last 5 min" },
  { label: "p99 latency",    value: "—", sub: "99th pct, last 5 min" },
  { label: "error rate",     value: "—", sub: "last 24h, 4xx + 5xx" },
];

// Each incident:
//   { id, title, status: "investigating" | "identified" | "monitoring" | "resolved" | "scheduled",
//     severity: "minor" | "major" | "maint",
//     started: "2026-05-14 14:08 UTC",
//     components: [...names matching COMPONENTS.name],
//     updates: [{ at, note, tag }]  // newest first
//   }
const INCIDENTS = [];

// ─── utility ─────────────────────────────────────────────────────────────
const STATUS_LABEL = {
  ok:    "operational",
  minor: "minor degradation",
  major: "partial outage",
  maint: "under maintenance",
  none:  "—",
};

function overallStatus(components) {
  // Take the worst component status as overall.
  const order = ["major", "minor", "maint", "ok"];
  for (const s of order) if (components.some((c) => c.status === s)) return s;
  return "none";
}

function overallText(s) {
  switch (s) {
    case "ok":    return "All systems operational";
    case "minor": return "Some systems experiencing minor issues";
    case "major": return "Major outage in progress";
    case "maint": return "Scheduled maintenance in progress";
    default:      return "Awaiting first collector run";
  }
}

function componentStatusFromMarketStatus(status) {
  return LIVE_STATUS[status] || "none";
}

function componentDesc(market) {
  const source = market.market_type === "instant_bot" ? "instant-bot" : market.market_type;
  const bits = [`${source} marketplace`];
  if (market.last_success_at) bits.push("last success " + formatRelativeTimestamp(market.last_success_at));
  if (market.last_error_at) bits.push("last error " + formatRelativeTimestamp(market.last_error_at));
  return bits.join(" · ");
}

function marketComponent(market) {
  return {
    id: market.market,
    group: "markets",
    name: (MARKET_NAMES[market.market] || market.market) + " integration",
    desc: componentDesc(market),
    status: componentStatusFromMarketStatus(market.status),
    uptime90: null,
    history: noHistory(),
  };
}

function restComponent(fetchState) {
  return {
    id: "rest",
    group: "core",
    name: "REST API",
    desc: fetchState === "error"
      ? "Status endpoint is not reachable."
      : "Read endpoints for prices, history, and spreads.",
    status: fetchState === "ready" ? "ok" : fetchState === "error" ? "major" : "none",
    uptime90: null,
    history: noHistory(),
  };
}

function formatRelativeTimestamp(value) {
  const ts = new Date(value).getTime();
  if (!Number.isFinite(ts)) return "unknown";
  const diffSeconds = Math.max(0, Math.round((Date.now() - ts) / 1000));
  if (diffSeconds < 60) return diffSeconds + "s ago";
  if (diffSeconds < 3600) return Math.round(diffSeconds / 60) + "m ago";
  if (diffSeconds < 86400) return Math.round(diffSeconds / 3600) + "h ago";
  return Math.round(diffSeconds / 86400) + "d ago";
}

function formatCheckedAt(value) {
  if (!value) return "waiting for first check";
  return "last checked " + value.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
}

// ─── Components ──────────────────────────────────────────────────────────

function Nav() {
  return (
    <nav className="status-nav">
      <a href="../index.html" className="brand">
        <img src="/logos/favicon.svg" alt="" className="brand-mark" />
        <span>skintick.io</span>
      </a>
      <div className="nav-links">
        <a href="../index.html#about">about</a>
        <a href="../index.html#markets">markets</a>
        <a href="../index.html#api">api</a>
        <a href="../index.html#pricing">pricing</a>
        <a href="status.html" className="text-link active">status</a>
        <a href="../dashboard.html" className="btn btn-secondary">dashboard</a>
      </div>
    </nav>
  );
}

function StatusBanner({ status, checkedAt }) {
  const cls = "status-banner status-banner-" + status;
  return (
    <section className={cls}>
      <div className="status-banner-inner">
        <div className="status-banner-left">
          <div className="status-banner-icon">
            {status === "ok"    ? "✓"
            : status === "minor" ? "!"
            : status === "major" ? "×"
            : status === "maint" ? "⚙"
            : "—"}
          </div>
          <div>
            <h1 className="status-banner-title">{overallText(status)}</h1>
            <div className="status-banner-sub">
              {formatCheckedAt(checkedAt)} · refreshes every 30s
            </div>
          </div>
        </div>
        <div className="status-banner-right">
          <a href="#subscribe" className="btn btn-secondary">subscribe to updates</a>
        </div>
      </div>
    </section>
  );
}

function MetricRow() {
  return (
    <section className="metric-row">
      {METRICS.map((m) => (
        <div key={m.label} className="metric">
          <div className="metric-label">{m.label}</div>
          <div className="metric-value">{m.value}</div>
          <div className="metric-sub">{m.sub}</div>
        </div>
      ))}
    </section>
  );
}

function FrameBox({ label, children, id, actions }) {
  return (
    <section className="framebox" id={id}>
      <div className="framebox-label">{label}</div>
      {actions && <div className="framebox-actions">{actions}</div>}
      <div className="framebox-body">{children}</div>
    </section>
  );
}

function ComponentRow({ comp }) {
  return (
    <div className="comp-row">
      <div className="comp-head">
        <div className="comp-meta">
          <div className="comp-name">
            <span className={"comp-dot status-dot-" + comp.status} />
            {comp.name}
          </div>
          {comp.desc && <div className="comp-desc">{comp.desc}</div>}
        </div>
        <div className="comp-tail">
          <span className="comp-uptime">{comp.uptime90 != null ? comp.uptime90.toFixed(2) + "%" : "—"} <span className="comp-uptime-sub">90d uptime</span></span>
          <span className={"comp-status comp-status-" + comp.status}>{STATUS_LABEL[comp.status]}</span>
        </div>
      </div>
      <Heatbar history={comp.history} />
    </div>
  );
}

function Heatbar({ history }) {
  // history[0] = today, history[89] = 90 days ago
  // Render oldest-to-newest left → right so "today" is on the right edge.
  const days = [...history].reverse();
  return (
    <div className="heatbar" aria-label="90-day uptime history">
      <div className="heatbar-bars">
        {days.map((s, i) => (
          <div
            key={i}
            className={"heatcell heat-" + s}
            title={`${s === "ok" ? "Operational" : s === "minor" ? "Minor issue" : s === "major" ? "Major outage" : s === "maint" ? "Maintenance" : "No data"} · ${89 - i} day${89 - i === 1 ? "" : "s"} ago`}
          />
        ))}
      </div>
      <div className="heatbar-axis">
        <span>90 days ago</span>
        <span>today</span>
      </div>
    </div>
  );
}

function ComponentsSection({ components }) {
  const core = components.filter((c) => c.group === "core");
  const markets = components.filter((c) => c.group === "markets");
  return (
    <>
      <FrameBox label="core services">
        <div className="comp-list">
          {core.map((c) => <ComponentRow key={c.id} comp={c} />)}
        </div>
      </FrameBox>
      <FrameBox label="marketplace integrations">
        <div className="comp-list">
          {markets.map((c) => <ComponentRow key={c.id} comp={c} />)}
        </div>
      </FrameBox>
    </>
  );
}

function IncidentsSection() {
  return (
    <FrameBox label="incidents — last 90 days" actions={INCIDENTS.length > 0 ? <a href="#" className="row-link">full history →</a> : null}>
      {INCIDENTS.length === 0 ? (
        <div className="incident-empty">No incidents reported in the last 90 days.</div>
      ) : (
        <div className="incident-list">
          {INCIDENTS.map((i) => (
            <Incident key={i.id} incident={i} />
          ))}
        </div>
      )}
    </FrameBox>
  );
}

function Incident({ incident }) {
  return (
    <article className={"incident incident-" + incident.severity}>
      <div className="incident-head">
        <div className="incident-title-row">
          <span className={"incident-badge sev-" + incident.severity}>{incident.severity}</span>
          <h3 className="incident-title">{incident.title}</h3>
          <span className={"incident-status istat-" + incident.status}>{incident.status}</span>
        </div>
        <div className="incident-meta">
          started {incident.started} · {incident.components.join(", ")}
        </div>
      </div>
      <ol className="incident-timeline">
        {incident.updates.map((u, i) => (
          <li key={i} className="incident-update">
            <span className={"update-tag update-tag-" + u.tag}>{u.tag}</span>
            <span className="update-time">{u.at}</span>
            <span className="update-note">{u.note}</span>
          </li>
        ))}
      </ol>
    </article>
  );
}

function Legend() {
  return (
    <section className="legend">
      <span className="legend-item"><span className="legend-swatch heat-ok" /> operational</span>
      <span className="legend-item"><span className="legend-swatch heat-minor" /> minor</span>
      <span className="legend-item"><span className="legend-swatch heat-major" /> outage</span>
      <span className="legend-item"><span className="legend-swatch heat-maint" /> maintenance</span>
      <span className="legend-item"><span className="legend-swatch heat-none" /> no data</span>
    </section>
  );
}

function Subscribe() {
  const [email, setEmail] = useState("");
  const [done, setDone] = useState(false);
  return (
    <section className="subscribe" id="subscribe">
      <div className="subscribe-inner">
        <h2>Get notified when something breaks.</h2>
        <p>We'll email you when a new incident is opened, its status changes, or it's resolved. No marketing.</p>
        {!done ? (
          <form className="subscribe-form" onSubmit={(e) => { e.preventDefault(); if (email) setDone(true); }}>
            <input
              className="subscribe-input"
              type="email"
              placeholder="you@yourdomain.com"
              required
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />
            <button type="submit" className="btn btn-primary subscribe-btn">subscribe</button>
            <a href="#" className="subscribe-rss">RSS ↗</a>
          </form>
        ) : (
          <div className="subscribe-thanks">
            <span className="thanks-check">✓</span> subscribed. confirm via the link sent to <b>{email}</b>.
          </div>
        )}
      </div>
    </section>
  );
}

function Footer() {
  return (
    <footer className="footer">
      <div className="footer-grid">
        <div className="footer-col">
          <div className="footer-brand">
            <img src="/logos/favicon.svg" alt="" className="footer-brand-mark" />
            <span>skintick.io</span>
          </div>
          <div className="footer-tag">price intelligence for the CS2 skin economy.</div>
        </div>
        <div className="footer-col">
          <div className="footer-h">product</div>
          <a href="../index.html#api">API reference</a>
          <a href="#">WebSocket streams</a>
          <a href="#">Bulk exports</a>
          <a href="#">Changelog</a>
        </div>
        <div className="footer-col">
          <div className="footer-h">markets</div>
          <a href="../buff.html">BUFF163</a>
          <a href="#">CSFloat</a>
          <a href="#">Skinport</a>
          <a href="#">Steam</a>
        </div>
        <div className="footer-col">
          <div className="footer-h">company</div>
          <a href="../index.html#about">About</a>
          <a href="../index.html#pricing">Pricing</a>
          <a href="status.html">Status</a>
          <a href="../dashboard.html">Dashboard</a>
        </div>
      </div>
      <div className="footer-bottom">
        <span>© 2026 skintick.io</span>
        <span>not affiliated with valve. all trademarks belong to their owners.</span>
      </div>
    </footer>
  );
}

// ─── App ──────────────────────────────────────────────────────────────────
function App() {
  const [markets, setMarkets] = useState([]);
  const [fetchState, setFetchState] = useState("loading");
  const [checkedAt, setCheckedAt] = useState(null);

  useEffect(() => {
    let cancelled = false;

    async function loadStatus() {
      try {
        const res = await fetch(STATUS_ENDPOINT, { headers: { Accept: "application/json" } });
        if (!res.ok) throw new Error("status request failed");
        const body = await res.json();
        if (!cancelled) {
          setMarkets(Array.isArray(body.markets) ? body.markets : []);
          setFetchState("ready");
          setCheckedAt(new Date());
        }
      } catch (err) {
        if (!cancelled) {
          setFetchState("error");
          setCheckedAt(new Date());
        }
      }
    }

    loadStatus();
    const timer = setInterval(loadStatus, 30000);
    return () => {
      cancelled = true;
      clearInterval(timer);
    };
  }, []);

  const components = useMemo(() => [
    restComponent(fetchState),
    ...markets.map(marketComponent),
  ], [fetchState, markets]);
  const marketComponents = components.filter((c) => c.group === "markets");
  const status = fetchState === "ready" && marketComponents.length > 0 && marketComponents.every((c) => c.status === "none")
    ? "none"
    : overallStatus(components);

  return (
    <div className="status-page">
      <Nav />
      {SHOW_STATUS_BANNER && <StatusBanner status={status} checkedAt={checkedAt} />}
      <main className="status-main">
        <MetricRow />
        <Legend />
        <ComponentsSection components={components} />
        <IncidentsSection />
      </main>
      <Subscribe />
      <Footer />
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
