"use client";

import {
  Activity,
  ArrowUpRight,
  Blocks,
  BrainCircuit,
  Check,
  CheckCircle2,
  Clock3,
  Copy,
  Database,
  ExternalLink,
  HandCoins,
  Radar,
  RefreshCw,
  Rocket,
  ShieldCheck,
} from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";

type Launch = {
  token: string;
  deployer: string;
  pool: string;
  pairToken: string;
  name: string;
  symbol: string;
  initialBuyEth: string;
  blockNumber: number;
  timestamp: number;
  txHash: string;
  explorerUrl: string;
};

type ChainData = {
  source: string;
  chainId: number;
  latestBlock: number;
  scannedFromBlock: number;
  factory: string;
  launches: Launch[];
  updatedAt: string;
};

type AgentEventType =
  | "trend.detected"
  | "model.decision"
  | "launch.submitted"
  | "launch.confirmed"
  | "fees.claimed"
  | "buyback.submitted"
  | "buyback.confirmed"
  | "agent.paused"
  | "agent.resumed";

type AgentEvent = {
  eventId: string;
  agentId: string;
  sequence: number;
  type: AgentEventType;
  status: "observed" | "proposed" | "submitted" | "confirmed" | "failed";
  occurredAt: string;
  chainId: number;
  txHash?: string;
  payload: Record<string, unknown>;
  signer: string;
  verification: "signature" | "chain";
};

type AgentFeed = {
  events: AgentEvent[];
  pipelineEvents?: AgentEvent[];
  updatedAt: string;
};

type AgentLaunchSummary = {
  token: string;
  pool?: string;
  name?: string;
  symbol?: string;
  topic?: string;
  totalValueEth?: string;
  txHash: string;
  occurredAt: string;
};

type FeedView = "launches" | "agent";
type StageState = "queued" | "processing" | "verified" | "waiting";
type EventOutcome = "accepted" | "blocked" | "failed" | "neutral";

const EXPLORER = "https://robinhoodchain.blockscout.com";
const PONS_FACTORY_URL = `${EXPLORER}/address/0xA5aAb3F0c6EeadF30Ef1D3Eb997108E976351feB`;

const short = (value: string, edge = 6) => {
  if (value.length <= edge * 2 + 2) return value;
  return `${value.slice(0, edge + 2)}…${value.slice(-edge)}`;
};

const formatAge = (timestamp: number) => {
  const seconds = Math.max(0, Math.floor((Date.now() - timestamp) / 1_000));
  if (seconds < 10) return "just now";
  if (seconds < 60) return `${seconds}s ago`;
  if (seconds < 3_600) return `${Math.floor(seconds / 60)}m ago`;
  if (seconds < 86_400) return `${Math.floor(seconds / 3_600)}h ago`;
  return `${Math.floor(seconds / 86_400)}d ago`;
};

const formatTimestamp = (timestamp: number) => new Intl.DateTimeFormat(undefined, {
  month: "short",
  day: "2-digit",
  hour: "2-digit",
  minute: "2-digit",
  second: "2-digit",
  hour12: false,
}).format(timestamp);

const payloadText = (payload: Record<string, unknown>, key: string) => {
  const value = payload[key];
  return typeof value === "string" ? value : undefined;
};

const payloadNumber = (payload: Record<string, unknown>, key: string) => {
  const value = payload[key];
  return typeof value === "number" ? value : undefined;
};

const payloadBoolean = (payload: Record<string, unknown>, key: string) => {
  const value = payload[key];
  return typeof value === "boolean" ? value : undefined;
};

const payloadStrings = (payload: Record<string, unknown>, key: string) => {
  const value = payload[key];
  return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
};

const eventMeta: Record<AgentEventType, { title: string; icon: typeof Radar }> = {
  "trend.detected": { title: "Trend detected", icon: Radar },
  "model.decision": { title: "Model decision", icon: BrainCircuit },
  "launch.submitted": { title: "Launch submitted", icon: Rocket },
  "launch.confirmed": { title: "Launch confirmed", icon: CheckCircle2 },
  "fees.claimed": { title: "Fees claimed", icon: HandCoins },
  "buyback.submitted": { title: "Buyback submitted", icon: Activity },
  "buyback.confirmed": { title: "Buyback confirmed", icon: CheckCircle2 },
  "agent.paused": { title: "Agent paused", icon: Clock3 },
  "agent.resumed": { title: "Agent resumed", icon: Activity },
};

export default function LiveDashboard() {
  const [data, setData] = useState<ChainData | null>(null);
  const [agentFeed, setAgentFeed] = useState<AgentFeed | null>(null);
  const [chainError, setChainError] = useState<string | null>(null);
  const [agentError, setAgentError] = useState<string | null>(null);
  const [initialLoading, setInitialLoading] = useState(true);
  const [refreshing, setRefreshing] = useState(false);
  const [view, setView] = useState<FeedView>("agent");
  const [toast, setToast] = useState<string | null>(null);
  const [, setClockTick] = useState(0);
  const toastTimer = useRef<number | null>(null);

  const load = useCallback(async () => {
    setRefreshing(true);
    const launchesRequest = fetch("/api/launches", { cache: "no-store" }).then(async (response) => {
      if (!response.ok) throw new Error("Chain RPC is temporarily unavailable");
      return response.json() as Promise<ChainData>;
    });
    const eventsRequest = fetch("/api/agent-events?limit=50", { cache: "no-store" }).then(async (response) => {
      if (!response.ok) throw new Error("Signed Agent feed is temporarily unavailable");
      return response.json() as Promise<AgentFeed>;
    });

    const [launchesResult, eventsResult] = await Promise.allSettled([launchesRequest, eventsRequest]);
    if (launchesResult.status === "fulfilled") {
      setData(launchesResult.value);
      setChainError(null);
    } else {
      setChainError(launchesResult.reason instanceof Error ? launchesResult.reason.message : "Unable to read chain data");
    }
    if (eventsResult.status === "fulfilled") {
      setAgentFeed(eventsResult.value);
      setAgentError(null);
    } else {
      setAgentError(eventsResult.reason instanceof Error ? eventsResult.reason.message : "Unable to read signed Agent events");
    }
    setInitialLoading(false);
    setRefreshing(false);
  }, []);

  useEffect(() => {
    load();
    const poll = window.setInterval(load, 15_000);
    const clock = window.setInterval(() => setClockTick((value) => value + 1), 10_000);
    return () => {
      window.clearInterval(poll);
      window.clearInterval(clock);
      if (toastTimer.current) window.clearTimeout(toastTimer.current);
    };
  }, [load]);

  const copy = useCallback(async (value: string, label: string) => {
    try {
      await navigator.clipboard.writeText(value);
      setToast(`${label} copied`);
    } catch {
      setToast("Copy failed");
    }
    if (toastTimer.current) window.clearTimeout(toastTimer.current);
    toastTimer.current = window.setTimeout(() => setToast(null), 2_800);
  }, []);

  const latestLaunch = data?.launches[0];
  const events = agentFeed?.events ?? [];
  const latestControlEvent = events.find((event) => event.type === "agent.paused" || event.type === "agent.resumed");
  const agentStatus = events.length === 0
    ? "Awaiting signed feed"
    : latestControlEvent?.type === "agent.paused"
      ? "Paused"
      : "Operating";
  const lastSynced = data?.updatedAt ?? agentFeed?.updatedAt;
  const pipelineEvents = agentFeed?.pipelineEvents ?? events;
  const stages = useMemo(() => buildPipelineStages(pipelineEvents), [pipelineEvents]);
  const agentLaunches = useMemo(() => buildAgentLaunches(events), [events]);

  return (
    <>
      <section className="operations" id="top">
        <header className="operations-header">
          <div className="operations-title">
            <span>Autonomous onchain agent</span>
            <h1>ZOI AGENT</h1>
            <div className="operations-manifesto">Execution, in public.</div>
            <p>
              X market signals become Pons launches, fee claims and buybacks on Robinhood Chain.
              Every financial action is attributable to a signed event or a public transaction.
            </p>
            <div className="operations-actions">
              <a className="action-primary" href="#live">View live activity <ArrowUpRight size={13} /></a>
              <a className="action-secondary" href="#protocol">Explore protocol <ArrowUpRight size={13} /></a>
            </div>
          </div>
          <div className="operations-state" aria-label="Live network status">
            <div><span>Network</span><strong><i className="status-pulse" /> Robinhood Chain</strong></div>
            <div><span>Current block</span><strong>{data ? data.latestBlock.toLocaleString() : <Skeleton width="118px" />}</strong></div>
            <div><span>Agent source</span><strong className={events.length ? "signal" : "muted"}>{agentStatus}</strong></div>
            <div><span>Last sync</span><strong>{lastSynced ? formatAge(Date.parse(lastSynced)) : "Connecting"}</strong></div>
          </div>
        </header>

        <div className="live-sections">
        <div className="execution-rail" id="protocol" aria-label="Agent execution pipeline">
          {stages.map((stage, index) => (
            <PipelineStage key={stage.label} index={index + 1} {...stage} />
          ))}
        </div>

        <div className="proof-stage">
          <div className="proof-stage-label">
            <span>Latest chain observation</span>
            <strong>Pons Factory</strong>
            <a href={PONS_FACTORY_URL} target="_blank" rel="noreferrer">
              Verify factory <ArrowUpRight size={13} />
            </a>
          </div>

          <div className="primary-proof">
            {latestLaunch ? (
              <>
                <div className="primary-proof-heading">
                  <span className="verified-label"><CheckCircle2 size={13} /> Confirmed</span>
                  <time>{formatAge(latestLaunch.timestamp)}</time>
                </div>
                <div className="primary-token">
                  <span>${latestLaunch.symbol}</span>
                  <h2>{latestLaunch.name}</h2>
                </div>
                <dl className="proof-grid">
                  <div><dt>Block</dt><dd>{latestLaunch.blockNumber.toLocaleString()}</dd></div>
                  <div><dt>Initial buy</dt><dd>{Number(latestLaunch.initialBuyEth).toFixed(5)} ETH</dd></div>
                  <div><dt>Token</dt><dd><AddressButton label="Token" value={latestLaunch.token} onCopy={copy} compact /></dd></div>
                  <div><dt>Pool</dt><dd><AddressButton label="Pool" value={latestLaunch.pool} onCopy={copy} compact /></dd></div>
                </dl>
                <a className="transaction-proof" href={latestLaunch.explorerUrl} target="_blank" rel="noreferrer">
                  <span>Transaction hash</span>
                  <code>{latestLaunch.txHash}</code>
                  <ExternalLink size={16} />
                </a>
              </>
            ) : (
              <div className="primary-proof-empty" role="status">
                {initialLoading ? <RefreshCw className="spin" size={18} /> : <Clock3 size={18} />}
                <span>{initialLoading ? "Reading Pons Factory" : "Waiting for a verified launch"}</span>
              </div>
            )}
          </div>

          <aside className="source-separation">
            <div className="source-separation-head">
              <span>Attribution boundary</span>
              <ShieldCheck size={16} />
            </div>
            <p>Pons launches are network observations. They are not attributed to this Agent unless a matching signed Agent event exists.</p>
            <div className="source-fact"><span>Pons network</span><strong>{data ? `${data.launches.length} launches observed` : "Connecting"}</strong></div>
            <div className="source-fact"><span>ZOI AGENT</span><strong className={events.length ? "signal" : "muted"}>{events.length ? `${events.length} signed events` : "No signed events yet"}</strong></div>
            <button className="source-view-button" type="button" onClick={() => { setView("agent"); document.querySelector(".ledger-section")?.scrollIntoView({ behavior: "smooth" }); }}>
              Open signed event feed <ArrowUpRight size={13} />
            </button>
          </aside>
        </div>
      <section className="ledger-section" id="live" aria-labelledby="ledger-title">
        <header className="ledger-header">
          <div>
            <span className="ledger-live-label"><i className="status-pulse" aria-hidden="true" /> Live Agent Log</span>
            <h2 id="ledger-title">Real activity from the running Agent.</h2>
            <p>Signed X discoveries, model decisions, launches, fee claims and buybacks. Transaction hashes link directly to Blockscout.</p>
          </div>
          <button
            className="refresh-button"
            type="button"
            onClick={load}
            disabled={refreshing}
            aria-busy={refreshing}
            title="Refresh live data"
          >
            <RefreshCw size={14} className={refreshing ? "spin" : ""} />
            <span>{refreshing ? "Syncing" : "Refresh"}</span>
          </button>
        </header>

        <div className="ledger-stats" aria-label="Feed status">
          <StatusDatum label="Agent status" value={agentStatus} live={agentStatus === "Operating"} />
          <StatusDatum label="Last activity" value={events[0] ? formatAge(Date.parse(events[0].occurredAt)) : "Awaiting event"} />
          <StatusDatum label="Current block" value={data ? data.latestBlock.toLocaleString() : "—"} />
          <StatusDatum label="Signed Agent events" value={agentFeed ? String(agentFeed.events.length) : "—"} />
        </div>

        <AgentLaunches launches={agentLaunches} onCopy={copy} />

        <div className="ledger-toolbar">
          <div className="feed-tabs" role="group" aria-label="Activity source">
            <button type="button" aria-pressed={view === "agent"} onClick={() => setView("agent")}>
              Agent activity <span>{agentFeed?.events.length ?? 0}</span>
            </button>
            <button type="button" aria-pressed={view === "launches"} onClick={() => setView("launches")}>
              Pons network <span>{data?.launches.length ?? 0}</span>
            </button>
          </div>
          <span className="source-label">
            {view === "launches" ? <><Blocks size={13} /> Robinhood RPC</> : <><ShieldCheck size={13} /> Signed event API</>}
          </span>
        </div>

        {view === "launches" ? (
          <LaunchFeed data={data} loading={initialLoading} error={chainError} onCopy={copy} onRetry={load} />
        ) : (
          <AgentEventFeed feed={agentFeed} loading={initialLoading} error={agentError} onCopy={copy} onRetry={load} />
        )}
      </section>
        </div>
      </section>

      <div className={toast ? "copy-toast visible" : "copy-toast"} role="status" aria-live="polite">
        <Check size={14} /> {toast ?? "Copied"}
      </div>
    </>
  );
}

function buildAgentLaunches(events: AgentEvent[]): AgentLaunchSummary[] {
  const submitted = new Map<string, AgentEvent>();
  for (const event of events) {
    if (event.type === "launch.submitted") {
      const token = payloadText(event.payload, "token");
      if (token) submitted.set(token.toLowerCase(), event);
    }
  }
  return events
    .filter((event) => event.type === "launch.confirmed" && payloadText(event.payload, "token") && event.txHash)
    .map((event) => {
      const token = payloadText(event.payload, "token")!;
      const source = submitted.get(token.toLowerCase());
      return {
        token,
        pool: payloadText(event.payload, "pool"),
        name: source ? payloadText(source.payload, "name") : undefined,
        symbol: source ? payloadText(source.payload, "symbol") : undefined,
        topic: source ? payloadText(source.payload, "topic") : undefined,
        totalValueEth: payloadText(event.payload, "totalValueEth") ?? (source ? payloadText(source.payload, "totalValueEth") : undefined),
        txHash: event.txHash!,
        occurredAt: event.occurredAt,
      };
    });
}

function AgentLaunches({ launches, onCopy }: { launches: AgentLaunchSummary[]; onCopy: (value: string, label: string) => void }) {
  return <section className="agent-launches" aria-labelledby="agent-launches-title">
    <div className="agent-launches-header">
      <div><span><Rocket size={13} /> Agent execution proofs</span><h3 id="agent-launches-title">Tokens launched by ZOI AGENT</h3></div>
      <strong>{launches.length} confirmed</strong>
    </div>
    {launches.length === 0 ? <div className="agent-launches-empty"><Clock3 size={15} /> No Agent launch has been confirmed in the signed feed yet.</div> : <div className="agent-launch-list">
      {launches.map((launch) => <article className="agent-launch-row" key={launch.txHash}>
        <div className="agent-launch-identity"><CheckCircle2 size={15} /><div><strong>{launch.name ?? "Token launch"}{launch.symbol ? ` · $${launch.symbol}` : ""}</strong><small>{launch.topic ?? "Agent execution"} · {formatAge(Date.parse(launch.occurredAt))}</small></div></div>
        <div className="agent-launch-contracts"><AddressButton label="CA" value={launch.token} onCopy={onCopy} />{launch.pool ? <AddressButton label="Pool" value={launch.pool} onCopy={onCopy} /> : null}</div>
        <div className="agent-launch-proof"><span>{launch.totalValueEth ? `${launch.totalValueEth} ETH` : "Pons launch"}</span><a href={`${EXPLORER}/tx/${launch.txHash}`} target="_blank" rel="noreferrer" title="Open launch transaction on Blockscout"><code>{short(launch.txHash, 6)}</code><ArrowUpRight size={13} /></a></div>
      </article>)}
    </div>}
  </section>;
}

function LaunchFeed({
  data,
  loading,
  error,
  onCopy,
  onRetry,
}: {
  data: ChainData | null;
  loading: boolean;
  error: string | null;
  onCopy: (value: string, label: string) => void;
  onRetry: () => void;
}) {
  if (loading && !data) return <FeedSkeleton />;
  if (error && !data) return <FeedState icon={<Database size={19} />} text={error} action={onRetry} />;
  if (data && data.launches.length === 0) return <FeedState icon={<Clock3 size={19} />} text="Waiting for the next verified Pons launch" />;

  return (
    <div className="event-table">
      <div className="event-table-head" aria-hidden="true">
        <span>Asset</span><span>Block / buy</span><span>Contracts</span><span>Transaction proof</span>
      </div>
      <div className="event-list">
        {data?.launches.map((launch, index) => (
          <article className={index === 0 ? "event-row newest" : "event-row"} key={launch.txHash}>
            <div className="event-identity">
              <span className="row-index">{String(index + 1).padStart(2, "0")}</span>
              <div className="asset-name"><strong>{launch.name}</strong><span>${launch.symbol}</span><small>{formatAge(launch.timestamp)}</small></div>
            </div>
            <div className="event-values">
              <span><small>Block</small><code>{launch.blockNumber.toLocaleString()}</code></span>
              <span><small>Initial buy</small><code>{Number(launch.initialBuyEth).toFixed(5)} ETH</code></span>
            </div>
            <div className="event-contracts">
              <AddressButton label="Token" value={launch.token} onCopy={onCopy} />
              <AddressButton label="Pool" value={launch.pool} onCopy={onCopy} />
            </div>
            <a className="event-tx" href={launch.explorerUrl} target="_blank" rel="noreferrer" title="Open transaction on Blockscout">
              <code>{short(launch.txHash, 6)}</code><ArrowUpRight size={13} />
            </a>
          </article>
        ))}
      </div>
    </div>
  );
}

function AgentEventFeed({
  feed,
  loading,
  error,
  onCopy,
  onRetry,
}: {
  feed: AgentFeed | null;
  loading: boolean;
  error: string | null;
  onCopy: (value: string, label: string) => void;
  onRetry: () => void;
}) {
  if (loading && !feed) return <FeedSkeleton />;
  if (error && !feed) return <FeedState icon={<ShieldCheck size={19} />} text={error} action={onRetry} />;
  if (feed && feed.events.length === 0) {
    return <FeedState icon={<Clock3 size={19} />} text="Waiting for the first signed Agent event" detail="No X trend, model decision, fee claim or buyback has been published." />;
  }

  return (
    <div className="agent-event-list">
      {feed?.events.map((event, index) => {
        const meta = eventMeta[event.type];
        const Icon = meta.icon;
        const topic = payloadText(event.payload, "topic");
        const name = payloadText(event.payload, "name");
        const symbol = payloadText(event.payload, "symbol");
        const score = payloadNumber(event.payload, "score");
        const token = payloadText(event.payload, "token");
        const pool = payloadText(event.payload, "pool");
        const presentation = describeAgentEvent(event);
        const occurredAt = Date.parse(event.occurredAt);
        return (
          <article className={index === 0 ? "agent-event-row newest" : "agent-event-row"} key={event.eventId}>
            <div className="agent-log-time">
              <Icon size={15} aria-hidden="true" />
              <time dateTime={event.occurredAt} title={new Date(occurredAt).toISOString()}>
                <span>{formatTimestamp(occurredAt)}</span>
                <small>{formatAge(occurredAt)}</small>
              </time>
            </div>
            <div className="agent-event-main">
              <div className="agent-event-title">
                <strong>{meta.title}</strong>
                <span className={`event-outcome ${presentation.outcome}`}>{presentation.label}</span>
                <code>SEQ {event.sequence.toString().padStart(4, "0")}</code>
              </div>
              <p className="agent-log-subject">{name ?? topic ?? presentation.summary}{symbol ? ` · $${symbol}` : ""}{score !== undefined ? ` · score ${score.toFixed(0)}/100` : ""}</p>
              {presentation.detail ? <p className="agent-log-detail" title={presentation.detail}>{presentation.detail}</p> : null}
              <div className="event-links">
                {token ? <AddressButton label="Token" value={token} onCopy={onCopy} /> : null}
                {pool ? <AddressButton label="Pool" value={pool} onCopy={onCopy} /> : null}
                <AddressButton label="Signer" value={event.signer} onCopy={onCopy} />
              </div>
            </div>
            <div className="agent-verification">
              <span><ShieldCheck size={13} /> {event.verification === "chain" ? "Chain verified" : "Signature verified"}</span>
              {event.txHash ? <a href={`${EXPLORER}/tx/${event.txHash}`} target="_blank" rel="noreferrer"><code>{short(event.txHash, 5)}</code><ArrowUpRight size={13} /></a> : <code>SEQ {event.sequence.toString().padStart(4, "0")}</code>}
            </div>
          </article>
        );
      })}
    </div>
  );
}

function describeAgentEvent(event: AgentEvent): { label: string; outcome: EventOutcome; summary: string; detail?: string } {
  const error = payloadText(event.payload, "error");
  if (event.status === "failed" || error) {
    return { label: "Failed", outcome: "failed", summary: "Execution failed", detail: error ?? "The action did not complete." };
  }

  if (event.type === "model.decision") {
    const eligible = payloadBoolean(event.payload, "eligible");
    const blockedReasons = payloadStrings(event.payload, "blockedReasons");
    if (eligible) return { label: "Approved", outcome: "accepted", summary: "Model approved the signal for launch." };
    return {
      label: "Blocked",
      outcome: "blocked",
      summary: "Model rejected the signal.",
      detail: blockedReasons.length ? blockedReasons.join(" · ") : "The signal did not pass the execution policy.",
    };
  }

  if (event.type === "trend.detected") {
    const stage = payloadText(event.payload, "stage");
    return { label: "Observed", outcome: "neutral", summary: stage ? `X trend observed at ${stage} stage.` : "X trend observed." };
  }
  if (event.type === "launch.confirmed") return { label: "Confirmed", outcome: "accepted", summary: "Token launch confirmed on Robinhood Chain." };
  if (event.type === "fees.claimed") return { label: "Confirmed", outcome: "accepted", summary: "Creator fees claimed; 50% assigned to the buyback ledger." };
  if (event.type === "buyback.confirmed") return { label: "Confirmed", outcome: "accepted", summary: "Buyback confirmed on Robinhood Chain." };
  if (event.type === "launch.submitted" || event.type === "buyback.submitted") return { label: "Submitted", outcome: "neutral", summary: "Transaction submitted and awaiting confirmation." };
  if (event.type === "agent.paused") return { label: "Paused", outcome: "blocked", summary: "Agent execution paused." };
  return { label: "Operating", outcome: "accepted", summary: "Agent execution resumed." };
}

function FeedSkeleton() {
  return (
    <div className="feed-skeleton" aria-label="Loading activity" aria-busy="true" role="status">
      {Array.from({ length: 4 }, (_, index) => <div className="skeleton-row" key={index}><Skeleton width="28%" /><Skeleton width="24%" /><Skeleton width="20%" /></div>)}
    </div>
  );
}

function FeedState({ icon, text, detail, action }: { icon: ReactNode; text: string; detail?: string; action?: () => void }) {
  return (
    <div className="feed-state" role={action ? "alert" : "status"} aria-live="polite">
      <span>{icon}</span><strong>{text}</strong>{detail ? <p>{detail}</p> : null}
      {action ? <button type="button" onClick={action}>Retry <RefreshCw size={13} /></button> : null}
    </div>
  );
}

function Skeleton({ width }: { width: string }) {
  return <span className="skeleton" style={{ width }} aria-hidden="true" />;
}

function StatusDatum({ label, value, live = false }: { label: string; value: string; live?: boolean }) {
  return <div className="status-datum"><span>{label}</span><strong>{live ? <i className="status-pulse" /> : null}{value}</strong></div>;
}

function AddressButton({ label, value, onCopy, compact = false }: {
  label: string;
  value: string;
  onCopy: (value: string, label: string) => void;
  compact?: boolean;
}) {
  return (
    <button type="button" className={compact ? "address-button compact" : "address-button"} onClick={() => onCopy(value, label)} title={`Copy ${label.toLowerCase()} address`}>
      {compact ? null : <span>{label}</span>}<code>{short(value, compact ? 5 : 4)}</code><Copy size={12} />
    </button>
  );
}

function buildPipelineStages(events: AgentEvent[]) {
  const find = (...types: AgentEventType[]) => events.find((event) => types.includes(event.type));
  const discover = find("trend.detected");
  const reason = find("model.decision");
  const launch = find("launch.confirmed", "launch.submitted");
  const belongsToFlow = (event: AgentEvent, boundary: AgentEvent) =>
    event.signer.toLowerCase() === boundary.signer.toLowerCase()
    && Date.parse(event.occurredAt) >= Date.parse(boundary.occurredAt);
  const claim = launch
    ? events.find((event) => event.type === "fees.claimed" && belongsToFlow(event, launch))
    : undefined;
  const buyback = claim
    ? events.find((event) =>
      (event.type === "buyback.confirmed" || event.type === "buyback.submitted")
      && belongsToFlow(event, claim))
    : undefined;
  const stateFor = (event: AgentEvent | undefined): StageState => {
    if (!event) return "waiting";
    if (event.status === "confirmed") return "verified";
    if (event.status === "submitted" || event.status === "observed" || event.status === "proposed") return "processing";
    return "queued";
  };
  return [
    { label: "Discover", description: "X market velocity", source: discover ? "Signed trend event" : "Waiting for X signal", event: discover },
    { label: "Reason", description: "5.6sol decision", source: reason ? "Signed model output" : "Waiting for trend", event: reason },
    { label: "Launch", description: "Pons Factory", source: launch ? (launch.verification === "chain" ? "Chain receipt" : "Transaction submitted") : "Waiting for decision", event: launch },
    { label: "Claim", description: "Creator fees", source: claim?.verification === "chain" ? "Chain receipt" : "Waiting for fees", event: claim },
    { label: "Buy back", description: "50% allocation", source: buyback ? (buyback.verification === "chain" ? "Chain receipt" : "Transaction submitted") : "Waiting for balance", event: buyback },
  ].map((stage) => ({ ...stage, state: stateFor(stage.event), txHash: stage.event?.txHash }));
}

function PipelineStage({ index, label, description, source, state, txHash }: {
  index: number;
  label: string;
  description: string;
  source: string;
  state: StageState;
  txHash?: string;
}) {
  return (
    <div className={`pipeline-stage ${state}`}>
      <div className="stage-number">{index.toString().padStart(2, "0")}</div>
      <div className="stage-copy"><strong>{label}</strong><span>{description}</span></div>
      <div className="stage-source"><i /><span>{source}</span></div>
      {txHash ? <a href={`${EXPLORER}/tx/${txHash}`} target="_blank" rel="noreferrer" title="Open transaction on Blockscout"><ExternalLink size={13} /><span className="sr-only">Open stage transaction</span></a> : null}
    </div>
  );
}
