"use client";

import { useCallback, useEffect, useMemo, useState, type FormEvent } from "react";
import {
  Activity,
  AlertTriangle,
  ArrowUpRight,
  Check,
  CircleDot,
  Copy,
  ExternalLink,
  ImageIcon,
  KeyRound,
  LoaderCircle,
  LockKeyhole,
  LogOut,
  Play,
  Pencil,
  RefreshCw,
  ShieldCheck,
  Wallet,
} from "lucide-react";
import { formatEther } from "viem";

type Command = {
  id: string;
  kind: "launch" | "claim";
  status: "queued" | "running" | "completed" | "failed";
  request: Record<string, unknown>;
  result: Record<string, unknown> | null;
  error: string | null;
  createdAt: string;
};

type OperatorStatus = {
  mode: "observe" | "live";
  wallet: string | null;
  walletSource: "encrypted-vault" | "environment" | null;
  walletConfiguredAt: string | null;
  balanceWei: string | null;
  autoClaimEnabled: boolean;
  autoBuybackEnabled: boolean;
  autoLaunchEnabled: boolean;
  launches: Array<{ token: string; pool: string; txHash: string; name: string; symbol: string; createdAt: string }>;
  executions: Array<{ id: string; kind: string; status: string; target: string | null; txHash: string | null; error: string | null; createdAt: string }>;
  commands: Command[];
  cycles: Array<{ id: string; status: string; startedAt: string; completedAt: string | null; decisionCount: number | null; error: string | null }>;
};

type LaunchPrediction = {
  logo: string;
  salt: string;
  predictedTokenAddress: string;
  launchFeeWei: string;
  launchFeeEth: string;
  initialBuyEth: string;
  totalValueWei: string;
  totalValueEth: string;
  walletBalanceWei: string;
  walletBalanceEth: string;
  launchEnabled: boolean;
  addressAvailable: boolean;
  canLaunch: boolean;
  blockers: string[];
};

type LaunchPreparationResponse = {
  prediction: LaunchPrediction;
  prepared: Record<string, unknown>;
};

type ReviewCandidate = {
  topic: string;
  score: number;
  blockedReasons: string[];
  evidence: string[];
  token: { name: string; symbol: string; description: string } | null;
  occurredAt: string;
};

type AgentEvent = {
  type: string;
  occurredAt: string;
  payload: {
    topic?: string;
    score?: number;
    blockedReasons?: string[];
    evidence?: string[];
    token?: ReviewCandidate["token"];
  };
};

type EthereumProvider = { request: (input: { method: string; params?: unknown[] }) => Promise<unknown> };
declare global { interface Window { ethereum?: EthereumProvider } }

const explorer = "https://robinhoodchain.blockscout.com";
const compact = (value: string) => `${value.slice(0, 6)}…${value.slice(-4)}`;
const dateTime = (value: string | null) => value ? new Intl.DateTimeFormat("zh-CN", { dateStyle: "short", timeStyle: "medium" }).format(new Date(value)) : "—";

const readError = async (response: Response) => {
  const body = await response.json().catch(() => ({})) as { error?: string };
  return body.error ?? `Request failed (${response.status})`;
};

export default function OperatorPage() {
  const [authenticated, setAuthenticated] = useState<boolean | null>(null);
  const [csrf, setCsrf] = useState("");
  const [status, setStatus] = useState<OperatorStatus | null>(null);
  const [reviewCandidates, setReviewCandidates] = useState<ReviewCandidate[]>([]);
  const [reviewDraft, setReviewDraft] = useState<ReviewCandidate | null>(null);
  const [connectedAddress, setConnectedAddress] = useState<string | null>(null);
  const [busy, setBusy] = useState<string | null>(null);
  const [notice, setNotice] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  const refresh = useCallback(async () => {
    const response = await fetch("/api/operator/status", { cache: "no-store" });
    if (response.status === 401) {
      setAuthenticated(false);
      setStatus(null);
      return;
    }
    if (!response.ok) throw new Error(await readError(response));
    setStatus(await response.json() as OperatorStatus);
    const eventsResponse = await fetch("/api/agent-events", { cache: "no-store" });
    if (eventsResponse.ok) {
      const body = await eventsResponse.json() as { events?: AgentEvent[] };
      const events = body.events ?? [];
      const evidence = new Map<string, string[]>();
      for (const event of events) {
        if (event.type === "trend.detected" && event.payload.topic && !evidence.has(event.payload.topic.toLowerCase())) {
          evidence.set(event.payload.topic.toLowerCase(), event.payload.evidence ?? []);
        }
      }
      const seen = new Set<string>();
      const candidates = events.flatMap((event) => {
        const topic = event.payload.topic?.trim();
        const blockedReasons = event.payload.blockedReasons ?? [];
        if (event.type !== "model.decision" || !topic || seen.has(topic.toLowerCase()) || !blockedReasons.some((reason) => /(?:manipulation|legal) risk is high/i.test(reason))) return [];
        seen.add(topic.toLowerCase());
        return [{
          topic,
          score: event.payload.score ?? 0,
          blockedReasons,
          evidence: evidence.get(topic.toLowerCase()) ?? [],
          token: event.payload.token ?? null,
          occurredAt: event.occurredAt,
        } satisfies ReviewCandidate];
      }).slice(0, 8);
      setReviewCandidates(candidates);
    }
  }, []);

  useEffect(() => {
    void (async () => {
      try {
        const response = await fetch("/api/operator/session", { cache: "no-store" });
        if (!response.ok) return setAuthenticated(false);
        const session = await response.json() as { csrf: string };
        setCsrf(session.csrf);
        setAuthenticated(true);
        await refresh();
      } catch (cause) {
        setError(cause instanceof Error ? cause.message : "Unable to reach the Agent control service");
        setAuthenticated(false);
      }
    })();
  }, [refresh]);

  useEffect(() => {
    if (!authenticated) return;
    const timer = window.setInterval(() => { void refresh().catch(() => undefined); }, 8_000);
    return () => window.clearInterval(timer);
  }, [authenticated, refresh]);

  const mutate = async (path: string, body: unknown, label: string) => {
    setBusy(label);
    setError(null);
    setNotice(null);
    try {
      const response = await fetch(path, {
        method: "POST",
        headers: { "content-type": "application/json", "x-csrf-token": csrf },
        body: JSON.stringify(body),
      });
      if (!response.ok) throw new Error(await readError(response));
      const result = await response.json() as Record<string, unknown>;
      if (path !== "/api/operator/launch/predict") await refresh();
      return result;
    } finally {
      setBusy(null);
    }
  };

  if (authenticated === null) return <OperatorLoading />;
  if (!authenticated) return <OperatorLogin onLogin={async (password) => {
    setBusy("login"); setError(null);
    try {
      const response = await fetch("/api/operator/login", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ password }),
      });
      if (!response.ok) throw new Error(await readError(response));
      const session = await response.json() as { csrf: string };
      setCsrf(session.csrf); setAuthenticated(true); await refresh();
    } catch (cause) { setError(cause instanceof Error ? cause.message : "Login failed"); }
    finally { setBusy(null); }
  }} busy={busy === "login"} error={error} />;

  const walletMatches = Boolean(status?.wallet && connectedAddress && status.wallet.toLowerCase() === connectedAddress.toLowerCase());
  const balance = status?.balanceWei ? Number(formatEther(BigInt(status.balanceWei))).toFixed(5) : "—";

  return (
    <main className="operator-shell">
      <header className="operator-topbar">
        <a className="operator-brand" href="/" aria-label="Pons Agent public site"><span className="operator-node"><i /><i /><i /></span><strong>PONS AGENT</strong><span>OPERATOR</span></a>
        <div className="operator-network"><i /><span>Robinhood Chain</span><code>4663</code></div>
        <button className="icon-control" type="button" title="退出" aria-label="退出" onClick={() => void mutate("/api/operator/logout", {}, "logout").then(() => setAuthenticated(false))}><LogOut size={16} /></button>
      </header>

      <section className="operator-overview">
        <div>
          <span className="operator-kicker">Private execution control</span>
          <h1>Agent execution desk</h1>
          <p>配置一次执行钱包。后续发币、手续费领取与回购由同一个 Agent 钱包签名并按顺序执行。</p>
        </div>
        <dl className="operator-metrics">
          <div><dt>Agent mode</dt><dd><span className={status?.mode === "live" ? "mode-live" : "mode-observe"}>{status?.mode ?? "—"}</span></dd></div>
          <div><dt>Wallet balance</dt><dd>{balance} <small>ETH</small></dd></div>
          <div><dt>Launches</dt><dd>{status?.launches.length ?? 0}</dd></div>
          <div><dt>Queue</dt><dd>{status?.commands.filter((item) => item.status === "queued" || item.status === "running").length ?? 0}</dd></div>
        </dl>
      </section>

      {status?.mode !== "live" && <div className="operator-mode-notice"><ShieldCheck size={16} /><p>当前是观察模式。后台命令会完成模拟和记录，但不会广播真实交易；切换真实资金仍需在服务器明确启用 live 模式。</p></div>}
      {(notice || error) && <div className={error ? "operator-toast error" : "operator-toast"} role="status">{error ?? notice}<button onClick={() => { setError(null); setNotice(null); }} aria-label="关闭">×</button></div>}

      <div className="operator-grid">
        <section className="operator-panel wallet-panel">
          <PanelHeader number="01" title="Execution wallet" icon={<Wallet size={17} />} />
          <div className="wallet-current">
            <div><span>Agent wallet</span><strong>{status?.wallet ? compact(status.wallet) : "Not configured"}</strong><small>{status?.walletSource === "encrypted-vault" ? "Encrypted vault" : status?.walletSource === "environment" ? "Server environment" : "No signing authority"}</small></div>
            {status?.wallet && <button type="button" className="copy-control" onClick={() => void navigator.clipboard.writeText(status.wallet!).then(() => setNotice("钱包地址已复制"))}><Copy size={14} /> Copy</button>}
          </div>
          <button className="operator-button secondary full" type="button" onClick={async () => {
            setError(null);
            try {
              if (!window.ethereum) throw new Error("未检测到浏览器钱包");
              const accounts = await window.ethereum.request({ method: "eth_requestAccounts" }) as string[];
              setConnectedAddress(accounts[0] ?? null);
            } catch (cause) { setError(cause instanceof Error ? cause.message : "连接钱包失败"); }
          }}><Wallet size={15} />{connectedAddress ? compact(connectedAddress) : "连接钱包核对地址"}{walletMatches && <Check size={14} />}</button>
          <WalletProvisionForm connectedAddress={connectedAddress} busy={busy === "wallet"} onSubmit={async (privateKey) => {
            try {
              const result = await mutate("/api/operator/wallet", { privateKey, expectedAddress: connectedAddress ?? undefined }, "wallet");
              setNotice(`执行钱包已配置：${compact(String(result?.address ?? ""))}`);
            } catch (cause) { setError(cause instanceof Error ? cause.message : "钱包配置失败"); }
          }} />
          <div className="security-facts"><span><LockKeyhole size={13} />HTTPS 传输</span><span><KeyRound size={13} />AES-256-GCM</span><span><ShieldCheck size={13} />私钥不回显</span></div>
        </section>

        <section className="operator-panel launch-panel" id="launch-panel">
          <PanelHeader number="02" title="Launch on Pons" icon={<Play size={17} />} />
          <LaunchForm reviewDraft={reviewDraft} disabled={!status?.wallet} predicting={busy === "launch-predict" || busy === "logo-upload"} launching={busy === "launch"} onUploadLogo={async (payload) => {
            const result = await mutate("/api/operator/logo", payload, "logo-upload");
            return String(result.logo);
          }} onPredict={async (payload) => {
            const result = await mutate("/api/operator/launch/predict", payload, "launch-predict");
            return result as unknown as LaunchPreparationResponse;
          }} onSubmit={async (payload) => {
            try {
              const result = await mutate("/api/operator/commands/launch", payload, "launch");
              setNotice(`发币任务已进入 Agent 队列：${String(result?.commandId ?? "")}`);
            } catch (cause) { setError(cause instanceof Error ? cause.message : "发币任务提交失败"); }
          }} />
        </section>

        <section className="operator-panel policy-panel">
          <PanelHeader number="03" title="Automation policy" icon={<Activity size={17} />} />
          <div className="policy-list">
            <PolicyRow label="Automatic fee claim" enabled={Boolean(status?.autoClaimEnabled)} detail="检查 Agent 已发射代币的 creator fees" />
            <PolicyRow label="Automatic buyback" enabled={Boolean(status?.autoBuybackEnabled)} detail="使用 creator fees 的 50% 回购热点代币" />
            <PolicyRow label="Autonomous launch" enabled={Boolean(status?.autoLaunchEnabled)} detail="按趋势评分自动创建新代币" />
          </div>
          <div className="allocation-rule"><span>BUYBACK ALLOCATION</span><strong>1% × 70% × 50%</strong><code>= 35% gross fee revenue</code></div>
          <button className="operator-button secondary full" type="button" disabled={!status?.wallet || busy === "claim"} onClick={() => void mutate("/api/operator/commands/claim", {}, "claim").then(() => setNotice("领费任务已进入 Agent 队列")).catch((cause) => setError(cause instanceof Error ? cause.message : "任务提交失败"))}>{busy === "claim" ? <LoaderCircle className="spin" size={15} /> : <RefreshCw size={15} />}立即检查并领取手续费</button>
        </section>
      </div>

      <section className="operator-reviews" aria-labelledby="risk-review-title">
        <PanelHeader number="05" title="Manual risk review" icon={<AlertTriangle size={17} />} />
        {reviewCandidates.length === 0 ? <div className="empty-reviews"><ShieldCheck size={18} /><span>No high-risk candidates awaiting review</span></div> : <div className="review-list">
          {reviewCandidates.map((candidate) => <article className="review-row" key={`${candidate.topic}-${candidate.occurredAt}`}>
            <div className="review-score"><strong>{candidate.score}</strong><span>score</span></div>
            <div className="review-copy">
              <strong>{candidate.topic}</strong>
              <p>{candidate.blockedReasons.join(" · ")}</p>
              {candidate.evidence.length > 0 && <small>{candidate.evidence.slice(0, 2).join(" ")}</small>}
            </div>
            <button className="operator-button secondary" type="button" onClick={() => {
              setReviewDraft(candidate);
              setNotice("候选已载入发币表单；请核对资料、头像、CA 和费用后手动确认");
              document.getElementById("launch-panel")?.scrollIntoView({ behavior: "smooth", block: "start" });
            }}>Review for launch</button>
          </article>)}
        </div>}
      </section>

      <section className="operator-ledger">
        <div className="ledger-title"><div><span>04</span><h2>Execution queue</h2></div><button className="icon-control" type="button" title="刷新" aria-label="刷新" onClick={() => void refresh()}><RefreshCw size={15} /></button></div>
        <CommandTable commands={status?.commands ?? []} />
      </section>
    </main>
  );
}

function OperatorLoading() { return <main className="operator-loading"><span className="operator-node"><i /><i /><i /></span><LoaderCircle className="spin" size={18} /><span>Connecting to Agent</span></main>; }

function OperatorLogin({ onLogin, busy, error }: { onLogin: (password: string) => Promise<void>; busy: boolean; error: string | null }) {
  const [password, setPassword] = useState("");
  return <main className="operator-login"><form onSubmit={(event) => { event.preventDefault(); void onLogin(password); }}><div className="login-brand"><span className="operator-node"><i /><i /><i /></span><div><strong>PONS AGENT</strong><span>Private operator</span></div></div><h1>Operator login</h1><label>密码<input type="password" autoComplete="current-password" value={password} onChange={(event) => setPassword(event.target.value)} autoFocus /></label>{error && <p className="form-error">{error}</p>}<button className="operator-button primary full" disabled={busy || !password}>{busy ? <LoaderCircle className="spin" size={15} /> : <LockKeyhole size={15} />}登录</button><a href="/">返回公开页面 <ArrowUpRight size={13} /></a></form></main>;
}

function PanelHeader({ number, title, icon }: { number: string; title: string; icon: React.ReactNode }) { return <header className="panel-header"><span>{number}</span><h2>{title}</h2>{icon}</header>; }

function WalletProvisionForm({ connectedAddress, busy, onSubmit }: { connectedAddress: string | null; busy: boolean; onSubmit: (key: string) => Promise<void> }) {
  const [privateKey, setPrivateKey] = useState("");
  return <form className="operator-form wallet-form" onSubmit={(event) => { event.preventDefault(); void onSubmit(privateKey).then(() => setPrivateKey("")); }}><label>执行钱包私钥<input type="password" spellCheck={false} autoComplete="off" placeholder="0x…" value={privateKey} onChange={(event) => setPrivateKey(event.target.value.trim())} /></label><p>{connectedAddress ? "导入的私钥必须与已连接钱包地址一致。" : "未连接浏览器钱包时，将直接以私钥推导地址并设为当前 Agent 钱包。"}</p><button className="operator-button primary full" disabled={busy || !/^0x[a-fA-F0-9]{64}$/.test(privateKey)}>{busy ? <LoaderCircle className="spin" size={15} /> : <KeyRound size={15} />}配置为当前 Agent 钱包</button></form>;
}

const normalizeHttpsUrl = (value: string) => {
  const trimmed = value.trim();
  if (!trimmed) return "";
  const candidate = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
  const url = new URL(candidate);
  if (url.protocol !== "https:" || url.username || url.password) throw new Error("链接必须是有效的 HTTPS 地址");
  return url.href;
};

const resizePng = async (file: File) => {
  const bitmap = await createImageBitmap(file);
  const canvas = document.createElement("canvas");
  canvas.width = 1024;
  canvas.height = 1024;
  const context = canvas.getContext("2d");
  if (!context) throw new Error("浏览器不支持头像处理");
  context.drawImage(bitmap, 0, 0, 1024, 1024);
  bitmap.close();
  const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, "image/png"));
  if (!blob) throw new Error("头像转换失败");
  const bytes = new Uint8Array(await blob.arrayBuffer());
  let binary = "";
  for (let index = 0; index < bytes.length; index += 0x8000) binary += String.fromCharCode(...bytes.subarray(index, index + 0x8000));
  return btoa(binary);
};

function LaunchForm({ reviewDraft, disabled, predicting, launching, onUploadLogo, onPredict, onSubmit }: {
  reviewDraft: ReviewCandidate | null;
  disabled: boolean;
  predicting: boolean;
  launching: boolean;
  onUploadLogo: (payload: Record<string, unknown>) => Promise<string>;
  onPredict: (payload: Record<string, unknown>) => Promise<LaunchPreparationResponse>;
  onSubmit: (payload: Record<string, unknown>) => Promise<void>;
}) {
  const [values, setValues] = useState({ name: "", symbol: "", topic: "", description: "", initialBuyEth: "0", twitter: "", website: "" });
  const [logoFile, setLogoFile] = useState<File | null>(null);
  const [preparation, setPreparation] = useState<LaunchPreparationResponse | null>(null);
  const [formError, setFormError] = useState<string | null>(null);
  const locked = Boolean(preparation);
  useEffect(() => {
    if (!reviewDraft || locked) return;
    setValues((current) => ({
      ...current,
      name: reviewDraft.token?.name ?? "",
      symbol: reviewDraft.token?.symbol ?? "",
      topic: reviewDraft.topic,
      description: reviewDraft.token?.description ?? "",
    }));
    setFormError(null);
  }, [reviewDraft, locked]);
  const update = (name: keyof typeof values) => (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
    setValues((current) => ({ ...current, [name]: name === "symbol" ? event.target.value.toUpperCase() : event.target.value }));
    setFormError(null);
  };
  const draft = () => {
    if (!values.name.trim()) throw new Error("请输入代币名称");
    if (!values.symbol.trim()) throw new Error("请输入代币代码");
    if (!values.topic.trim()) throw new Error("请输入热点或主题");
    if (!/^(?:0|[1-9]\d*)(?:\.\d{1,18})?$/.test(values.initialBuyEth.trim())) throw new Error("首笔买入数量格式无效，最多 18 位小数");
    return {
      name: values.name.trim(),
      symbol: values.symbol.trim().toUpperCase(),
      topic: values.topic.trim(),
      description: values.description.trim(),
      initialBuyEth: values.initialBuyEth.trim(),
      socials: {
        twitter: normalizeHttpsUrl(values.twitter),
        website: normalizeHttpsUrl(values.website),
      },
    };
  };
  const predict = async () => {
    try {
      setFormError(null);
      const draftPayload = draft();
      if (!logoFile) throw new Error("请先上传 1024x1024 PNG 头像");
      const imageBase64 = await resizePng(logoFile);
      const logo = await onUploadLogo({ name: draftPayload.name, symbol: draftPayload.symbol, topic: draftPayload.topic, imageBase64 });
      setPreparation(await onPredict({ ...draftPayload, logo }));
    } catch (cause) {
      setFormError(cause instanceof Error ? cause.message : "CA 预测失败");
    }
  };
  const prediction = preparation?.prediction;

  return <form className="operator-form launch-form" noValidate onSubmit={(event) => { event.preventDefault(); if (!preparation) void predict(); }}>
    <div className="launch-steps" aria-label="发币步骤"><span className="active"><i>1</i>填写资料</span><b /><span className={locked ? "active" : ""}><i>2</i>核对并发射</span></div>
    <fieldset disabled={locked || predicting || launching}>
      <div className="form-pair"><label>Token name<input value={values.name} maxLength={64} onChange={update("name")} /></label><label>Symbol<input value={values.symbol} maxLength={16} onChange={update("symbol")} /></label></div>
      <label>Trend / topic<input value={values.topic} maxLength={160} onChange={update("topic")} /></label>
      <label>Description<textarea value={values.description} maxLength={1000} rows={3} onChange={update("description")} /></label>
      <div className="form-pair"><label>Initial buy (ETH)<input inputMode="decimal" value={values.initialBuyEth} onChange={update("initialBuyEth")} /></label><label>X URL<input type="text" inputMode="url" placeholder="x.com/..." value={values.twitter} onChange={update("twitter")} /></label></div>
      <label>Website URL<input type="text" inputMode="url" placeholder="example.com" value={values.website} onChange={update("website")} /></label>
      <label>Token avatar<input type="file" accept="image/png" onChange={(event) => setLogoFile(event.target.files?.[0] ?? null)} /></label>
    </fieldset>
    {formError && <p className="launch-form-error" role="alert">{formError}</p>}
    {!preparation ? <>
      <div className="logo-generation"><ImageIcon size={14} /><span>使用你上传的 PNG 头像保存为链上资产，并预测 CA。</span></div>
      <button className="operator-button primary full" disabled={disabled || predicting || launching}>
        {predicting ? <LoaderCircle className="spin" size={15} /> : <CircleDot size={15} />}
        {disabled ? "先配置执行钱包" : predicting ? "正在上传头像与预测 CA" : "使用头像并预测 CA"}
      </button>
    </> : prediction && <div className="launch-review">
      <div className="prediction-head">
        <img src={prediction.logo} alt={`${values.name} token logo`} width={72} height={72} />
        <div><span>Predicted token address</span><strong>{prediction.predictedTokenAddress}</strong><div className="prediction-actions"><button type="button" className="copy-control" onClick={() => void navigator.clipboard.writeText(prediction.predictedTokenAddress)}><Copy size={13} />复制 CA</button><a href={`${explorer}/address/${prediction.predictedTokenAddress}`} target="_blank" rel="noreferrer">Blockscout <ExternalLink size={12} /></a></div></div>
      </div>
      <dl className="prediction-metrics">
        <div><dt>Pons fee</dt><dd>{prediction.launchFeeEth} ETH</dd></div>
        <div><dt>Initial buy</dt><dd>{prediction.initialBuyEth} ETH</dd></div>
        <div><dt>Total value</dt><dd>{prediction.totalValueEth} ETH</dd></div>
        <div><dt>Wallet balance</dt><dd>{Number(prediction.walletBalanceEth).toFixed(6)} ETH</dd></div>
      </dl>
      <div className="prediction-salt"><span>Salt</span><code>{prediction.salt}</code></div>
      {prediction.blockers.length > 0 && <div className="prediction-blockers" role="alert">{prediction.blockers.map((blocker) => <p key={blocker}>{blocker}</p>)}</div>}
      <div className="launch-review-actions">
        <button type="button" className="operator-button secondary" disabled={launching} onClick={() => setPreparation(null)}><Pencil size={14} />修改资料</button>
        <button type="button" className="operator-button primary" disabled={launching || !prediction.canLaunch} onClick={() => void onSubmit(preparation.prepared).catch((cause) => setFormError(cause instanceof Error ? cause.message : "发币任务提交失败"))}>{launching ? <LoaderCircle className="spin" size={15} /> : <Play size={15} />}{launching ? "正在提交" : "确认并立即发射"}</button>
      </div>
    </div>}
  </form>;
}

function PolicyRow({ label, enabled, detail }: { label: string; enabled: boolean; detail: string }) { return <div className="policy-row"><i className={enabled ? "enabled" : ""} /><div><strong>{label}</strong><span>{detail}</span></div><code>{enabled ? "ENABLED" : "DISABLED"}</code></div>; }

function CommandTable({ commands }: { commands: Command[] }) {
  const rows = useMemo(() => commands.slice(0, 20), [commands]);
  if (!rows.length) return <div className="empty-queue"><Activity size={18} /><span>还没有后台任务</span></div>;
  return <div className="command-table" role="table"><div className="command-head" role="row"><span>Task</span><span>Type</span><span>Status</span><span>Created</span><span>Proof</span></div>{rows.map((command) => {
    const claims = Array.isArray(command.result?.claims) ? command.result.claims.filter((item): item is Record<string, unknown> => Boolean(item && typeof item === "object")) : [];
    const txHashes = [
      typeof command.result?.txHash === "string" ? command.result.txHash : null,
      ...claims.map((claim) => typeof claim.txHash === "string" ? claim.txHash : null),
    ].filter((value): value is string => Boolean(value));
    const label = typeof command.request.name === "string" ? `${command.request.name} / ${String(command.request.symbol ?? "")}` : "Fee claim";
    const outcome = command.error
      ?? (command.kind === "claim" && command.status === "completed" && claims.length === 0 ? "未发现该钱包发行的 Pons 代币"
        : command.kind === "claim" && claims.length > 0 && claims.every((claim) => claim.mode === "skipped") ? "已检查，当前没有可领取手续费"
          : command.status === "queued" ? "等待 Agent 执行"
            : command.status === "running" ? "Agent 正在执行" : null);
    return <div className="command-row" role="row" key={command.id}><div><strong>{label}</strong><small>{outcome ?? command.id.slice(0, 8)}</small></div><span>{command.kind}</span><span className={`command-status ${command.status}`}><i />{command.status}</span><time>{dateTime(command.createdAt)}</time><div className="command-proofs">{txHashes.length ? txHashes.map((txHash) => <a key={txHash} href={`${explorer}/tx/${txHash}`} target="_blank" rel="noreferrer">{compact(txHash)} <ExternalLink size={12} /></a>) : command.error ? <span className="command-error" title={command.error}>查看错误</span> : <span>{outcome ?? "—"}</span>}</div></div>;
  })}</div>;
}
