// Exec dashboard — wired to the API. The page that must be excellent.

// React hooks (Babel-standalone — make hooks visible per-script)
const { useState, useEffect, useRef, useMemo, useCallback, createContext, useContext } = React;

const execStyles = {
  page: { padding: 16, display: "flex", flexDirection: "column", gap: 14 },
  row: { display: "flex", gap: 14, alignItems: "stretch" },
  briefingEntry: {
    display: "flex", flexDirection: "column", gap: 6,
    padding: "12px 0", borderBottom: "1px solid var(--vk-border-subtle)",
  },
  briefingEntryLast: {
    display: "flex", flexDirection: "column", gap: 6, padding: "12px 0 0",
  },
  vipRow: {
    display: "grid", gridTemplateColumns: "28px 1fr auto",
    gap: 10, alignItems: "center", padding: "8px 0",
    borderBottom: "1px solid var(--vk-border-subtle)",
    cursor: "pointer",
  },
};

// Render a line of free-form text and turn known canonical entity names into EntityLinks.
function linkifyBlurb(text, entityIndex, openEntity) {
  if (!text) return null;
  // Sort names by length desc to match longest first.
  const names = Object.keys(entityIndex).sort((a, b) => b.length - a.length);
  const parts = [];
  let remaining = text;
  let safety = 0;
  while (remaining && safety++ < 200) {
    let bestIdx = -1, bestName = null;
    for (const n of names) {
      const i = remaining.indexOf(n);
      if (i !== -1 && (bestIdx === -1 || i < bestIdx)) {
        bestIdx = i; bestName = n;
        if (i === 0) break;
      }
    }
    if (bestIdx === -1) { parts.push(remaining); break; }
    if (bestIdx > 0) parts.push(remaining.slice(0, bestIdx));
    const ent = entityIndex[bestName];
    parts.push(
      <EntityLink key={"el" + parts.length} entity={ent} onOpen={openEntity}>{bestName}</EntityLink>
    );
    remaining = remaining.slice(bestIdx + bestName.length);
  }
  return parts;
}

// Parse a brief's markdown body for category-tagged blurbs (CAPITAL/PEOPLE/POLICY/SIGNAL).
function parseBriefingEntries(brief) {
  if (!brief?.markdown_body) {
    return brief?.executive_summary
      ? [{ cat: "SUMMARY", body: brief.executive_summary }]
      : [];
  }
  const out = [];
  const lines = brief.markdown_body.split("\n");
  // Match either ## CAPITAL / ## Capital headers OR inline **CAPITAL** — anything tagged.
  const tags = ["CAPITAL", "PEOPLE", "POLICY", "SIGNAL", "RISK"];
  let currentTag = null;
  let buf = [];
  for (const ln of lines) {
    const m = ln.match(/^##\s+(.+)/);
    if (m) {
      if (currentTag && buf.length) out.push({ cat: currentTag, body: buf.join(" ").trim() });
      buf = [];
      const t = m[1].toUpperCase();
      currentTag = tags.find((tag) => t.includes(tag)) || null;
      continue;
    }
    if (currentTag && ln.trim() && !/^\[\^/.test(ln)) {
      // strip markdown link syntax for the plain blurb (linkifier will re-find names)
      buf.push(ln.replace(/\[([^\]]+)\]\(#[^)]+\)/g, "$1").replace(/^[-*]\s*/, "").replace(/\*\*([^*]+)\*\*/g, "$1"));
    }
  }
  if (currentTag && buf.length) out.push({ cat: currentTag, body: buf.join(" ").trim() });
  // If we found no tagged entries, fall back to executive summary as one entry.
  if (!out.length && brief.executive_summary) out.push({ cat: "SUMMARY", body: brief.executive_summary });
  return out.slice(0, 4);
}

// ---------- KPI strip ----------
function KpiStrip({ kpis, loading }) {
  if (loading) {
    return (
      <div style={execStyles.row}>
        {[0,1,2,3,4].map((i) => (
          <div key={i} className="vk-card" style={{ padding: 14, flex: 1, minWidth: 0 }}>
            <SkeletonRow width="50%" height={11} />
            <div style={{ height: 8 }}></div>
            <SkeletonRow width="60%" height={22} />
          </div>
        ))}
      </div>
    );
  }
  if (!kpis) return null;
  const hi = kpis.high_confidence_pct;
  const hiColor = hi >= 60 ? "var(--vk-success)" : hi >= 40 ? "var(--vk-warning)" : "var(--vk-danger)";
  return (
    <div style={execStyles.row}>
      <KpiTile label="Entities" value={(kpis.entity_count ?? 0).toLocaleString()} />
      <KpiTile label="Relationships" value={(kpis.relationship_count ?? 0).toLocaleString()} />
      <KpiTile label="Sources" value={(kpis.source_count ?? 0).toLocaleString()} />
      <KpiTile label="High conf." value={(kpis.high_confidence_pct ?? 0) + "%"} scoreColor={hiColor} />
      <KpiTile label="Needs review" value={String(kpis.needs_review_count ?? 0)} scoreColor={kpis.needs_review_count > 0 ? "var(--vk-warning)" : undefined} />
    </div>
  );
}

// ---------- Intel briefing ----------
function IntelBriefing({ city, runLabel, openEntity, openEvidence }) {
  const briefsQ = useApi("/briefs", { city, limit: 1 }, [city]);
  const list = Array.isArray(briefsQ.data) ? briefsQ.data : [];
  const briefId = list[0]?.brief_id;
  const detailQ = useApi(briefId ? "/briefs/" + briefId : "/briefs", briefId ? {} : { city, limit: 1 }, [briefId]);
  const brief = briefId ? detailQ.data : list[0];

  // Index all entities for this city to turn names into links.
  const entsQ = useApi("/entities", { city, limit: 500 }, [city]);
  const entIndex = useMemo(() => {
    const idx = {};
    (entsQ.data || []).forEach((e) => { idx[e.canonical_name] = e; });
    return idx;
  }, [entsQ.data]);

  const loading = briefsQ.loading || (briefId && detailQ.loading);
  const entries = useMemo(() => parseBriefingEntries(brief), [brief]);

  return (
    <Card style={{ flex: 2 }} padding={16}>
      <CardHeader
        title={`Intel briefing — ${city}`}
        subtitle={brief ? `${runLabel} · ${brief.source_count ?? "?"} sources` : runLabel}
        action={
          <button className="vk-btn vk-btn--ghost" style={{ height: 24, padding: "0 8px" }}>
            <Icon name="ExternalLink" size={13} />
            Open full brief
          </button>
        }
      />
      {loading && <SkeletonBlock rows={6} />}
      {!loading && !brief && (
        <EmptyState
          icon="FileText"
          title="No brief generated for this run yet."
          hint="Run the Brief Agent to generate one."
        />
      )}
      {!loading && brief && entries.length === 0 && (
        <p style={{ fontSize: 13, color: "var(--vk-text-secondary)" }}>{brief.executive_summary}</p>
      )}
      {!loading && brief && entries.length > 0 && (
        <div>
          {entries.map((entry, i) => (
            <div
              key={entry.cat + i}
              style={i === entries.length - 1 ? execStyles.briefingEntryLast : execStyles.briefingEntry}
            >
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <MicroCap>{entry.cat}</MicroCap>
                <div style={{ flex: 1 }} />
                <EvidenceIcon evidence={null} onOpen={() => openEvidence(null)} label={`${entry.cat} evidence`} />
              </div>
              <p style={{ fontSize: 13, lineHeight: 1.55, color: "var(--vk-text-primary)" }}>
                {linkifyBlurb(entry.body, entIndex, openEntity)}
              </p>
            </div>
          ))}
          <div style={{ marginTop: 10, fontSize: 11, color: "var(--vk-text-tertiary)" }}>
            Click any bold entity to see evidence →
          </div>
        </div>
      )}
    </Card>
  );
}

// ---------- VIP people ----------
function VipPeople({ runId, city, openEntity }) {
  const { data, loading } = useApi("/ranked/top_influencers", { city, run_id: runId, limit: 12 }, [city, runId]);
  const people = (Array.isArray(data) ? data : []).filter((e) => e.entity_type === "person").slice(0, 4);

  return (
    <Card style={{ flex: 1 }} padding={16}>
      <CardHeader title="VIP people" subtitle="influence top 4" />
      {loading && <SkeletonBlock rows={4} />}
      {!loading && people.length === 0 && (
        <EmptyState icon="Users" title="No people scored for this run yet." />
      )}
      {!loading && people.length > 0 && (
        <div style={{ display: "flex", flexDirection: "column" }}>
          {people.map((p, i) => (
            <div
              key={p.entity_id}
              style={{
                ...execStyles.vipRow,
                borderBottom: i === people.length - 1 ? "none" : "1px solid var(--vk-border-subtle)",
              }}
              onClick={() => openEntity(p)}
            >
              <Avatar name={p.canonical_name} size={28} />
              <div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
                <span style={{ fontSize: 13, fontWeight: 500, color: "var(--vk-text-primary)" }}>{p.canonical_name}</span>
                <span style={{ fontSize: 11, color: "var(--vk-text-tertiary)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                  {p.role_in_ecosystem?.split(",")[0] || p.subtype}
                </span>
              </div>
              <Score value={p.influence_score} />
            </div>
          ))}
        </div>
      )}
    </Card>
  );
}

// ---------- Ranked mini-table ----------
function RankedMiniTable({ endpoint, city, runId, columns, openEntity, emptyText }) {
  const { data, loading } = useApi(endpoint, { city, run_id: runId, limit: 4 }, [city, runId]);
  const rows = Array.isArray(data) ? data : [];
  if (loading) return <SkeletonBlock rows={4} />;
  if (!rows.length) return <EmptyState icon="Inbox" title={emptyText} />;
  return (
    <MiniTable
      columns={columns.cols}
      rows={rows.slice(0, 4).map((r) => columns.row(r, openEntity))}
    />
  );
}

// ---------- Blockers full-width table ----------
function BlockersWidget({ city, runId, openEntity, openEvidence }) {
  const { data, loading } = useApi("/ranked/top_blockers", { city, run_id: runId, limit: 6 }, [city, runId]);
  const rows = Array.isArray(data) ? data : [];
  return (
    <Card padding={16}>
      <CardHeader
        title="Blockers & risk"
        action={
          <div style={{ display: "flex", gap: 6 }}>
            <Pill kind="low">Blockers</Pill>
            <Pill kind="med">Conflicts</Pill>
            <Pill kind="neutral">Sensitive</Pill>
          </div>
        }
      />
      {loading && <SkeletonBlock rows={4} />}
      {!loading && rows.length === 0 && <EmptyState icon="ShieldCheck" title="No blockers detected for this run." />}
      {!loading && rows.length > 0 && (
        <MiniTable
          columns={[
            { key: "name", label: "Entity", width: "1.4fr" },
            { key: "category", label: "Category", width: "1.1fr" },
            { key: "pattern", label: "Pattern", width: "1.6fr" },
            { key: "risk", label: "Risk", width: "60px", align: "right" },
            { key: "conf", label: "Conf.", width: "60px", align: "right" },
          ]}
          rows={rows.slice(0, 6).map((e) => {
            const isRedacted = (e.canonical_name || "").startsWith("[redacted");
            const cat = e.blocker_candidate === "yes"
              ? { kind: "low", label: e.entity_type === "regulator" ? "Regulatory blocker" : "Operational blocker" }
              : { kind: "med", label: "Conflict of interest" };
            return {
              key: e.entity_id,
              name: isRedacted
                ? <span style={{ color: "var(--vk-text-tertiary)", fontStyle: "italic" }}>{e.canonical_name}</span>
                : <EntityLink entity={e} onOpen={openEntity}>{e.canonical_name}</EntityLink>,
              category: <Pill kind={cat.kind}>{cat.label}</Pill>,
              pattern: <span style={{ color: "var(--vk-text-secondary)" }}>{e.role_in_ecosystem || e.sector_or_focus || "—"}</span>,
              risk: <Score value={e.risk_score} riskScale />,
              conf: <ConfPill confidence={e.confidence_score >= 70 ? "high" : e.confidence_score >= 40 ? "med" : "low"} />,
            };
          })}
        />
      )}
    </Card>
  );
}

function ExecDashboard({ setPage, openEntity, openEvidence }) {
  const app = useAppState();
  const kpisQ = useApi("/exec-kpis", { city: app.city, run_id: app.runId }, [app.city, app.runId]);

  return (
    <div style={execStyles.page}>
      <KpiStrip kpis={kpisQ.data} loading={kpisQ.loading} />

      <div style={execStyles.row}>
        <IntelBriefing
          city={app.city}
          runLabel={app.runLabel}
          openEntity={openEntity}
          openEvidence={openEvidence}
        />
        <VipPeople runId={app.runId} city={app.city} openEntity={openEntity} />
      </div>

      <div style={execStyles.row}>
        <Card style={{ flex: 1 }} padding={16}>
          <CardHeader
            title="Top competitors"
            subtitle="competitor candidates"
            onTitleClick={() => setPage("competitors")}
          />
          <RankedMiniTable
            endpoint="/ranked/top_competitors"
            city={app.city} runId={app.runId}
            openEntity={openEntity}
            emptyText="No competitors flagged for this run."
            columns={{
              cols: [
                { key: "name", label: "Entity", width: "1fr" },
                { key: "sector", label: "Sector", width: "100px" },
                { key: "score", label: "Comp.", width: "48px", align: "right" },
                { key: "conf", label: "Conf.", width: "60px", align: "right" },
              ],
              row: (e, open) => ({
                key: e.entity_id,
                name: <EntityLink entity={e} onOpen={open}>{e.canonical_name}</EntityLink>,
                sector: <span style={{ color: "var(--vk-text-secondary)" }}>{e.sector_or_focus}</span>,
                score: <Score value={e.relevance_score} riskScale />,
                conf: <ConfPill confidence={e.confidence_score >= 70 ? "high" : e.confidence_score >= 40 ? "med" : "low"} />,
              }),
            }}
          />
        </Card>

        <Card style={{ flex: 1 }} padding={16}>
          <CardHeader
            title="Investment opportunities"
            subtitle="investment candidates"
            onTitleClick={() => setPage("investment")}
          />
          <RankedMiniTable
            endpoint="/ranked/top_investment_targets"
            city={app.city} runId={app.runId}
            openEntity={openEntity}
            emptyText="No investment targets flagged for this run."
            columns={{
              cols: [
                { key: "name", label: "Entity", width: "1fr" },
                { key: "stage", label: "Stage", width: "84px" },
                { key: "score", label: "Score", width: "48px", align: "right" },
                { key: "conf", label: "Conf.", width: "60px", align: "right" },
              ],
              row: (e, open) => ({
                key: e.entity_id,
                name: <EntityLink entity={e} onOpen={open}>{e.canonical_name}</EntityLink>,
                stage: <span style={{ color: "var(--vk-text-secondary)" }}>{e.subtype || "—"}</span>,
                score: <Score value={e.relevance_score} />,
                conf: <ConfPill confidence={e.confidence_score >= 70 ? "high" : e.confidence_score >= 40 ? "med" : "low"} />,
              }),
            }}
          />
        </Card>
      </div>

      <BlockersWidget
        city={app.city} runId={app.runId}
        openEntity={openEntity}
        openEvidence={openEvidence}
      />
    </div>
  );
}

Object.assign(window, { ExecDashboard });
