// Dashboard — workspace overview matching the real app's information layout
const { useState, useEffect } = React;

function PageHead({ eyebrow, title, sub, children }) {
  return (
    <div className="page-head">
      <div>
        <div className="eyebrow">{eyebrow}</div>
        <h1 className="page-title">{title}</h1>
        {sub && <p className="page-sub">{sub}</p>}
      </div>
      {children && <div className="flex" style={{ gap: 8 }}>{children}</div>}
    </div>
  );
}
window.PageHead = PageHead;

function DashStat({ icon, label, value, sub, accent, glow, big }) {
  return (
    <div className={cls("card", "stat", glow && "glow")}>
      <div className="stat-top">
        <div style={{ fontSize: 11, letterSpacing: ".1em", textTransform: "uppercase", color: "var(--muted)", fontWeight: 700, fontFamily: "var(--font-mono)" }}>{label}</div>
        <div className="stat-icon" style={accent ? { background: `color-mix(in oklab, ${accent} 16%, var(--surface-2))`, color: accent } : null}><Icon name={icon} size={17} /></div>
      </div>
      <div className="stat-val">{big ? fmt(value) : <CountUp value={value} />}</div>
      <div className="stat-label mono" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".08em" }}>{sub}</div>
    </div>
  );
}

// live/preview source badge
function SourceBadge({ live }) {
  if (live === null) return null;
  return live
    ? <span className="badge ok"><span className="d" />live</span>
    : <span className="badge warn"><span className="d" />preview data</span>;
}

// period <-> range-label helpers ("7D"/"14D"/"30D" <-> 7/14/30)
const PERIODS = [7, 14, 30];
const rangeLabel = (p) => `${p}D`;
function readPeriod() {
  const raw = parseInt(localStorage.getItem("dashboard.analyticsPeriod"), 10);
  return PERIODS.includes(raw) ? raw : 30;
}

// relative time from an ISO/ms timestamp -> "8m ago" style
function relTime(ts) {
  if (!ts) return "";
  const t = typeof ts === "number" ? (ts < 1e12 ? ts * 1000 : ts) : Date.parse(ts);
  if (!t || isNaN(t)) return "";
  const s = Math.max(0, (Date.now() - t) / 1000);
  if (s < 60) return "just now";
  if (s < 3600) return Math.floor(s / 60) + "m ago";
  if (s < 86400) return Math.floor(s / 3600) + "h ago";
  if (s < 172800) return "yesterday";
  return Math.floor(s / 86400) + "d ago";
}

function WatchdogOverviewCard({ snapshot, go }) {
  const counts = snapshot?.counts || {};
  const projects = Array.isArray(snapshot?.projects) ? snapshot.projects : [];
  const unhealthy = projects.filter((p) => ["critical", "degraded", "readiness-unverified"].includes(p.state));
  const tone = snapshot?.error || counts.openIncidents || counts.downEndpoints ? "bad" : snapshot?.configured ? "ok" : "warn";
  return (
    <div className="card watchdog-overview" style={{ marginBottom: "var(--gap)" }}>
      <div className="card-head" style={{ marginBottom: projects.length ? 12 : 0 }}>
        <div>
          <h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>Watchdog Fleet Health</h2>
          <p>{snapshot === null ? "Loading monitored services…" : snapshot?.error ? `Live overview unavailable · ${snapshot.error}` : !snapshot?.configured ? "Connector is not configured for this deployment." : `${counts.projects || 0} projects · ${counts.endpoints || 0} checks · ${counts.openIncidents || 0} open incidents`}</p>
        </div>
        <div className="flex watchdog-actions" style={{ gap: 8 }}>
          <span className={cls("badge", tone)}><span className="d" />{snapshot === null ? "loading" : snapshot?.error ? "unreachable" : !snapshot?.configured ? "not configured" : counts.openIncidents ? `${counts.openIncidents} incidents` : "healthy"}</span>
          <button className="btn btn-subtle btn-sm" onClick={() => go("projects")}>Projects<Icon name="chevron" size={14} /></button>
          {snapshot?.dashboardUrl && <a className="btn btn-ghost btn-sm" href={snapshot.dashboardUrl} target="_blank" rel="noopener noreferrer">Open Watchdog<Icon name="external" size={13} /></a>}
        </div>
      </div>
      {snapshot?.configured && !snapshot?.error && projects.length > 0 && (
        <div className="rows">
          {(unhealthy.length ? unhealthy : projects).slice(0, 5).map((project) => (
            <div className="row" key={project.projectId}>
              <div className="glyph"><Icon name="activity" size={15} /></div>
              <div className="row-main"><b>{project.displayName}</b><div className="row-sub mono"><span>{project.endpoints.length} checks</span><span>·</span><span>{project.openIncidentCount || 0} incidents</span></div></div>
              <span className={cls("badge", project.state === "healthy" ? "ok" : ["critical", "degraded"].includes(project.state) ? "bad" : "warn")}><span className="d" />{project.state}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function sessionTime(s) {
  const raw = s?.updatedAt || s?.last_active || s?.startedAt || s?.started_at || 0;
  if (typeof raw === "number") return raw < 1e12 ? raw * 1000 : raw;
  return Date.parse(raw) || 0;
}

// The agent dashboard returns snake_case analytics while the workspace
// overview uses camelCase. Normalize the authoritative response once here.
function withDashboardAnalytics(overview, usage) {
  if (!usage || typeof usage !== "object") return overview || {};
  const totals = usage.totals || {};
  const skillData = usage.skills || {};
  const summary = skillData.summary || {};
  return {
    ...(overview || {}),
    analytics: {
      ...((overview && overview.analytics) || {}),
      daily: (Array.isArray(usage.daily) ? usage.daily : []).map((d) => ({
        ...d,
        inputTokens: d.inputTokens ?? d.input_tokens ?? 0,
        outputTokens: d.outputTokens ?? d.output_tokens ?? 0,
      })),
      totalSessions: totals.totalSessions ?? totals.total_sessions ?? 0,
      totalTokens: totals.totalTokens ?? ((totals.total_input || 0) + (totals.total_output || 0)),
      totalApiCalls: totals.totalApiCalls ?? totals.total_api_calls ?? 0,
      estimatedCostUsd: totals.estimatedCostUsd ?? totals.total_estimated_cost ?? 0,
    },
    skillsUsage: {
      ...((overview && overview.skillsUsage) || {}),
      totalLoads: summary.totalSkillLoads ?? summary.total_skill_loads ?? 0,
      totalActions: summary.totalSkillActions ?? summary.total_skill_actions ?? 0,
      topSkills: (skillData.topSkills || skillData.top_skills || []).map((s) => ({
        ...s,
        totalCount: s.totalCount ?? s.total_count ?? 0,
      })),
    },
  };
}

// ============================================================================
// LIVE render — driven by /api/dashboard/overview + /api/sessions.
// Same visual layout/cards as the offline view; only the data source differs.
// ============================================================================
function DashLive({ go, overview, sessionRows, sessionsFailed, watchdog, period, setPeriod }) {
  const o = overview || {};
  const analytics = o.analytics || {};
  const status = o.status || {};
  const skillsUsage = o.skillsUsage || {};
  const ach = o.achievements || {};

  const daily = Array.isArray(analytics.daily) ? analytics.daily : [];
  // tokens per day = input + output (matches AnalyticsChartCard "tokens (in+out)")
  const tokSeries = daily.length
    ? daily.map((d) => (d?.inputTokens || 0) + (d?.outputTokens || 0))
    : [0];

  const totalSessions = analytics.totalSessions || (sessionRows ? sessionRows.length : 0) || 0;
  const totalTokens = analytics.totalTokens || 0;
  const totalApiCalls = analytics.totalApiCalls || 0;
  const cost = analytics.estimatedCostUsd || 0;

  const topSkills = (Array.isArray(skillsUsage.topSkills) ? skillsUsage.topSkills : []).slice(0, 5);
  const maxUse = topSkills.reduce((m, s) => Math.max(m, s?.totalCount || 0), 0) || 1;
  const toolCalls = skillsUsage.totalActions || skillsUsage.totalLoads || topSkills.reduce((a, s) => a + (s?.totalCount || 0), 0) || 0;

  const recentUnlocks = Array.isArray(ach.recentUnlocks) ? ach.recentUnlocks : [];

  const rows = (Array.isArray(sessionRows) ? sessionRows : []).slice();
  rows.sort((a, b) => sessionTime(b) - sessionTime(a));

  const gwState = status.gatewayState || "";
  const gwOk = ["running", "connected", "ok"].includes(gwState);

  return (
    <div className="page">
      <PageHead eyebrow="Sylar's WorkSpace" title="Dashboard" sub="Your agent team, sessions, and gateway usage at a glance.">
        <button className="btn btn-ghost" onClick={() => go("conductor")}><Icon name="rocket" size={16} />Launch mission</button>
        <button className="btn btn-primary" onClick={() => go("chat")}><Icon name="chat" size={16} />New chat</button>
      </PageHead>

      <div className="statusbar">
        <StatusDot status={gwOk ? "running" : "warn"} live={gwOk} />
        <span>Gateway{status.version ? ` v${String(status.version).replace(/^v/, "")}` : ""}</span>
        <span className="sep">·</span><span>{status.activeAgents || 0} active runs</span>
        {status.lastHeartbeatAt && <><span className="sep">·</span><span>pulse {relTime(status.lastHeartbeatAt)}</span></>}
        <span style={{ marginLeft: "auto" }}><SourceBadge live={true} /></span>
      </div>

      <div className="grid cols-4" style={{ marginBottom: "var(--gap)" }}>
        <DashStat icon="chat" label="Sessions" value={totalSessions} sub="all time" glow />
        <DashStat icon="bolt" label="Tokens" value={totalTokens} sub="hermes ledger" accent="#f0883e" big />
        <DashStat icon="terminal" label="API Calls" value={totalApiCalls} sub="gateway" accent="#4d8bf5" />
        <DashStat icon="skills" label="Tool Calls" value={toolCalls} sub="across skills" accent="#2dd4a0" glow big />
      </div>

      <WatchdogOverviewCard snapshot={watchdog} go={go} />

      <div className="split" style={{ marginBottom: "var(--gap)" }}>
        <div className="card">
          <div className="card-head">
            <div>
              <h2 className="mono" style={{ fontSize: 13, letterSpacing: ".06em", textTransform: "uppercase" }}>Usage Trend · {period}d</h2>
              <p className="mono" style={{ fontSize: 12 }}>{fmt(totalTokens)} tokens · {totalApiCalls} calls · ${cost.toFixed(0)}</p>
            </div>
            <div className="seg">
              {PERIODS.map((p) => <button key={p} className={cls(period === p && "on")} onClick={() => setPeriod(p)}>{rangeLabel(p)}</button>)}
            </div>
          </div>
          <div className="chart-wrap">
            <AreaChart data={tokSeries} color="var(--primary)" height={200} />
          </div>
        </div>

        <div className="grid" style={{ gap: "var(--gap)", gridTemplateRows: "auto auto" }}>
          <div className="card">
            <div className="card-head" style={{ marginBottom: 10 }}><h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>Achievements</h2><Icon name="sparkle" size={15} className="faint" /></div>
            {recentUnlocks.length === 0 && <div className="faint" style={{ fontSize: 12, padding: "6px 2px" }}>No recent unlocks.</div>}
            {recentUnlocks.slice(0, 4).map((a, i) => (
              <div className="ach on" key={a.id || i}>
                <div className="ic"><Icon name="sparkle" size={16} /></div>
                <div style={{ minWidth: 0 }}><div style={{ fontSize: 12.5, fontWeight: 600 }}>{a.name || "Achievement"}</div><div className="faint" style={{ fontSize: 11 }}>{a.description || a.category || ""}</div></div>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div className="split">
        <div className="card">
          <div className="card-head">
            <h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>Sessions Intelligence</h2>
            <button className="btn btn-subtle btn-sm" onClick={() => go("chat")}>{rows.length} recent · open chat<Icon name="chevron" size={14} /></button>
          </div>
          {sessionRows === null && !sessionsFailed && <div className="faint" style={{ fontSize: 12, padding: "6px 2px" }}>Loading session history…</div>}
          {sessionsFailed && <div className="badge bad" style={{ height: "auto", padding: "8px 10px" }}><Icon name="activity" size={13} />Session history is temporarily unavailable.</div>}
          {sessionRows !== null && !sessionsFailed && rows.length === 0 && <div className="faint" style={{ fontSize: 12, padding: "6px 2px" }}>No sessions yet — start a chat.</div>}
          <div className="rows">
            {rows.slice(0, 4).map((s, i) => {
              const title = stripInjectedContext(s.title || s.label || s.derivedTitle || s.friendlyId) || "Session";
              const msgs = s.message_count || s.messageCount || 0;
              const tok = s.totalTokens || s.tokenCount || ((s.input_tokens || 0) + (s.output_tokens || 0));
              const meta = s.model ? modelMeta(s.model).short : (s.source || "session");
              return (
                <button className="row" key={s.key || s.id || i} onClick={() => go("chat", s.key || s.id)}>
                  <div className="glyph"><Icon name="chat" size={16} /></div>
                  <div className="row-main">
                    <b>{title}</b>
                    <div className="row-sub mono"><span>{meta}</span><span>·</span><span>{msgs} msgs</span><span>·</span><span>{fmt(tok)} tok</span></div>
                  </div>
                  <span className="faint mono" style={{ fontSize: 11.5 }}>{relTime(s.updatedAt || s.last_active || s.startedAt || s.started_at)}</span>
                </button>
              );
            })}
          </div>
        </div>

        <div className="card">
          <div className="card-head"><h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>Skills Usage</h2><button className="btn btn-subtle btn-sm" onClick={() => go("skills")}>All<Icon name="chevron" size={14} /></button></div>
          {topSkills.length === 0 && <div className="faint" style={{ fontSize: 12, padding: "6px 2px" }}>No usage in this window yet.</div>}
          {topSkills.map((s, i) => (
            <div className="usebar" key={s.skill || i}>
              <span className="nm">{s.skill || "skill"}</span>
              <Meter value={(s.totalCount || 0) / maxUse} />
              <span className="vl">{fmt(s.totalCount || 0)}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ============================================================================
// OFFLINE render — byte-identical to the original window.DATA dashboard,
// plus the "preview data" source badge. Used when dashboardOverview fails.
// ============================================================================
function DashOffline({ go, watchdog }) {
  const { gateway, usage, sessions, achievements, skills, agents } = window.DATA;
  const [range, setRange] = useState("30D");
  const sliceN = range === "7D" ? 7 : range === "14D" ? 14 : 30;
  const tokSeries = usage.tokens30d.slice(-sliceN);
  const totalTokens = Math.round(tokSeries.reduce((a, b) => a + b, 0));
  const totalCalls = Math.round(usage.calls30d.slice(-sliceN).reduce((a, b) => a + b, 0));
  const cost = usage.cost30d.slice(-sliceN).reduce((a, b) => a + b, 0);
  const topSkills = [...skills].sort((a, b) => b.uses - a.uses).slice(0, 5);
  const maxUse = topSkills[0].uses;

  return (
    <div className="page">
      <PageHead eyebrow="Sylar's WorkSpace" title="Dashboard" sub="Your agent team, sessions, and gateway usage at a glance.">
        <button className="btn btn-ghost" onClick={() => go("conductor")}><Icon name="rocket" size={16} />Launch mission</button>
        <button className="btn btn-primary" onClick={() => go("chat")}><Icon name="chat" size={16} />New chat</button>
      </PageHead>

      <div className="statusbar">
        <StatusDot status="running" live /><span>Gateway {gateway.version}</span>
        <span className="sep">·</span><span>{gateway.activeRuns} active runs</span>
        <span className="sep">·</span><span>pulse {gateway.pulse}</span>
        <span style={{ marginLeft: "auto" }}><SourceBadge live={false} /></span>
      </div>

      <div className="grid cols-4" style={{ marginBottom: "var(--gap)" }}>
        <DashStat icon="chat" label="Sessions" value={sessions.length} sub="all time" glow />
        <DashStat icon="bolt" label="Tokens" value={totalTokens} sub="hermes ledger" accent="#f0883e" big />
        <DashStat icon="terminal" label="API Calls" value={totalCalls} sub="gateway" accent="#4d8bf5" />
        <DashStat icon="skills" label="Tool Calls" value={skills.reduce((a, b) => a + b.uses, 0)} sub="across skills" accent="#2dd4a0" glow big />
      </div>

      <WatchdogOverviewCard snapshot={watchdog} go={go} />

      <div className="split" style={{ marginBottom: "var(--gap)" }}>
        <div className="card">
          <div className="card-head">
            <div>
              <h2 className="mono" style={{ fontSize: 13, letterSpacing: ".06em", textTransform: "uppercase" }}>Usage Trend · {range}</h2>
              <p className="mono" style={{ fontSize: 12 }}>{fmt(totalTokens)} tokens · {totalCalls} calls · ${cost.toFixed(0)}</p>
            </div>
            <div className="seg">
              {["7D", "14D", "30D"].map((r) => <button key={r} className={cls(range === r && "on")} onClick={() => setRange(r)}>{r}</button>)}
            </div>
          </div>
          <div className="chart-wrap">
            <AreaChart data={tokSeries} color="var(--primary)" height={200} />
          </div>
        </div>

        <div className="grid" style={{ gap: "var(--gap)", gridTemplateRows: "auto auto" }}>
          <div className="card">
            <div className="card-head" style={{ marginBottom: 10 }}><h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>Achievements</h2><Icon name="sparkle" size={15} className="faint" /></div>
            {achievements.slice(0, 4).map((a) => (
              <div className={cls("ach", a.unlocked ? "on" : "off")} key={a.id}>
                <div className="ic"><Icon name={a.icon === "swarm" ? "swarm" : a.icon} size={16} /></div>
                <div style={{ minWidth: 0 }}><div style={{ fontSize: 12.5, fontWeight: 600 }}>{a.name}</div><div className="faint" style={{ fontSize: 11 }}>{a.desc}</div></div>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div className="split">
        <div className="card">
          <div className="card-head">
            <h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>Sessions Intelligence</h2>
            <button className="btn btn-subtle btn-sm" onClick={() => go("chat")}>{sessions.length} recent · open chat<Icon name="chevron" size={14} /></button>
          </div>
          <div className="rows">
            {sessions.slice(0, 4).map((s) => {
              const a = agentById(s.agent);
              return (
                <button className="row" key={s.id} onClick={() => go("chat", s.id)}>
                  <AgentBot agent={a} size={32} />
                  <div className="row-main">
                    <b>{s.title}{s.pinned && <Icon name="pin" size={12} className="faint" style={{ display: "inline", verticalAlign: "-1px", marginLeft: 6 }} />}</b>
                    <div className="row-sub mono"><span>{a.name}</span><span>·</span><span>{s.messages} msgs</span><span>·</span><span>{fmt(s.tokens)} tok</span></div>
                  </div>
                  <span className="faint mono" style={{ fontSize: 11.5 }}>{s.updated}</span>
                </button>
              );
            })}
          </div>
        </div>

        <div className="card">
          <div className="card-head"><h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>Skills Usage</h2><button className="btn btn-subtle btn-sm" onClick={() => go("skills")}>All<Icon name="chevron" size={14} /></button></div>
          {topSkills.map((s) => (
            <div className="usebar" key={s.id}>
              <span className="nm">{s.name}</span>
              <Meter value={s.uses / maxUse} />
              <span className="vl">{fmt(s.uses)}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function PageDashboard({ go }) {
  const [period, setPeriodState] = useState(readPeriod);
  const [overview, setOverview] = useState(null);   // backend overview object
  const [sessions, setSessions] = useState(null);   // backend session rows
  const [sessionsFailed, setSessionsFailed] = useState(false);
  const [watchdog, setWatchdog] = useState(null);
  const [live, setLive] = useState(null);           // null=loading, true=backend, false=offline fallback

  // persist period selection (key matches upstream "dashboard.analyticsPeriod")
  const setPeriod = (p) => {
    setPeriodState(p);
    try { localStorage.setItem("dashboard.analyticsPeriod", String(p)); } catch { /* ignore */ }
  };

  useEffect(() => {
    let alive = true;
    Promise.allSettled([
      HERMES.dashboardOverview(period, { achievements: 5 }),
      HERMES.analyticsUsage(period),
    ]).then(([overviewResult, usageResult]) => {
      if (!alive) return;
      if (overviewResult.status === "rejected" && usageResult.status === "rejected") {
        setLive(false);
        return;
      }
      const ov = overviewResult.status === "fulfilled" ? overviewResult.value : {};
      const usage = usageResult.status === "fulfilled" ? usageResult.value : null;
      setOverview(withDashboardAnalytics(ov, usage));
      setLive(true);
    });
    return () => { alive = false; };
  }, [period]);

  // Session history does not depend on the chart period, so load it once.
  useEffect(() => {
    let alive = true;
    HERMES.listSessions(200)
      .then((sx) => {
        if (!alive) return;
        setSessions(Array.isArray(sx) ? sx : (sx && sx.sessions) || []);
        setSessionsFailed(false);
      })
      .catch(() => { if (alive) setSessionsFailed(true); });
    return () => { alive = false; };
  }, []);

  useEffect(() => {
    let alive = true;
    HERMES.watchdogOverview()
      .then((snapshot) => { if (alive) setWatchdog(snapshot); })
      .catch((error) => { if (alive) setWatchdog({ configured: true, error: error?.message || "Watchdog unavailable", counts: {}, projects: [] }); });
    return () => { alive = false; };
  }, []);

  if (live === true) return <DashLive go={go} overview={overview} sessionRows={sessions} sessionsFailed={sessionsFailed} watchdog={watchdog} period={period} setPeriod={setPeriod} />;
  // live === false (offline) OR live === null (initial load): show the design data view.
  // Offline looks identical to today; while loading we also show it so there is no blank flash.
  return <DashOffline go={go} watchdog={watchdog} />;
}
window.PageDashboard = PageDashboard;
