import { useEffect, useEffectEvent, useMemo, useRef, useState } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { ImageAttach } from "../../components/studio/ImageAttach";
import { Badge, Button, Card, EmptyState, Input, Select, Textarea } from "../../components/ui";
import { useData, useRepos } from "../../data/DataContext";
import { useJobLive } from "../../data/JobLiveContext";
import { speakersOf, validateUnderPostScheme } from "../../domain/speakers";
import type {
  Brand,
  ExternalThreadTarget,
  OwnPost,
  Persona,
  PlayStep,
  ThreadPlay,
  ThreadsAccount,
} from "../../domain/types";
import type { AttachedImage } from "../../lib/attachImage";
import { allowHttpUrl } from "../../lib/externalUrl";
import { newId } from "../../lib/id";

import { isPersonaReady, personaOptionLabel } from "../../lib/personaPrompt";
import { formatLocalDateTime, nowUnixNano } from "../../lib/time";
import { useI18n } from "../../i18n/I18nContext";

function stepToImages(
  step: PlayStep,
  t: (k: string, p?: Record<string, string | number>) => string,
): AttachedImage[] {
  return (step.image_urls || []).map((url, i) => ({
    id: `${step.id}_img_${i}`,
    url,
    name: t("image.named", { n: i + 1 }),
  }));
}

type Props = {
  accountId?: string;
  personaId?: string;
  personas?: Persona[];
};

type TargetMode = "own" | "link";

type TargetContext = {
  text: string;
  /** own 貼的帳號；external 無 */
  ownerAccountId?: string;
  permalink?: string;
};

function emptySchemeOwn(
  post: OwnPost,
  castIds: string[],
  t: (k: string, p?: Record<string, string | number>) => string,
): ThreadPlay {
  const now = nowUnixNano();
  const snippet = `${post.text.slice(0, 32)}${post.text.length > 32 ? "…" : ""}`;
  return {
    id: newId("play"),
    title: t("plays.defaultTitle"),
    topic: t("plays.topicOnPost", { snippet }),
    status: "draft",
    lead_account_id: post.account_id,
    cast_account_ids: castIds.filter((id) => id !== post.account_id),
    target_own_post_id: post.id,
    target_external: null,
    interval_minutes: 5,
    steps: [
      {
        id: newId("step"),
        sort_order: 0,
        kind: "reply",
        account_id: castIds.find((id) => id !== post.account_id) || post.account_id,
        text: "",
        // 第一則 delay 不影響：送出後立刻排程；後續步用 interval
        delay_from_previous_sec: 0,
        persona_id: null,
        brand_id: null,
      },
    ],
    // 預設立刻發第一則（不要預設 +1 小時）
    schedule_start_at: now,
    created_at: now,
    updated_at: now,
  };
}

function emptySchemeExternal(
  target: ExternalThreadTarget,
  usableIds: string[],
  t: (k: string, p?: Record<string, string | number>) => string,
): ThreadPlay {
  const now = nowUnixNano();
  const lead = usableIds[0] || "";
  const cast = usableIds.slice(1);
  const label = target.author_username
    ? `@${target.author_username}`
    : target.shortcode || t("plays.externalFallback");
  return {
    id: newId("play"),
    title: t("plays.defaultTitle"),
    topic: t("plays.topicOnExternal", {
      label,
      snippet: (target.text_preview || target.url).slice(0, 28),
    }),
    status: "draft",
    lead_account_id: lead,
    cast_account_ids: cast,
    target_own_post_id: null,
    target_external: target,
    interval_minutes: 5,
    steps: [
      {
        id: newId("step"),
        sort_order: 0,
        kind: "reply",
        account_id: lead || cast[0] || "",
        text: "",
        delay_from_previous_sec: 0,
        persona_id: null,
        brand_id: null,
      },
    ],
    schedule_start_at: now,
    created_at: now,
    updated_at: now,
  };
}

export function PlaysPanel({ accountId, personas: personasProp = [] }: Props) {
  const repos = useRepos();
  const { refresh, tick } = useData();
  const { jobs, reload: reloadJobs } = useJobLive();
  const { t } = useI18n();
  const navigate = useNavigate();
  const [params] = useSearchParams();
  const postFromQuery = params.get("post") || "";

  const [posts, setPosts] = useState<OwnPost[]>([]);
  const [accounts, setAccounts] = useState<ThreadsAccount[]>([]);
  const [personas, setPersonas] = useState<Persona[]>(personasProp);
  const [brands, setBrands] = useState<Brand[]>([]);
  const [targetMode, setTargetMode] = useState<TargetMode>("own");
  const [postId, setPostId] = useState("");
  const [linkInput, setLinkInput] = useState("");
  const [external, setExternal] = useState<ExternalThreadTarget | null>(null);
  const [schemes, setSchemes] = useState<ThreadPlay[]>([]);
  const [editing, setEditing] = useState<ThreadPlay | null>(null);
  const [busy, setBusy] = useState("");
  const [busyStepId, setBusyStepId] = useState("");
  const [message, setMessage] = useState("");
  const [error, setError] = useState("");
  /** 背景「一次產全文」job */
  const [scriptJobId, setScriptJobId] = useState(() => {
    try {
      return sessionStorage.getItem("harbor.play.scriptJobId") || "";
    } catch {
      return "";
    }
  });
  const appliedScriptJob = useRef("");

  function trackScriptJob(id: string) {
    setScriptJobId(id);
    try {
      if (id) sessionStorage.setItem("harbor.play.scriptJobId", id);
      else sessionStorage.removeItem("harbor.play.scriptJobId");
    } catch {
      /* ignore */
    }
  }

  useEffect(() => {
    void (async () => {
      const [acc, allPosts, personaList, brandList] = await Promise.all([
        repos.accounts.list(),
        repos.ownPosts.list(),
        repos.personas.list(),
        repos.scout.listBrands(),
      ]);
      setAccounts(acc.filter((a) => a.is_usable));
      setPersonas(personaList);
      setBrands(brandList);
      const filtered = accountId ? allPosts.filter((p) => p.account_id === accountId) : allPosts;
      setPosts(filtered);
      setPostId((cur) => {
        if (postFromQuery && filtered.some((p) => p.id === postFromQuery)) return postFromQuery;
        if (cur && filtered.some((p) => p.id === cur)) return cur;
        return filtered[0]?.id || "";
      });
      if (postFromQuery && filtered.some((p) => p.id === postFromQuery)) {
        setTargetMode("own");
      }
    })();
  }, [repos, tick, accountId, postFromQuery]);

  useEffect(() => {
    if (personasProp.length) setPersonas(personasProp);
  }, [personasProp]);

  useEffect(() => {
    void (async () => {
      if (targetMode === "own") {
        if (!postId) {
          setSchemes([]);
          return;
        }
        setSchemes(await repos.plays.listByPost(postId));
        return;
      }
      if (!external?.url) {
        setSchemes([]);
        return;
      }
      setSchemes(await repos.plays.listByExternalUrl(external.url));
    })();
  }, [targetMode, postId, external?.url, repos.plays, tick]);

  const selectedPost = useMemo(() => posts.find((p) => p.id === postId) || null, [posts, postId]);

  const poolAccounts = useMemo(() => accounts, [accounts]);

  const targetCtx: TargetContext | null = useMemo(() => {
    if (targetMode === "own" && selectedPost) {
      return {
        text: selectedPost.text,
        ownerAccountId: selectedPost.account_id,
        permalink: selectedPost.permalink,
      };
    }
    if (targetMode === "link" && external) {
      return {
        text: external.text_preview || external.url,
        permalink: external.url,
      };
    }
    return null;
  }, [targetMode, selectedPost, external]);

  const hasTarget = Boolean(targetCtx);

  async function reloadSchemes() {
    if (targetMode === "own" && postId) {
      setSchemes(await repos.plays.listByPost(postId));
    } else if (targetMode === "link" && external?.url) {
      setSchemes(await repos.plays.listByExternalUrl(external.url));
    }
  }

  const reloadSchemesAfterJob = useEffectEvent(reloadSchemes);

  function switchMode(mode: TargetMode) {
    setTargetMode(mode);
    setEditing(null);
    setMessage("");
    setError("");
  }

  async function resolveLink() {
    setBusy("resolve");
    setError("");
    setMessage("");
    setEditing(null);
    try {
      const target = await repos.plays.resolveExternalLink(linkInput);
      setExternal(target);
      setMessage(t("plays.resolved"));
    } catch (e) {
      setExternal(null);
      setError(e instanceof Error ? e.message : t("plays.resolveFail"));
    } finally {
      setBusy("");
    }
  }

  function startNew() {
    if (targetMode === "own") {
      if (!selectedPost) return;
      setEditing(emptySchemeOwn(selectedPost, poolAccounts.map((a) => a.id), t));
    } else {
      if (!external) return;
      setEditing(emptySchemeExternal(external, poolAccounts.map((a) => a.id), t));
    }
    setError("");
    setMessage("");
  }

  function startEdit(s: ThreadPlay) {
    setEditing({
      ...s,
      steps: s.steps.map((st, i) => ({
        ...st,
        sort_order: i,
        kind: "reply",
        persona_id: st.persona_id ?? null,
        brand_id: st.brand_id ?? null,
      })),
    });
    setError("");
    setMessage("");
  }

  function patchEditing(partial: Partial<ThreadPlay>) {
    setEditing((e) => (e ? { ...e, ...partial } : e));
  }

  function updateStep(id: string, partial: Partial<PlayStep>) {
    if (!editing) return;
    patchEditing({
      steps: editing.steps.map((s) => (s.id === id ? { ...s, ...partial } : s)),
    });
  }

  function addReply() {
    if (!editing || !targetCtx) return;
    const interval = (editing.interval_minutes || 5) * 60;
    const ownerId = targetCtx.ownerAccountId;
    const defaultAcc =
      editing.cast_account_ids[0] ||
      poolAccounts.find((a) => a.id !== ownerId)?.id ||
      editing.lead_account_id ||
      poolAccounts[0]?.id ||
      "";
    const prev = editing.steps[editing.steps.length - 1];
    patchEditing({
      steps: [
        ...editing.steps,
        {
          id: newId("step"),
          sort_order: editing.steps.length,
          kind: "reply",
          account_id: defaultAcc,
          text: "",
          delay_from_previous_sec: interval,
          persona_id: prev?.persona_id ?? null,
          brand_id: prev?.brand_id ?? null,
        },
      ],
    });
  }

  function removeStep(id: string) {
    if (!editing || editing.steps.length <= 1) return;
    patchEditing({
      steps: editing.steps.filter((s) => s.id !== id).map((s, i) => ({ ...s, sort_order: i })),
    });
  }

  function applyInterval() {
    if (!editing) return;
    const sec = Math.max(0, Math.floor((editing.interval_minutes || 5) * 60));
    patchEditing({
      steps: editing.steps.map((s, i) => ({
        ...s,
        sort_order: i,
        delay_from_previous_sec: i === 0 ? sec : sec,
      })),
    });
  }

  /** own：貼主帳鎖住；external：可自由勾選，lead = 第一個 */
  function toggleSpeaker(accId: string) {
    if (!editing) return;
    if (targetMode === "own" && targetCtx?.ownerAccountId === accId) return;

    const current = speakersOf(editing);
    const has = current.includes(accId);
    let next = has ? current.filter((id) => id !== accId) : [...current, accId];

    if (targetMode === "own" && targetCtx?.ownerAccountId) {
      // 貼主永遠在池
      if (!next.includes(targetCtx.ownerAccountId)) {
        next = [targetCtx.ownerAccountId, ...next];
      }
      patchEditing({
        lead_account_id: targetCtx.ownerAccountId,
        cast_account_ids: next.filter((id) => id !== targetCtx.ownerAccountId),
      });
      return;
    }

    patchEditing({
      lead_account_id: next[0] || "",
      cast_account_ids: next.slice(1),
    });
  }

  function resolveStepPersona(step: PlayStep): Persona | null {
    if (!step.persona_id) return null;
    const p = personas.find((x) => x.id === step.persona_id);
    return isPersonaReady(p) ? p! : null;
  }

  async function generateForStep(
    step: PlayStep,
    index: number,
    steps: PlayStep[],
    ctx: TargetContext,
    topic: string,
  ): Promise<string> {
    const persona = resolveStepPersona(step);
    if (!persona) {
      throw new Error(t("plays.needPersonaForStep"));
    }
    const prevText = index > 0 ? steps[index - 1]?.text : ctx.text;
    const speaker = poolAccounts.find((a) => a.id === step.account_id);
    return repos.plays.generateStep({
      personaId: persona.id,
      context: (prevText || ctx.text || topic || "").trim(),
      topic: topic || undefined,
      speakerLabel: speaker?.display_name || speaker?.username || t("plays.accountFallback"),
      isLead: Boolean(ctx.ownerAccountId && step.account_id === ctx.ownerAccountId),
      mode: "reply",
    });
  }

  async function aiFillStep(stepId: string) {
    if (!editing || !targetCtx) return;
    const index = editing.steps.findIndex((s) => s.id === stepId);
    const step = editing.steps[index];
    if (!step) return;
    setBusyStepId(stepId);
    setError("");
    setMessage("");
    try {
      const text = await generateForStep(step, index, editing.steps, targetCtx, editing.topic);
      if (!text) throw new Error(t("plays.aiEmptyResult"));
      updateStep(stepId, { text });
      setMessage(t("plays.aiStepDone"));
    } catch (e) {
      setError(e instanceof Error ? e.message : t("plays.aiFail"));
    } finally {
      setBusyStepId("");
    }
  }

  // 背景一次產全文完成 → 從 DB 重載 play，步驟會在正確位置
  useEffect(() => {
    if (!scriptJobId) return;
    const j = jobs.find((x) => x.id === scriptJobId);
    const finish = async (status: string, errMsg?: string) => {
      if (appliedScriptJob.current === scriptJobId) return;
      appliedScriptJob.current = scriptJobId;
      setBusy("");
      if (status === "succeeded") {
        const playId = j?.ref_id || editing?.id;
        if (playId) {
          const fresh = await repos.plays.get(playId);
          if (fresh) {
            setEditing(fresh);
            setMessage(t("plays.scriptJobDone"));
            void reloadSchemesAfterJob();
            refresh();
          } else {
            setMessage(t("plays.scriptJobDoneReload"));
          }
        }
      } else {
        setError(errMsg || t("plays.aiFail"));
      }
      trackScriptJob("");
    };
    if (!j) {
      void (async () => {
        const full = await repos.jobs.get(scriptJobId);
        if (!full) return;
        if (full.status === "succeeded" || full.status === "failed") {
          await finish(full.status, full.error || full.progress_summary);
        } else if (full.status === "running" || full.status === "queued" || full.status === "pending") {
          setBusy("ai-all");
          if (full.progress_summary) setMessage(full.progress_summary);
        }
      })();
      return;
    }
    if (j.status === "running" || j.status === "queued" || j.status === "pending") {
      setBusy("ai-all");
      if (j.progress_summary) setMessage(j.progress_summary);
      return;
    }
    if (j.status === "succeeded" || j.status === "failed") {
      void finish(j.status, j.error || j.progress_summary);
    }
  }, [jobs, scriptJobId, editing?.id, repos.jobs, repos.plays, refresh, t]);

  async function aiFillAllEmpty() {
    if (!editing || !targetCtx) return;
    // 至少要先存成後端 play，才能背景產文寫回
    if (!editing.id || editing.id.startsWith("tmp_") || editing.id.startsWith("local_")) {
      setError(t("plays.saveBeforeAi"));
      return;
    }
    const emptyN = editing.steps.filter((s) => !s.text.trim()).length;
    if (emptyN === 0) {
      setError(t("plays.aiNoneFilled"));
      return;
    }
    setBusy("ai-all");
    setError("");
    setMessage("");
    appliedScriptJob.current = "";
    try {
      // 先存目前骨架（帳號／delay／空白）再觸發 job
      await repos.plays.save(editing);
      const out = await repos.plays.generateScript(editing.id);
      if (out.async && out.jobId) {
        trackScriptJob(out.jobId);
        setMessage(t("plays.scriptJobQueued"));
        void reloadJobs();
        return;
      }
      // 同步降級
      const fresh = await repos.plays.get(editing.id);
      if (fresh) {
        setEditing(fresh);
        setMessage(t("plays.filled", { n: emptyN }));
      }
      setBusy("");
    } catch (e) {
      setError(e instanceof Error ? e.message : t("plays.aiFail"));
      setBusy("");
    }
  }

  async function saveScheme() {
    if (!editing || !targetCtx) return;

    let play: ThreadPlay;
    if (targetMode === "own" && selectedPost) {
      const cast = editing.cast_account_ids.filter((id) => id !== selectedPost.account_id);
      play = {
        ...editing,
        lead_account_id: selectedPost.account_id,
        cast_account_ids: cast,
        target_own_post_id: selectedPost.id,
        target_external: null,
        steps: editing.steps.map((s, i) => ({
          ...s,
          kind: "reply",
          sort_order: i,
          delay_from_previous_sec:
            s.delay_from_previous_sec || (editing.interval_minutes || 5) * 60,
        })),
      };
    } else if (targetMode === "link" && external) {
      const speakers = speakersOf(editing);
      play = {
        ...editing,
        lead_account_id: speakers[0] || editing.lead_account_id,
        cast_account_ids: speakers.slice(1),
        target_own_post_id: null,
        target_external: external,
        steps: editing.steps.map((s, i) => ({
          ...s,
          kind: "reply",
          sort_order: i,
          delay_from_previous_sec:
            s.delay_from_previous_sec || (editing.interval_minutes || 5) * 60,
        })),
      };
    } else {
      setError(t("plays.needTarget"));
      return;
    }

    const code = validateUnderPostScheme(play);
    if (code) {
      setError(t(code));
      return;
    }
    setBusy("save");
    setError("");
    try {
      const saved = await repos.plays.save(play);
      setEditing(saved);
      setMessage(t("plays.saved"));
      refresh();
      await reloadSchemes();
    } catch (e) {
      setError(e instanceof Error ? e.message : t("plays.saveFail"));
    } finally {
      setBusy("");
    }
  }

  async function submitScheme() {
    if (!editing) return;
    setBusy("submit");
    setError("");
    try {
      await repos.plays.save(editing);
      const bundle = await repos.plays.submit(editing.id);
      setMessage(t("plays.submitted"));
      setEditing(null);
      refresh();
      navigate(`/app/outbox/${bundle.id}`);
    } catch (e) {
      setError(e instanceof Error ? e.message : t("plays.submitFail"));
    } finally {
      setBusy("");
    }
  }

  async function removeScheme(id: string) {
    if (!window.confirm(t("plays.confirmDelete"))) return;
    await repos.plays.remove(id);
    if (editing?.id === id) setEditing(null);
    refresh();
    await reloadSchemes();
  }

  const speakerIds = editing
    ? speakersOf({
        lead_account_id: editing.lead_account_id,
        cast_account_ids: editing.cast_account_ids,
      })
    : [];

  return (
    <div className="hb-stack">
      <div className="hb-tabs hb-tabs--sm" role="tablist">
        <button
          type="button"
          className={`hb-tab ${targetMode === "own" ? "is-active" : ""}`}
          onClick={() => switchMode("own")}
        >
          {t("plays.tabOwn")}
        </button>
        <button
          type="button"
          className={`hb-tab ${targetMode === "link" ? "is-active" : ""}`}
          onClick={() => switchMode("link")}
        >
          {t("plays.tabLink")}
        </button>
      </div>

      {targetMode === "own" ? (
        posts.length === 0 ? (
          <EmptyState title={t("plays.noPosts")} />
        ) : (
          <Select
            label={t("plays.targetPost")}
            value={postId}
            onChange={(e) => {
              setPostId(e.target.value);
              setEditing(null);
              setMessage("");
              setError("");
            }}
          >
            {posts.map((p) => (
              <option key={p.id} value={p.id}>
                {p.text.slice(0, 48)}
                {p.text.length > 48 ? "…" : ""}
                {t("plays.likesSuffix", { n: p.like_count })}
              </option>
            ))}
          </Select>
        )
      ) : (
        <Card title={t("plays.linkCard")}>
          <div className="hb-stack">
            <Input
              label={t("plays.postLink")}
              value={linkInput}
              onChange={(e) => setLinkInput(e.target.value)}
              placeholder="https://www.threads.net/@user/post/…"
              onKeyDown={(e) => {
                if (e.key === "Enter") {
                  e.preventDefault();
                  void resolveLink();
                }
              }}
            />
            <div className="hb-wizard-actions">
              <Button
                type="button"
                onClick={() => void resolveLink()}
                disabled={busy === "resolve" || !linkInput.trim()}
              >
                {busy === "resolve" ? t("plays.resolving") : t("plays.resolve")}
              </Button>
            </div>
            <p className="text-muted" style={{ fontSize: "var(--hb-text-xs)", margin: 0 }}>
              {t("plays.resolveHint")}
            </p>
          </div>
        </Card>
      )}

      {targetMode === "own" && selectedPost ? (
        <Card>
          <p className="text-muted" style={{ fontSize: "var(--hb-text-xs)", margin: "0 0 0.35rem" }}>
            {t("plays.targetOwn")}
          </p>
          <p style={{ margin: 0 }}>{selectedPost.text}</p>
          <p className="text-muted" style={{ fontSize: "var(--hb-text-xs)", margin: "0.35rem 0 0" }}>
            {formatLocalDateTime(selectedPost.published_at)}
            {allowHttpUrl(selectedPost.permalink) ? (
              <>
                {" · "}
                <a href={allowHttpUrl(selectedPost.permalink) ?? undefined} target="_blank" rel="noreferrer">
                  {t("plays.openThreads")}
                </a>
              </>
            ) : null}
          </p>
        </Card>
      ) : null}

      {targetMode === "link" && external ? (
        <Card>
          <div className="hb-inline-badges" style={{ marginBottom: "0.4rem" }}>
            <Badge tone="brand">{t("plays.external")}</Badge>
            {external.author_username ? (
              <Badge tone="neutral">@{external.author_username}</Badge>
            ) : null}
            {external.shortcode ? (
              <Badge tone="neutral">{external.shortcode}</Badge>
            ) : null}
          </div>
          <p style={{ margin: "0 0 0.35rem" }}>{external.text_preview}</p>
          <p className="text-muted" style={{ fontSize: "var(--hb-text-xs)", margin: 0 }}>
            {allowHttpUrl(external.url) ? (
              <a href={allowHttpUrl(external.url) ?? undefined} target="_blank" rel="noreferrer">
                {external.url}
              </a>
            ) : null}
          </p>
        </Card>
      ) : null}

      {hasTarget ? (
        <>
          <div className="hb-toolbar">
            <Button type="button" onClick={startNew}>
              {t("plays.addScheme")}
            </Button>
            <p className="text-muted hb-toolbar__hint">
              {t("plays.schemeCount", { n: schemes.length })}
            </p>
          </div>

          {message ? (
            <p className="hb-banner-ok" role="status">
              {message}
            </p>
          ) : null}
          {error ? (
            <p className="hb-form-error" role="alert">
              {error}
            </p>
          ) : null}

          {schemes.length === 0 && !editing ? (
            <EmptyState title={t("plays.noSchemes")} />
          ) : (
            <div className="hb-stack">
              {schemes.map((s) => (
                <Card key={s.id}>
                  <div className="hb-list-row">
                    <div>
                      <strong>{s.title}</strong>
                      <p className="text-muted" style={{ fontSize: "var(--hb-text-sm)" }}>
                        {t("plays.replyCount", { n: s.steps.length })}
                        {" · "}
                        {s.steps
                          .map(
                            (st) =>
                              accounts.find((a) => a.id === st.account_id)?.display_name || "?",
                          )
                          .join(" → ")}
                      </p>
                    </div>
                    <div className="hb-list-row__actions">
                      <Badge tone="neutral">{s.status}</Badge>
                      <Button type="button" variant="ghost" onClick={() => startEdit(s)}>
                        {t("common.edit")}
                      </Button>
                      <Button type="button" variant="ghost" onClick={() => void removeScheme(s.id)}>
                        {t("common.delete")}
                      </Button>
                    </div>
                  </div>
                </Card>
              ))}
            </div>
          )}

          {editing ? (
            <Card title={t("plays.editTitle", { title: editing.title })}>
              <div className="hb-stack">
                <Input
                  label={t("plays.schemeName")}
                  value={editing.title}
                  onChange={(e) => patchEditing({ title: e.target.value })}
                  placeholder={t("plays.schemeNamePh")}
                />
                <p className="hb-field__label">
                  {targetMode === "own" ? t("plays.speakersOwn") : t("plays.speakers")}
                </p>
                <div className="hb-inline-badges" style={{ gap: "0.5rem" }}>
                  {poolAccounts.map((a) => {
                    const isOwner =
                      targetMode === "own" && targetCtx?.ownerAccountId === a.id;
                    const checked = speakerIds.includes(a.id);
                    return (
                      <label key={a.id} className="hb-choice" style={{ padding: "0.4rem 0.6rem" }}>
                        <input
                          type="checkbox"
                          checked={checked}
                          disabled={isOwner}
                          onChange={() => toggleSpeaker(a.id)}
                        />
                        <span>
                          {a.display_name}
                          {isOwner ? t("plays.postOwner") : ""}
                        </span>
                      </label>
                    );
                  })}
                </div>
                {poolAccounts.length === 0 ? (
                  <p className="text-muted" style={{ fontSize: "var(--hb-text-xs)" }}>
                    {t("plays.noAccounts")}
                  </p>
                ) : null}

                <div className="hb-toolbar">
                  <Input
                    label={t("plays.interval")}
                    type="number"
                    min={0}
                    value={String(editing.interval_minutes ?? 5)}
                    onChange={(e) =>
                      patchEditing({ interval_minutes: Math.max(0, Number(e.target.value) || 0) })
                    }
                  />
                  <Button type="button" variant="ghost" onClick={applyInterval}>
                    {t("plays.applyInterval")}
                  </Button>
                </div>

                <div className="hb-wizard-actions">
                  <Button
                    type="button"
                    variant="ghost"
                    disabled={busy === "ai-all"}
                    onClick={() => void aiFillAllEmpty()}
                  >
                    {busy === "ai-all" ? t("plays.aiBusy") : t("plays.aiEmpty")}
                  </Button>
                </div>

                <p className="hb-field__label">{t("plays.replies", { n: editing.steps.length })}</p>
                {editing.steps.map((step, index) => (
                  <div key={step.id} className="hb-script-row">
                    <div className="hb-script-row__head">
                      <strong>{t("plays.stepN", { n: index + 1 })}</strong>
                      <div className="hb-wizard-actions" style={{ margin: 0 }}>
                        <Button
                          type="button"
                          variant="ghost"
                          disabled={busyStepId === step.id}
                          onClick={() => void aiFillStep(step.id)}
                        >
                          {busyStepId === step.id ? "…" : "AI"}
                        </Button>
                        <Button
                          type="button"
                          variant="ghost"
                          onClick={() => removeStep(step.id)}
                          disabled={editing.steps.length <= 1}
                        >
                          {t("common.delete")}
                        </Button>
                      </div>
                    </div>
                    <Select
                      label={t("plays.who")}
                      value={step.account_id}
                      onChange={(e) => updateStep(step.id, { account_id: e.target.value })}
                    >
                      {poolAccounts
                        .filter((a) => speakerIds.includes(a.id) || a.id === step.account_id)
                        .map((a) => (
                          <option key={a.id} value={a.id}>
                            {a.display_name} · @{a.username}
                          </option>
                        ))}
                    </Select>
                    <Select
                      label={t("plays.personaOpt")}
                      value={step.persona_id || ""}
                      onChange={(e) => updateStep(step.id, { persona_id: e.target.value || null })}
                    >
                      <option value="">—</option>
                      {personas.map((p) => (
                        <option key={p.id} value={p.id}>
                          {personaOptionLabel(p)}
                        </option>
                      ))}
                    </Select>
                    <Select
                      label={t("plays.brandOpt")}
                      value={step.brand_id || ""}
                      onChange={(e) => updateStep(step.id, { brand_id: e.target.value || null })}
                    >
                      <option value="">—</option>
                      {brands.map((b) => (
                        <option key={b.id} value={b.id}>
                          {b.display_name}
                        </option>
                      ))}
                    </Select>
                    <Textarea
                      label={t("plays.reply")}
                      value={step.text}
                      onChange={(e) => updateStep(step.id, { text: e.target.value })}
                      rows={3}
                    />
                    <ImageAttach
                      images={stepToImages(step, t)}
                      onChange={(imgs) =>
                        updateStep(step.id, {
                          image_urls: imgs.map((i) => i.url),
                        })
                      }
                      label={t("plays.attach")}
                    />
                  </div>
                ))}

                <Button type="button" variant="ghost" onClick={addReply}>
                  {t("plays.addOne")}
                </Button>

                <div className="hb-wizard-actions">
                  <Button type="button" onClick={() => void saveScheme()} disabled={busy === "save"}>
                    {busy === "save" ? t("plays.saving") : t("plays.saveScheme")}
                  </Button>
                  <Button
                    type="button"
                    onClick={() => void submitScheme()}
                    disabled={busy === "submit"}
                  >
                    {busy === "submit" ? t("plays.submitting") : t("plays.submitOutbox")}
                  </Button>
                  <Button type="button" variant="ghost" onClick={() => setEditing(null)}>
                    {t("plays.closeEdit")}
                  </Button>
                </div>
              </div>
            </Card>
          ) : null}
        </>
      ) : targetMode === "link" && !external ? (
        <EmptyState title={t("plays.noTarget")} />
      ) : null}

      {message && !hasTarget ? (
        <p className="hb-banner-ok" role="status">
          {message}
        </p>
      ) : null}
      {error && !hasTarget ? (
        <p className="hb-form-error" role="alert">
          {error}
        </p>
      ) : null}
    </div>
  );
}
