import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { api, ContentInboxItem, CopyMissionInspiration, Persona, ThreadsAccount } from "../api/haixun";
import { Badge, Button, Card, ErrorText, Field, Input, PageTitle, Select, Textarea } from "../components/ui";
import { formatNano, inputDateTimeToNano, nanoToInputDateTime, shortText } from "../lib/format";
import { notifyJobCreated, requestJobsRefresh } from "../lib/jobEvents";

type Mode = "direct" | "inspire" | "rewrite" | "matrix";
type ContentDirection = "auto" | "daily" | "expert" | "resonance" | "checklist" | "hot_take" | "interaction";
type InspirationKnowledgeCard = {
  id: string;
  title: string;
  detail: string;
  source?: string;
};
type ContentPlanDraft = {
  id?: string;
  topicCandidateId?: string;
  topic: string;
  mission: string;
  targetAudience: string;
  angle: string;
  openingType: string;
  bodyType: string;
  emotion: string;
  ctaType: string;
  riskLevel: string;
  avoidText: string;
};

const TERMINAL_JOB_STATUSES = new Set(["succeeded", "failed", "cancelled", "completed"]);

function useCopyJobWatcher(onComplete: () => void) {
  const pendingRef = useRef<Set<string>>(new Set());

  const trackJob = useCallback((jobId: string) => {
    if (!jobId) return;
    pendingRef.current.add(jobId);
    requestJobsRefresh();
  }, []);

  useEffect(() => {
    const timer = window.setInterval(() => {
      if (pendingRef.current.size === 0) return;
      void (async () => {
        for (const jobId of [...pendingRef.current]) {
          try {
            const job = await api.getJob(jobId);
            if (TERMINAL_JOB_STATUSES.has(job.status)) {
              pendingRef.current.delete(jobId);
              onComplete();
              requestJobsRefresh();
            }
          } catch {
            // keep polling until the job can be fetched
          }
        }
      })();
    }, 2000);
    return () => window.clearInterval(timer);
  }, [onComplete]);

  return { trackJob, hasPending: () => pendingRef.current.size > 0 };
}

function accountLabel(account: ThreadsAccount) {
  return account.display_name || account.username || account.id;
}

function personaLabel(persona: Persona) {
  return persona.display_name || persona.persona || persona.id;
}

function normalizeTopicTag(value: string) {
  return value.trim().replace(/^#+/, "").trim();
}

const CONTENT_DIRECTIONS: Array<{ id: ContentDirection; title: string; desc: string }> = [
  { id: "auto", title: "AI 幫我選", desc: "看熱度與人設自動選方向" },
  { id: "daily", title: "日常陪伴", desc: "生活感、心情、慢慢說" },
  { id: "expert", title: "專業知識", desc: "觀念、名詞、流程整理" },
  { id: "resonance", title: "情緒共鳴", desc: "讓讀者覺得這就是我" },
  { id: "checklist", title: "工具清單", desc: "可收藏、可照著做" },
  { id: "hot_take", title: "熱門觀點", desc: "接社群討論講自己的看法" },
  { id: "interaction", title: "互動提問", desc: "適合留言與社群感" }
];

function directionText(direction: ContentDirection) {
  const item = CONTENT_DIRECTIONS.find((option) => option.id === direction);
  return item ? `${item.title}：${item.desc}` : direction;
}

function ContentDirectionPicker({ value, onChange }: { value: ContentDirection; onChange: (value: ContentDirection) => void }) {
  return (
    <div className="grid-3" style={{ gap: "0.65rem" }}>
      {CONTENT_DIRECTIONS.map((item) => (
        <button
          key={item.id}
          type="button"
          className="card"
          style={{ textAlign: "left", cursor: "pointer", padding: "0.85rem", borderColor: value === item.id ? "var(--hx-brand)" : undefined }}
          onClick={() => onChange(item.id)}
        >
          <strong>{item.title}</strong>
          <p className="muted hx-text-sm" style={{ margin: "0.35rem 0 0" }}>{item.desc}</p>
        </button>
      ))}
    </div>
  );
}

function lifecycleBadge(lifecycle: string) {
  if (lifecycle === "published") return <Badge variant="success">已發布</Badge>;
  if (lifecycle === "scheduled") return <Badge variant="warning">已排程</Badge>;
  return <Badge variant="neutral">草稿</Badge>;
}

function reviewBadge(status?: string) {
  if (status === "ready") return <Badge variant="success">已核准</Badge>;
  if (status === "rejected") return <Badge variant="danger">已退回</Badge>;
  return <Badge variant="warning">待審核</Badge>;
}

function appendReviewNote(rationale: string | undefined, decision: "approve" | "reject", note: string) {
  const label = decision === "approve" ? "人工核准" : "人工退回";
  const cleanNote = note.trim() || (decision === "approve" ? "可進入後續排程。" : "需要重寫或調整。 ");
  return [rationale?.trim(), `【${label}】${cleanNote}`].filter(Boolean).join("\n\n");
}

function QualityScoreRow({ item }: { item: ContentInboxItem }) {
  const scores = [
    { label: "AI感", value: item.ai_score, badHigh: true },
    { label: "公式感", value: item.formula_score, badHigh: true },
    { label: "人設", value: item.brand_fit_score, badHigh: false },
    { label: "風險", value: item.risk_score, badHigh: true },
    { label: "相似", value: item.similarity_score, badHigh: true },
    { label: "互動", value: item.engagement_potential, badHigh: false },
    { label: "新鮮", value: item.freshness_score, badHigh: false }
  ].filter((score) => typeof score.value === "number" && score.value > 0);
  if (!scores.length) return null;
  return (
    <div className="button-row" style={{ flexWrap: "wrap", marginTop: "0.45rem" }}>
      {scores.map((score) => {
        const value = Number(score.value || 0);
        const danger = score.badHigh ? value >= 70 : value < 60;
        const warning = score.badHigh ? value >= 45 && value < 70 : value >= 60 && value < 75;
        return <Badge key={score.label} variant={danger ? "danger" : warning ? "warning" : "success"}>{score.label} {value}</Badge>;
      })}
    </div>
  );
}

export function MissionsPage() {
  const [accounts, setAccounts] = useState<ThreadsAccount[]>([]);
  const [personas, setPersonas] = useState<Persona[]>([]);
  const [accountId, setAccountId] = useState("");
  const [personaId, setPersonaId] = useState("");
  const [activeMode, setActiveMode] = useState<Mode>("inspire");
  const [draftRefreshKey, setDraftRefreshKey] = useState(0);
  const [message, setMessage] = useState("");
  const [error, setError] = useState<unknown>();
  const [loading, setLoading] = useState(false);

  const selectedPersona = useMemo(() => personas.find((item) => item.id === personaId), [personas, personaId]);

  async function loadBasics() {
    setLoading(true);
    setError(undefined);
    try {
      const [accountData, personaData] = await Promise.all([api.threadsAccounts(), api.personas()]);
      const nextAccounts = accountData.list || [];
      const nextPersonas = personaData.list || [];
      setAccounts(nextAccounts);
      setPersonas(nextPersonas);
      setAccountId((current) => current || accountData.active_account_id || nextAccounts[0]?.id || "");
      setPersonaId((current) => current || nextPersonas[0]?.id || "");
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    void loadBasics();
  }, []);

  const refreshDraftBoard = useCallback(() => {
    setDraftRefreshKey((value) => value + 1);
  }, []);

  const { trackJob } = useCopyJobWatcher(() => {
    setMessage("背景任務完成，草稿列表已更新。");
    refreshDraftBoard();
  });

  return (
    <div className="page-grid">
      <PageTitle
        title="AI 經營任務"
        subtitle="照 AI Threads Account Operator 流程：話題雷達、內容計畫、三版草稿、品質閘門、人工審核。"
        action={<Button loading={loading} onClick={() => void loadBasics()}>重新載入</Button>}
      />

      <Card>
        <div className="grid-2">
          <Field label="Threads 帳號" hint="直接排程會送到這個帳號。">
            <Select value={accountId} onChange={(event) => setAccountId(event.target.value)}>
              {accounts.map((account) => (
                <option key={account.id} value={account.id}>{accountLabel(account)}</option>
              ))}
            </Select>
          </Field>
          <Field label="人設語氣" hint="可選。用來抓語氣，不強制套模板。">
            <Select value={personaId} onChange={(event) => setPersonaId(event.target.value)}>
              {personas.map((persona) => (
                <option key={persona.id} value={persona.id}>{personaLabel(persona)}</option>
              ))}
            </Select>
          </Field>
        </div>
        {selectedPersona?.brief || selectedPersona?.style_profile ? (
          <p className="muted hx-text-sm" style={{ margin: "0.75rem 0 0" }}>
            目前語氣：{shortText(selectedPersona.brief || selectedPersona.style_profile, 120)}
          </p>
        ) : null}
      </Card>

      <ErrorText error={error} />
      {message ? <Card><p>{message}</p></Card> : null}

      <MemoryCard personaId={personaId} onMessage={setMessage} onError={setError} />

      <ModeTabs activeMode={activeMode} onChange={setActiveMode} />

      {activeMode === "direct" ? (
        <DirectPostCard accountId={accountId} onDone={(text) => setMessage(text)} onError={setError} />
      ) : null}
      {activeMode === "inspire" ? (
        <InspirationCard personaId={personaId} onMessage={setMessage} onDraftsChanged={refreshDraftBoard} onTrackJob={trackJob} onError={setError} />
      ) : null}
      {activeMode === "rewrite" ? (
        <RewritePostCard accountId={accountId} personaId={personaId} onMessage={setMessage} onDraftsChanged={refreshDraftBoard} onTrackJob={trackJob} onError={setError} />
      ) : null}
      {activeMode === "matrix" ? (
        <TopicMatrixCard personaId={personaId} onMessage={setMessage} onTrackJob={trackJob} onError={setError} />
      ) : null}

      <DraftBoard
        personaId={personaId}
        accountId={accountId}
        refreshKey={draftRefreshKey}
        onDone={setMessage}
        onError={setError}
      />
    </div>
  );
}

function MemoryCard({ personaId, onMessage, onError }: { personaId: string; onMessage: (message: string) => void; onError: (error: unknown) => void }) {
  const [sources, setSources] = useState(0);
  const [chunks, setChunks] = useState(0);
  const [formulas, setFormulas] = useState(0);
  const [loading, setLoading] = useState(false);

  async function load() {
    if (!personaId) return;
    setLoading(true);
    try {
      const [sourceData, chunkData, formulaData] = await Promise.all([api.knowledgeSources(personaId), api.knowledgeChunks(personaId), api.formulaPools(personaId)]);
      setSources(sourceData.list?.length || 0);
      setChunks(chunkData.list?.length || 0);
      setFormulas(formulaData.list?.length || 0);
    } catch (err) {
      onError(err);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => { void load(); }, [personaId]);

  async function importFile(file: File) {
    if (!personaId) return;
    setLoading(true);
    onError(undefined);
    try {
      const isXlsx = file.name.toLowerCase().endsWith(".xlsx");
      const body: { source_type: string; title: string; filename: string; content?: string; content_base64?: string } = {
        source_type: isXlsx ? "excel" : file.name.toLowerCase().endsWith(".csv") ? "csv" : "markdown",
        title: file.name,
        filename: file.name
      };
      if (isXlsx) {
        const buffer = await file.arrayBuffer();
        body.content_base64 = arrayBufferToBase64(buffer);
      } else {
        body.content = await file.text();
      }
      const saved = await api.createKnowledgeSource(personaId, body);
      onMessage(`已匯入 ${saved.filename || saved.title || "知識來源"}，建立 ${saved.chunk_count || 0} 個知識片段。`);
      await load();
    } catch (err) {
      onError(err);
    } finally {
      setLoading(false);
    }
  }

  return (
    <Card title="帳號大腦" tone="sky" action={<Button loading={loading} onClick={() => void load()}>重新讀取記憶</Button>}>
      <div className="page-grid">
        <div className="grid-3" style={{ gap: "0.65rem" }}>
          <StatMini label="知識來源" value={sources} />
          <StatMini label="知識片段" value={chunks} />
          <StatMini label="公式池" value={formulas} />
        </div>
        <Field label="匯入 Excel / Markdown / CSV / TXT" hint="會寫入 Knowledge Memory；下一次產文會讀這些知識與公式池。">
          <Input type="file" accept=".xlsx,.md,.txt,.csv" onChange={(event) => { const file = event.target.files?.[0]; if (file) void importFile(file); event.currentTarget.value = ""; }} />
        </Field>
      </div>
    </Card>
  );
}

function StatMini({ label, value }: { label: string; value: number }) {
  return <div className="card" style={{ padding: "0.8rem" }}><strong>{value}</strong><p className="muted hx-text-sm" style={{ margin: "0.25rem 0 0" }}>{label}</p></div>;
}

function arrayBufferToBase64(buffer: ArrayBuffer) {
  let binary = "";
  const bytes = new Uint8Array(buffer);
  for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
  return window.btoa(binary);
}

function ModeTabs({ activeMode, onChange }: { activeMode: Mode; onChange: (mode: Mode) => void }) {
  const modes: Array<{ id: Mode; title: string; desc: string }> = [
    { id: "inspire", title: "話題雷達", desc: "找題、評估、產 Content Plan。" },
    { id: "matrix", title: "草稿工廠", desc: "已有主題時產多版草稿。" },
    { id: "rewrite", title: "貼一篇改寫", desc: "貼參考文，換成你的話題。" },
    { id: "direct", title: "直接寫", desc: "手動輸入內容，保留舊流程。" }
  ];

  return (
    <div className="grid-3">
      {modes.map((mode) => (
        <button
          key={mode.id}
          type="button"
          className="card"
          style={{ textAlign: "left", cursor: "pointer", borderColor: activeMode === mode.id ? "var(--hx-brand)" : undefined }}
          onClick={() => onChange(mode.id)}
        >
          <div className="button-row" style={{ justifyContent: "space-between" }}>
            <strong>{mode.title}</strong>
            {activeMode === mode.id ? <Badge variant="brand">使用中</Badge> : null}
          </div>
          <p className="muted" style={{ margin: "0.45rem 0 0" }}>{mode.desc}</p>
        </button>
      ))}
    </div>
  );
}

function DirectPostCard({ accountId, onDone, onError }: { accountId: string; onDone: (message: string) => void; onError: (error: unknown) => void }) {
  const [text, setText] = useState("");
  const [topicTag, setTopicTag] = useState("");
  const [scheduledAt, setScheduledAt] = useState("");
  const [submitting, setSubmitting] = useState(false);

  async function submit() {
    if (!accountId || !text.trim()) return;
    setSubmitting(true);
    onError(undefined);
    try {
      await api.createPublishQueue(accountId, {
        text: text.trim(),
        topic_tag: normalizeTopicTag(topicTag) || undefined,
        scheduled_at: inputDateTimeToNano(scheduledAt) || Date.now() * 1_000_000
      });
      setText("");
      setTopicTag("");
      setScheduledAt("");
      onDone(scheduledAt ? "已排入發文佇列。" : "已送進發文佇列，系統會盡快發出。");
    } catch (err) {
      onError(err);
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <Card title="直接寫" tone="mint">
      <div className="page-grid">
        <Field label="貼文內容">
          <Textarea value={text} onChange={(event) => setText(event.target.value)} rows={7} placeholder="直接輸入要發的內容。不要想流程，先寫出來。" />
        </Field>
        <div className="grid-2">
          <Field label="標籤（可選）">
            <Input value={topicTag} onChange={(event) => setTopicTag(event.target.value)} placeholder="例如：AI工具" />
          </Field>
          <Field label="排程時間（空白就是盡快發）">
            <Input type="datetime-local" value={scheduledAt} onChange={(event) => setScheduledAt(event.target.value)} />
          </Field>
        </div>
        <Button variant="primary" loading={submitting} disabled={!accountId || !text.trim()} onClick={() => void submit()}>排程這篇</Button>
      </div>
    </Card>
  );
}

function InspirationCard({
  personaId,
  onMessage,
  onDraftsChanged,
  onTrackJob,
  onError
}: {
  personaId: string;
  onMessage: (message: string) => void;
  onDraftsChanged: () => void;
  onTrackJob: (jobId: string) => void;
  onError: (error: unknown) => void;
}) {
  const [keyword, setKeyword] = useState("");
  const [contentDirection, setContentDirection] = useState<ContentDirection>("auto");
  const [useWebSearch, setUseWebSearch] = useState(true);
  const [inspiration, setInspiration] = useState<CopyMissionInspiration | null>(null);
  const [avoidTopics, setAvoidTopics] = useState<string[]>([]);
  const [customKnowledge, setCustomKnowledge] = useState("");
  const [selectedKnowledgeIds, setSelectedKnowledgeIds] = useState<string[]>([]);
  const [selectedAngles, setSelectedAngles] = useState<string[]>([]);
  const [contentPlan, setContentPlan] = useState<ContentPlanDraft | null>(null);
  const [loading, setLoading] = useState(false);
  const [generating, setGenerating] = useState(false);

  const knowledgeCards = useMemo(() => buildInspirationKnowledgeCards(inspiration, customKnowledge), [inspiration, customKnowledge]);
  const selectedKnowledgeCards = knowledgeCards.filter((card) => selectedKnowledgeIds.includes(card.id));

  async function findInspiration() {
    if (!personaId) return;
    setLoading(true);
    onError(undefined);
    try {
      const data = await api.inspireCopyMission(personaId, {
        keyword: keyword.trim() || undefined,
        content_direction: directionText(contentDirection),
        use_web_search: useWebSearch,
        avoid_topics: avoidTopics
      });
      setInspiration(data);
      const cards = buildInspirationKnowledgeCards(data, customKnowledge);
      setSelectedKnowledgeIds(cards.slice(0, 6).map((card) => card.id));
      setSelectedAngles((data.angles || []).slice(0, 3));
      setContentPlan(buildContentPlanDraft(data, contentDirection));
      setAvoidTopics((prev) => [...prev, data.label, data.seed_query].filter(Boolean).slice(-8));
    } catch (err) {
      onError(err);
    } finally {
      setLoading(false);
    }
  }

  async function generateDrafts() {
    if (!personaId || !inspiration || !contentPlan) return;
    setGenerating(true);
    onError(undefined);
    try {
      if (contentPlan.id) {
        const updated = await api.updateContentPlan(personaId, contentPlan.id, {
          topic: contentPlan.topic,
          mission: contentPlan.mission,
          target_audience: contentPlan.targetAudience,
          angle: contentPlan.angle,
          opening_type: contentPlan.openingType,
          body_type: contentPlan.bodyType,
          emotion: contentPlan.emotion,
          cta_type: contentPlan.ctaType,
          risk_level: contentPlan.riskLevel,
          requires_human_review: contentPlan.riskLevel !== "low",
          avoid: contentPlan.avoidText.split("\n").map((line) => line.trim()).filter(Boolean),
          selected_knowledge: selectedKnowledgeCards.map((card) => `${card.title}：${card.detail}`),
          status: "drafting"
        });
        setContentPlan((current) => current ? { ...current, id: updated.id } : current);
      }
      const data = await api.startTopicMatrixJob(personaId, {
        topic: contentPlan.topic || inspiration.label,
        content_plan_id: contentPlan.id || inspiration.content_plan_id || undefined,
        brief: buildContentPlanBrief({ inspiration, contentPlan, contentDirection, selectedAngles, selectedKnowledgeCards, customKnowledge }),
        use_web_search: useWebSearch,
        draft_count: 3
      });
      notifyJobCreated({ jobId: data.job_id, label: "話題矩陣", templateType: "generate-topic-matrix" });
      onTrackJob(data.job_id);
      onMessage(data.message || `已在背景產生 3 篇草稿（${data.ai_provider} / ${data.ai_model}）`);
    } catch (err) {
      onError(err);
    } finally {
      setGenerating(false);
    }
  }

  return (
    <Card title="話題雷達" tone="mint">
      <div className="page-grid">
        <div className="grid-3" style={{ gap: "0.65rem" }}>
          <StepPill index={1} title="找話題" desc="社群熱度 + 人設適配" active />
          <StepPill index={2} title="內容計畫" desc="任務、切角、公式" active={!!contentPlan} />
          <StepPill index={3} title="草稿與審核" desc="3 版 + 品質閘門" active={false} />
        </div>
        <Field label="想經營的大方向" hint="可以很模糊。留空也會依人設找一個可延伸題。">
          <Input
            value={keyword}
            onChange={(event) => { setKeyword(event.target.value); setAvoidTopics([]); setInspiration(null); setContentPlan(null); }}
            placeholder="例如：小品牌起號、AI 工具、自由工作者"
          />
        </Field>
        <Field label="這次想發哪一種方向" hint="先決定任務，再找話題與切角，最後才產文。">
          <ContentDirectionPicker value={contentDirection} onChange={(next) => { setContentDirection(next); setInspiration(null); setSelectedAngles([]); setContentPlan(null); }} />
        </Field>
        <Field label="我想知道或補查的知識（選填）" hint="一行一個問題。會變成可勾選知識卡，產文時也會一起上網補資料。">
          <Textarea
            value={customKnowledge}
            onChange={(event) => setCustomKnowledge(event.target.value)}
            rows={4}
            placeholder={"例如：\n試管前 AMH 怎麼看？\n第一次生殖門診要準備什麼？"}
          />
        </Field>
        <label className="button-row" style={{ justifyContent: "flex-start" }}>
          <input type="checkbox" checked={useWebSearch} onChange={(event) => setUseWebSearch(event.target.checked)} />
          <span className="muted hx-text-sm">上網找近期素材</span>
        </label>
        <Button variant="primary" loading={loading} disabled={!personaId} onClick={() => void findInspiration()}>
          掃描話題並產內容計畫
        </Button>

        {inspiration ? (
          <div className="mission-draft-card">
            <div className="mission-draft-copy">
              <div className="button-row" style={{ flexWrap: "wrap" }}>
                <Badge variant="brand">候選話題</Badge>
                {inspiration.web_search_used ? <Badge variant="sky">有外部素材</Badge> : <Badge variant="neutral">人設推題</Badge>}
                {contentPlan?.riskLevel ? <Badge variant={contentPlan.riskLevel === "high" ? "danger" : contentPlan.riskLevel === "medium" ? "warning" : "success"}>風險 {contentPlan.riskLevel}</Badge> : null}
              </div>
              <strong>{inspiration.label}</strong>
              <p className="muted hx-text-sm" style={{ margin: "0.5rem 0 0" }}>{inspiration.brief}</p>
              {inspiration.trend_reason ? <p className="muted hx-text-sm">{inspiration.trend_reason}</p> : null}
              {inspiration.trend_keywords?.length ? (
                <div className="button-row" style={{ flexWrap: "wrap" }}>
                  {inspiration.trend_keywords.slice(0, 6).map((item) => <Badge key={item} variant="neutral">{item}</Badge>)}
                </div>
              ) : null}
              {inspiration.angles?.length ? (
                <div className="page-grid" style={{ gap: "0.65rem", marginTop: "0.75rem" }}>
                  <strong className="hx-text-sm">方向切角（勾選後才帶入產文）</strong>
                  <div className="grid-2" style={{ gap: "0.65rem" }}>
                    {inspiration.angles.map((angle) => (
                      <label key={angle} className="card" style={{ cursor: "pointer", padding: "0.8rem" }}>
                        <div className="button-row" style={{ alignItems: "flex-start", flexWrap: "nowrap" }}>
                          <input
                            type="checkbox"
                            checked={selectedAngles.includes(angle)}
                            onChange={(event) => {
                              const checked = event.target.checked;
                              setSelectedAngles((current) => checked ? [...new Set([...current, angle])] : current.filter((item) => item !== angle));
                            }}
                          />
                          <span>{angle}</span>
                        </div>
                      </label>
                    ))}
                  </div>
                </div>
              ) : null}
              {inspiration.sources?.length ? (
                <div className="page-grid" style={{ gap: "0.5rem" }}>
                  {inspiration.sources.slice(0, 3).map((source, index) => (
                    <p key={`${source.url || source.title || index}`} className="muted hx-text-sm" style={{ margin: 0 }}>
                      {source.title || source.query || "參考素材"}：{shortText(source.snippet || source.url || "", 90)}
                    </p>
                  ))}
                </div>
              ) : null}
              {contentPlan ? (
                <ContentPlanEditor value={contentPlan} onChange={setContentPlan} />
              ) : null}
              {knowledgeCards.length ? (
                <div className="page-grid" style={{ gap: "0.65rem", marginTop: "0.75rem" }}>
                  <div className="button-row" style={{ justifyContent: "space-between", flexWrap: "wrap" }}>
                    <strong className="hx-text-sm">輕量知識圖譜（勾選後才帶入產文）</strong>
                    <div className="button-row">
                      <Button variant="soft" onClick={() => setSelectedKnowledgeIds(knowledgeCards.map((card) => card.id))}>全選</Button>
                      <Button variant="soft" onClick={() => setSelectedKnowledgeIds([])}>清空</Button>
                    </div>
                  </div>
                  <div className="grid-2" style={{ gap: "0.65rem" }}>
                    {knowledgeCards.map((card) => (
                      <label key={card.id} className="card" style={{ cursor: "pointer", padding: "0.8rem" }}>
                        <div className="button-row" style={{ alignItems: "flex-start", flexWrap: "nowrap" }}>
                          <input
                            type="checkbox"
                            checked={selectedKnowledgeIds.includes(card.id)}
                            onChange={(event) => {
                              const checked = event.target.checked;
                              setSelectedKnowledgeIds((current) => checked ? [...new Set([...current, card.id])] : current.filter((id) => id !== card.id));
                            }}
                          />
                          <div>
                            <strong>{card.title}</strong>
                            <p className="muted hx-text-sm" style={{ margin: "0.35rem 0 0" }}>{shortText(card.detail, 140)}</p>
                            {card.source ? <p className="subtle hx-text-xs" style={{ margin: "0.25rem 0 0" }}>{card.source}</p> : null}
                          </div>
                        </div>
                      </label>
                    ))}
                  </div>
                </div>
              ) : null}
              <div className="button-row" style={{ flexWrap: "wrap" }}>
                <Button variant="primary" loading={generating} disabled={!contentPlan} onClick={() => void generateDrafts()}>產生 3 版草稿並做品質檢查</Button>
                <Button variant="soft" disabled={loading} onClick={() => void findInspiration()}>換一個話題</Button>
              </div>
            </div>
          </div>
        ) : null}
      </div>
    </Card>
  );
}

function StepPill({ index, title, desc, active }: { index: number; title: string; desc: string; active: boolean }) {
  return (
    <div className="card" style={{ padding: "0.85rem", borderColor: active ? "var(--hx-brand)" : undefined }}>
      <div className="button-row" style={{ justifyContent: "flex-start" }}>
        <Badge variant={active ? "brand" : "neutral"}>{index}</Badge>
        <strong>{title}</strong>
      </div>
      <p className="muted hx-text-sm" style={{ margin: "0.35rem 0 0" }}>{desc}</p>
    </div>
  );
}

function ContentPlanEditor({ value, onChange }: { value: ContentPlanDraft; onChange: (value: ContentPlanDraft) => void }) {
  const update = (key: keyof ContentPlanDraft, next: string) => onChange({ ...value, [key]: next });
  return (
    <div className="page-grid" style={{ gap: "0.75rem", marginTop: "0.75rem" }}>
      <div className="button-row" style={{ justifyContent: "space-between", flexWrap: "wrap" }}>
        <strong>Content Plan（產文前可修改）</strong>
        <Badge variant={value.riskLevel === "high" ? "danger" : value.riskLevel === "medium" ? "warning" : "success"}>人工審核：{value.riskLevel === "low" ? "建議抽查" : "必要"}</Badge>
      </div>
      <div className="grid-2">
        <Field label="主題">
          <Input value={value.topic} onChange={(event) => update("topic", event.target.value)} />
        </Field>
        <Field label="內容任務">
          <Input value={value.mission} onChange={(event) => update("mission", event.target.value)} placeholder="互動文、收藏文、信任文..." />
        </Field>
      </div>
      <Field label="打中誰">
        <Input value={value.targetAudience} onChange={(event) => update("targetAudience", event.target.value)} placeholder="越具體越好" />
      </Field>
      <Field label="核心切角">
        <Textarea value={value.angle} onChange={(event) => update("angle", event.target.value)} rows={2} />
      </Field>
      <div className="grid-2">
        <Field label="開頭公式">
          <Input value={value.openingType} onChange={(event) => update("openingType", event.target.value)} />
        </Field>
        <Field label="中段結構">
          <Input value={value.bodyType} onChange={(event) => update("bodyType", event.target.value)} />
        </Field>
      </div>
      <div className="grid-2">
        <Field label="情緒槓桿">
          <Input value={value.emotion} onChange={(event) => update("emotion", event.target.value)} />
        </Field>
        <Field label="CTA 類型">
          <Input value={value.ctaType} onChange={(event) => update("ctaType", event.target.value)} />
        </Field>
      </div>
      <div className="grid-2">
        <Field label="風險等級">
          <Select value={value.riskLevel || "medium"} onChange={(event) => update("riskLevel", event.target.value)}>
            <option value="low">low</option>
            <option value="medium">medium</option>
            <option value="high">high</option>
          </Select>
        </Field>
        <Field label="禁忌 / 不要寫偏">
          <Textarea value={value.avoidText} onChange={(event) => update("avoidText", event.target.value)} rows={3} placeholder="一行一個禁忌" />
        </Field>
      </div>
    </div>
  );
}

function buildContentPlanDraft(inspiration: CopyMissionInspiration, direction: ContentDirection): ContentPlanDraft {
  return {
    id: inspiration.content_plan_id,
    topicCandidateId: inspiration.topic_candidate_id,
    topic: inspiration.label || inspiration.seed_query || "",
    mission: inspiration.mission || directionText(direction).split("：")[0] || "互動文",
    targetAudience: inspiration.target_audience || "這個帳號最想服務的讀者",
    angle: inspiration.angles?.[0] || inspiration.brief || "",
    openingType: inspiration.opening_type || "依人設選擇自然開場",
    bodyType: inspiration.body_type || "先共鳴再整理",
    emotion: inspiration.emotion || "被理解感",
    ctaType: inspiration.cta_type || "輕提問",
    riskLevel: inspiration.risk_level || "medium",
    avoidText: (inspiration.avoid || ["不要太像 AI", "不要照搬競品", "不要寫成制式模板"]).join("\n")
  };
}

function buildContentPlanBrief({
  inspiration,
  contentPlan,
  contentDirection,
  selectedAngles,
  selectedKnowledgeCards,
  customKnowledge
}: {
  inspiration: CopyMissionInspiration;
  contentPlan: ContentPlanDraft;
  contentDirection: ContentDirection;
  selectedAngles: string[];
  selectedKnowledgeCards: InspirationKnowledgeCard[];
  customKnowledge: string;
}) {
  const parts = [
    "【Content Plan】",
    `主題：${contentPlan.topic || inspiration.label}`,
    `內容任務：${contentPlan.mission}`,
    `目標受眾：${contentPlan.targetAudience}`,
    `核心切角：${contentPlan.angle}`,
    `開頭方式：${contentPlan.openingType}`,
    `中段方式：${contentPlan.bodyType}`,
    `情緒槓桿：${contentPlan.emotion}`,
    `CTA 類型：${contentPlan.ctaType}`,
    `風險等級：${contentPlan.riskLevel}`,
    `使用者選擇方向：${directionText(contentDirection)}`,
    "",
    "【Topic Radar】",
    inspiration.brief,
    inspiration.trend_reason ? `選題理由：${inspiration.trend_reason}` : "",
    inspiration.trend_keywords?.length ? `可延伸關鍵字：${inspiration.trend_keywords.join("、")}` : "",
    selectedAngles.length ? `已選切角：\n${selectedAngles.map((angle) => `- ${angle}`).join("\n")}` : "",
    selectedKnowledgeCards.length ? `已勾選知識：\n${selectedKnowledgeCards.map((card) => `- ${card.title}：${card.detail}`).join("\n")}` : "",
    customKnowledge.trim() ? `使用者補充/待查：\n${customKnowledge.trim()}` : "",
    contentPlan.avoidText.trim() ? `禁止：\n${contentPlan.avoidText.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => `- ${line}`).join("\n")}` : "",
    "",
    "【產文要求】",
    "請產 3 版：A 穩定帳號風格、B 更有共鳴、C 更容易互動。",
    "每版都要像這個人設會自然發出的 Threads，不要像企劃文件。",
    "每版 rationale 必須含 AI 感、公式感、人設符合度、風險、互動潛力與審核建議。"
  ].filter(Boolean);
  return parts.join("\n");
}

function buildInspirationKnowledgeCards(inspiration: CopyMissionInspiration | null, customKnowledge: string): InspirationKnowledgeCard[] {
  const cards: InspirationKnowledgeCard[] = [];
  const seen = new Set<string>();
  const add = (card: InspirationKnowledgeCard) => {
    const key = `${card.title}\n${card.detail}`.trim();
    if (!key || seen.has(key)) return;
    seen.add(key);
    cards.push(card);
  };
  inspiration?.trend_keywords?.forEach((keyword, index) => {
    add({ id: `keyword:${index}:${keyword}`, title: keyword, detail: `以「${keyword}」延伸受眾痛點、常見問題與貼文角度。`, source: "AI 推薦關鍵字" });
  });
  inspiration?.sources?.forEach((source, index) => {
    add({
      id: `source:${index}:${source.url || source.title || source.query}`,
      title: source.title || source.query || `參考資料 ${index + 1}`,
      detail: source.snippet || source.url || "外部參考資料",
      source: source.url || source.query || "外部搜尋"
    });
  });
  customKnowledge.split("\n").map((line) => line.trim()).filter(Boolean).forEach((line, index) => {
    add({ id: `custom:${index}:${line}`, title: line, detail: "使用者指定想知道的知識，產文時需先查清楚再用人設語氣說明。", source: "手動補充" });
  });
  return cards.slice(0, 12);
}

function RewritePostCard({
  accountId,
  personaId,
  onMessage,
  onDraftsChanged,
  onTrackJob,
  onError
}: {
  accountId: string;
  personaId: string;
  onMessage: (message: string) => void;
  onDraftsChanged: () => void;
  onTrackJob: (jobId: string) => void;
  onError: (error: unknown) => void;
}) {
  const [referenceText, setReferenceText] = useState("");
  const [topic, setTopic] = useState("");
  const [brief, setBrief] = useState("");
  const [useWebSearch, setUseWebSearch] = useState(true);
  const [generating, setGenerating] = useState(false);

  async function generateSyncFallback() {
    const formulaData = await api.analyzeContentFormula(accountId, {
      persona_id: personaId,
      source_type: "paste",
      post_text: referenceText.trim(),
      save_label: `貼文改寫 ${new Date().toLocaleDateString("zh-TW")}`
    });
    if (!formulaData.formula?.id) throw new Error("無法建立參考寫法，請換一篇參考貼文再試。");
    const draftData = await api.generateFromFormula(personaId, formulaData.formula.id, {
      account_id: accountId,
      topic: topic.trim(),
      brief: brief.trim() || undefined,
      use_web_search: useWebSearch,
      draft_count: 1
    });
    setReferenceText("");
    setTopic("");
    setBrief("");
    onMessage(draftData.message || `已產生 ${draftData.list?.length || 1} 篇草稿`);
    onDraftsChanged();
  }

  async function generate() {
    if (!accountId || !personaId || !referenceText.trim() || !topic.trim()) return;
    setGenerating(true);
    onError(undefined);
    try {
      const draftData = await api.startRewriteDraftJob(personaId, {
        account_id: accountId,
        reference_text: referenceText.trim(),
        topic: topic.trim(),
        brief: brief.trim() || undefined,
        save_label: `貼文改寫 ${new Date().toLocaleDateString("zh-TW")}`,
        use_web_search: useWebSearch,
        draft_count: 1
      });
      notifyJobCreated({ jobId: draftData.job_id, label: "貼文改寫", templateType: "generate-rewrite-draft" });
      onTrackJob(draftData.job_id);
      setReferenceText("");
      setTopic("");
      setBrief("");
      onMessage(draftData.message || `改寫已在背景執行（${draftData.ai_provider} / ${draftData.ai_model}）`);
    } catch (err) {
      try {
        await generateSyncFallback();
      } catch (fallbackErr) {
        onError(fallbackErr ?? err);
      }
    } finally {
      setGenerating(false);
    }
  }

  return (
    <Card title="貼一篇改寫" tone="sky">
      <div className="page-grid">
        <Field label="參考貼文">
          <Textarea value={referenceText} onChange={(event) => setReferenceText(event.target.value)} rows={7} placeholder="貼上你想借鑑節奏、角度或口吻的貼文。" />
        </Field>
        <Field label="你要改成什麼話題">
          <Input value={topic} onChange={(event) => setTopic(event.target.value)} placeholder="例如：小品牌怎麼用 AI 找客戶" />
        </Field>
        <Field label="補充限制（可選）">
          <Textarea value={brief} onChange={(event) => setBrief(event.target.value)} rows={3} placeholder="想加入的產品、觀點、禁忌、不要太業配等。" />
        </Field>
        <label className="button-row" style={{ justifyContent: "flex-start" }}>
          <input type="checkbox" checked={useWebSearch} onChange={(event) => setUseWebSearch(event.target.checked)} />
          <span className="muted hx-text-sm">需要時補一點網路資料</span>
        </label>
        <Button variant="primary" loading={generating} disabled={!accountId || !personaId || !referenceText.trim() || !topic.trim()} onClick={() => void generate()}>
          改寫這篇
        </Button>
      </div>
    </Card>
  );
}

function TopicMatrixCard({
  personaId,
  onMessage,
  onTrackJob,
  onError
}: {
  personaId: string;
  onMessage: (message: string) => void;
  onTrackJob: (jobId: string) => void;
  onError: (error: unknown) => void;
}) {
  const [topic, setTopic] = useState("");
  const [brief, setBrief] = useState("");
  const [contentDirection, setContentDirection] = useState<ContentDirection>("auto");
  const [count, setCount] = useState(5);
  const [generating, setGenerating] = useState(false);

  async function generate() {
    if (!personaId || !topic.trim()) return;
    setGenerating(true);
    onError(undefined);
    try {
      const data = await api.startTopicMatrixJob(personaId, {
        topic: topic.trim(),
        brief: [`內容方向：${directionText(contentDirection)}`, brief.trim()].filter(Boolean).join("\n") || undefined,
        use_web_search: true,
        draft_count: count
      });
      notifyJobCreated({ jobId: data.job_id, label: "話題矩陣", templateType: "generate-topic-matrix" });
      onTrackJob(data.job_id);
      setTopic("");
      setBrief("");
      onMessage(data.message || `已在背景產生 ${count} 篇草稿（${data.ai_provider} / ${data.ai_model}）`);
    } catch (err) {
      onError(err);
    } finally {
      setGenerating(false);
    }
  }

  return (
    <Card title="話題矩陣" tone="lavender">
      <div className="page-grid">
        <Field label="話題">
          <Input value={topic} onChange={(event) => setTopic(event.target.value)} placeholder="例如：Threads 經營為什麼不能只追爆文" />
        </Field>
        <Field label="這次想發哪一種方向" hint="同一個主題，不同方向會產出不同切角與格式。">
          <ContentDirectionPicker value={contentDirection} onChange={setContentDirection} />
        </Field>
        <Field label="補充資料（可選）">
          <Textarea value={brief} onChange={(event) => setBrief(event.target.value)} rows={4} placeholder="想講的觀點、目標受眾、產品脈絡。AI 會盡量分成不同角度。" />
        </Field>
        <Field label="產生篇數">
          <Select value={String(count)} onChange={(event) => setCount(Number(event.target.value))}>
            <option value="3">3 篇</option>
            <option value="5">5 篇</option>
          </Select>
        </Field>
        <Button variant="primary" loading={generating} disabled={!personaId || !topic.trim()} onClick={() => void generate()}>
          產生矩陣草稿
        </Button>
      </div>
    </Card>
  );
}

function DraftBoard({
  personaId,
  accountId,
  refreshKey,
  onDone,
  onError
}: {
  personaId: string;
  accountId: string;
  refreshKey: number;
  onDone: (message: string) => void;
  onError: (error: unknown) => void;
}) {
  const [items, setItems] = useState<ContentInboxItem[]>([]);
  const [page, setPage] = useState(1);
  const [totalPages, setTotalPages] = useState(1);
  const [status, setStatus] = useState("draft");
  const [loading, setLoading] = useState(false);
  const [editingId, setEditingId] = useState("");
  const [editText, setEditText] = useState("");
  const [topicTags, setTopicTags] = useState<Record<string, string>>({});
  const [scheduleAt, setScheduleAt] = useState<Record<string, string>>({});
  const [reviewNotes, setReviewNotes] = useState<Record<string, string>>({});
  const [selectedDraftIds, setSelectedDraftIds] = useState<string[]>([]);

  async function load(nextPage = page) {
    if (!personaId) return;
    setLoading(true);
    onError(undefined);
    try {
      const data = await api.contentInbox(personaId, {
        page: nextPage,
        pageSize: 10,
        status: status === "all" ? undefined : status
      });
      setItems(data.list || []);
      setSelectedDraftIds([]);
      setPage(Number(data.pagination?.page || nextPage));
      setTotalPages(Number(data.pagination?.totalPages || 1));
    } catch (err) {
      onError(err);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    void load(1);
  }, [personaId, status, refreshKey]);

  async function saveEdit(item: ContentInboxItem) {
    try {
      onError(undefined);
      await api.updateDraft(personaId, item.id, {
        text: editText.trim(),
        topic_tag: normalizeTopicTag(topicTags[item.id] ?? item.topic_tag ?? "")
      });
      setEditingId("");
      onDone("草稿已更新。 ");
      await load(page);
    } catch (err) {
      onError(err);
    }
  }

  async function scheduleItem(item: ContentInboxItem) {
    if (!accountId || item.lifecycle !== "draft") return;
    try {
      onError(undefined);
      await api.scheduleCopyDraft(personaId, item.id, {
        account_id: accountId,
        topic_tag: normalizeTopicTag(topicTags[item.id] ?? item.topic_tag ?? ""),
        scheduled_at: inputDateTimeToNano(scheduleAt[item.id] || "") || Date.now() * 1_000_000
      });
      onDone("草稿已排程。 ");
      await load(page);
    } catch (err) {
      onError(err);
    }
  }

  async function deleteItem(item: ContentInboxItem) {
    if (item.lifecycle !== "draft") return;
    if (!window.confirm("確定刪除這篇草稿嗎？")) return;
    try {
      onError(undefined);
      await api.deleteDraft(personaId, item.id);
      onDone("草稿已刪除。 ");
      await load(page);
    } catch (err) {
      onError(err);
    }
  }

  async function reviewItem(item: ContentInboxItem, decision: "approve" | "reject") {
    if (item.lifecycle !== "draft") return;
    try {
      onError(undefined);
      await api.updateDraft(personaId, item.id, {
        status: decision === "approve" ? "ready" : "rejected",
        rationale: appendReviewNote(item.rationale, decision, reviewNotes[item.id] || "")
      });
      await api.createFeedbackEvent(personaId, {
        content_plan_id: item.content_plan_id || undefined,
        draft_id: item.id,
        decision: decision === "approve" ? "approve" : "reject",
        note: reviewNotes[item.id] || undefined,
        snapshot: item.text
      });
      setReviewNotes((prev) => ({ ...prev, [item.id]: "" }));
      onDone(decision === "approve" ? "草稿已核准，系統已記住人工回饋。" : "草稿已退回，系統已記住人工回饋。 ");
      await load(page);
    } catch (err) {
      onError(err);
    }
  }

  async function batchDeleteSelected() {
    const ids = selectedDraftIds.filter((id) => items.some((item) => item.id === id && item.lifecycle === "draft"));
    if (!ids.length) return;
    if (!window.confirm(`確定刪除 ${ids.length} 篇草稿嗎？只會刪除目前頁面中選取的草稿。`)) return;
    try {
      onError(undefined);
      for (const id of ids) {
        await api.deleteDraft(personaId, id);
      }
      setSelectedDraftIds([]);
      onDone(`已刪除 ${ids.length} 篇草稿。`);
      await load(page);
    } catch (err) {
      onError(err);
    }
  }

  async function pruneOldDrafts() {
    if (!personaId) return;
    if (!window.confirm("會刪除多餘的舊草稿，只保留最近 40 篇未排程草稿。確定繼續？")) return;
    try {
      onError(undefined);
      const data = await api.pruneCopyDrafts(personaId, { keep: 40 });
      onDone(data.message || "已整理草稿列表。");
      await load(1);
    } catch (err) {
      onError(err);
    }
  }

  const draftItems = items.filter((item) => item.lifecycle === "draft");
  const selectedCount = selectedDraftIds.length;
  const allDraftsSelected = draftItems.length > 0 && draftItems.every((item) => selectedDraftIds.includes(item.id));

  function toggleDraftSelection(id: string, checked: boolean) {
    setSelectedDraftIds((current) => {
      if (checked) return current.includes(id) ? current : [...current, id];
      return current.filter((item) => item !== id);
    });
  }

  function toggleAllDrafts(checked: boolean) {
    setSelectedDraftIds(checked ? draftItems.map((item) => item.id) : []);
  }

  return (
    <Card
      title="草稿區"
      action={(
        <div className="button-row">
          <Button variant="soft" disabled={loading || !personaId} onClick={() => void pruneOldDrafts()}>清除舊草稿</Button>
          <Button loading={loading} onClick={() => void load(page)}>重新載入草稿</Button>
        </div>
      )}
    >
      <div className="page-grid">
        <div className="button-row" style={{ justifyContent: "space-between", flexWrap: "wrap" }}>
          <p className="muted" style={{ margin: 0 }}>依建立時間由新到舊排序。產完任務後會自動刷新；也可手動重新載入。</p>
          <Select value={status} onChange={(event) => setStatus(event.target.value)} style={{ maxWidth: "12rem" }}>
            <option value="draft">只看草稿</option>
            <option value="scheduled">已排程</option>
            <option value="published">已發布</option>
            <option value="all">全部</option>
          </Select>
        </div>

        {draftItems.length > 0 ? (
          <div className="button-row" style={{ justifyContent: "space-between", flexWrap: "wrap" }}>
            <label className="button-row" style={{ justifyContent: "flex-start" }}>
              <input type="checkbox" checked={allDraftsSelected} onChange={(event) => toggleAllDrafts(event.target.checked)} />
              <span className="muted hx-text-sm">選取本頁草稿</span>
            </label>
            <Button variant="danger" disabled={!selectedCount} onClick={() => void batchDeleteSelected()}>
              批次刪除{selectedCount ? `（${selectedCount}）` : ""}
            </Button>
          </div>
        ) : null}

        {items.map((item) => (
          <div key={item.id} className="mission-draft-card">
            <div className="mission-draft-copy">
              <div className="button-row" style={{ justifyContent: "space-between", flexWrap: "wrap" }}>
                <div className="button-row">
                  {item.lifecycle === "draft" ? (
                    <input
                      type="checkbox"
                      checked={selectedDraftIds.includes(item.id)}
                      onChange={(event) => toggleDraftSelection(item.id, event.target.checked)}
                      aria-label="選取草稿"
                    />
                  ) : null}
                  {lifecycleBadge(item.lifecycle)}
                  {item.lifecycle === "draft" ? reviewBadge(item.status) : null}
                  {item.review_suggestion ? <Badge variant={item.review_suggestion === "可通過" ? "success" : item.review_suggestion === "建議重寫" ? "danger" : "warning"}>{item.review_suggestion}</Badge> : null}
                  {(topicTags[item.id] ?? item.topic_tag) ? <Badge variant="sky">{topicTags[item.id] ?? item.topic_tag}</Badge> : null}
                  {item.angle || item.draft_type ? <Badge variant="brand">{item.angle || item.draft_type}</Badge> : null}
                </div>
                <span className="muted hx-text-sm">
                  {item.create_at ? `建立 ${formatNano(item.create_at)}` : null}
                  {item.scheduled_at ? ` · 排程 ${formatNano(item.scheduled_at)}` : null}
                </span>
              </div>
              {editingId === item.id ? (
                <Textarea value={editText} onChange={(event) => setEditText(event.target.value)} rows={6} />
              ) : (
                <strong>{item.text}</strong>
              )}
              {item.rationale ? (
                <div className="card" style={{ padding: "0.75rem", marginTop: "0.75rem" }}>
                  <strong className="hx-text-sm">品質閘門 / 回饋記錄</strong>
                  <QualityScoreRow item={item} />
                  <p className="muted hx-text-sm" style={{ whiteSpace: "pre-wrap", margin: "0.4rem 0 0" }}>{shortText(item.rationale, 520)}</p>
                </div>
              ) : null}
              {item.published_permalink ? <a href={item.published_permalink} target="_blank" rel="noreferrer" className="muted">已發布連結</a> : null}
              <div className="button-row" style={{ flexWrap: "wrap" }}>
                {editingId === item.id ? (
                  <>
                    <Button variant="primary" disabled={!editText.trim()} onClick={() => void saveEdit(item)}>儲存</Button>
                    <Button variant="soft" onClick={() => setEditingId("")}>取消</Button>
                  </>
                ) : item.lifecycle === "draft" ? (
                  <Button variant="soft" onClick={() => { setEditingId(item.id); setEditText(item.text); }}>編輯</Button>
                ) : null}
                {item.lifecycle === "draft" ? <Button variant="danger" onClick={() => void deleteItem(item)}>刪除</Button> : null}
              </div>
            </div>
            {item.lifecycle === "draft" ? (
              <div className="mission-draft-schedule mission-draft-schedule--tagged">
                <Field label="人工審核回饋">
                  <Textarea
                    value={reviewNotes[item.id] || ""}
                    onChange={(event) => setReviewNotes((prev) => ({ ...prev, [item.id]: event.target.value }))}
                    rows={3}
                    placeholder="例如：這版太像教學文、開頭可以更口語、這版可留。"
                  />
                </Field>
                <div className="button-row" style={{ flexWrap: "wrap" }}>
                  <Button variant="primary" onClick={() => void reviewItem(item, "approve")}>核准草稿</Button>
                  <Button variant="danger" onClick={() => void reviewItem(item, "reject")}>退回重寫</Button>
                </div>
                <Field label="標籤">
                  <Input value={topicTags[item.id] ?? item.topic_tag ?? ""} onChange={(event) => setTopicTags((prev) => ({ ...prev, [item.id]: event.target.value }))} />
                </Field>
                <Field label="排程時間（空白就是盡快發）">
                  <Input
                    type="datetime-local"
                    value={scheduleAt[item.id] || nanoToInputDateTime(item.scheduled_at)}
                    onChange={(event) => setScheduleAt((prev) => ({ ...prev, [item.id]: event.target.value }))}
                  />
                </Field>
                <Button variant="primary" disabled={!accountId} onClick={() => void scheduleItem(item)}>排程</Button>
              </div>
            ) : null}
          </div>
        ))}

        {items.length === 0 && !loading ? <p className="muted">目前沒有內容。先用上面的三個入口產生或排程一篇。</p> : null}

        <div className="button-row" style={{ justifyContent: "center" }}>
          <Button disabled={page <= 1} onClick={() => void load(page - 1)}>上一頁</Button>
          <span className="muted">{page} / {totalPages}</span>
          <Button disabled={page >= totalPages} onClick={() => void load(page + 1)}>下一頁</Button>
        </div>
      </div>
    </Card>
  );
}
