/**
 * 今日商機頁（demand-radar T551／T552）。
 * 統計列＋高／中／低分組；低意向預設收合；空狀態必顯示 empty_reason 與下一步。
 */
import { useCallback, useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { PageHeader } from "../components/layout/PageHeader";
import { ExplorePanel } from "../components/radar/ExplorePanel";
import { ManualImportPanel } from "../components/radar/ManualImportPanel";
import { Badge, Button, EmptyState, Select } from "../components/ui";
import type { BadgeTone } from "../components/ui";
import { useRepos } from "../data/DataContext";
import type {
  IntentBand,
  Opportunity,
  RadarEmptyReason,
  RadarToday,
  ReplyVariant,
  ReplyVariantKind,
  ThreadsAccount,
} from "../domain/types";
import { useI18n } from "../i18n/I18nContext";
import { useFormatApiError } from "../lib/apiErrors";
import { formatTimeAgo } from "../lib/time";
import "../styles/radar.css";

const REPLY_VARIANTS: ReplyVariantKind[] = [
  "public_comment",
  "dm",
  "no_sales",
  "professional",
  "humorous",
];

function bandTone(band: string): BadgeTone {
  if (band === "high") return "success";
  if (band === "mid") return "warning";
  return "neutral";
}

function statusTone(status: string): BadgeTone {
  if (status === "accepted") return "brand";
  if (status === "dismissed") return "neutral";
  return "success";
}

function emptyAction(reason?: RadarEmptyReason): { to: string; labelKey: string } | null {
  switch (reason) {
    case "no_profile":
      return { to: "/app/brands?tab=serviceProfile", labelKey: "radar.today.empty.goProfile" };
    case "no_watch":
    case "all_watches_paused":
    case "not_swept_yet":
    case "sweep_failed":
    case "no_hit":
      return { to: "/app/radar/watches", labelKey: "radar.today.empty.goWatches" };
    default:
      return { to: "/app/radar/watches", labelKey: "radar.today.empty.goWatches" };
  }
}

function OppCard({
  o,
  reply,
  busy,
  canSendOutbox,
  onAccept,
  onDismiss,
  onGenerateReply,
  onCopyReply,
  onMarkUsed,
  onOverrideBand,
}: {
  o: Opportunity;
  reply?: ReplyVariant;
  busy: string;
  /** false＝沒有可用的已連 Threads 帳號，一鍵送出要停用並指路去連帳號 */
  canSendOutbox: boolean;
  onAccept: () => void;
  onDismiss: () => void;
  onGenerateReply: (variant: ReplyVariantKind) => void;
  onCopyReply: () => void;
  onMarkUsed: (channel: "outbox" | "manual_copy") => void;
  onOverrideBand: (band: IntentBand) => void;
}) {
  const { t } = useI18n();
  const [reasonsOpen, setReasonsOpen] = useState(false);
  const [replyOpen, setReplyOpen] = useState(Boolean(reply?.text || o.default_reply?.text));
  const [overrideOpen, setOverrideOpen] = useState(false);

  const activeReply = reply || o.default_reply;
  const shownReply = activeReply?.text;
  const isDm = activeReply?.variant === "dm";
  const isDone = o.status === "accepted" || o.status === "dismissed";

  return (
    <article className={`hb-opp-card hb-opp-card--${o.intent_band}`}>
      <header className="hb-opp-card__head">
        <div className="hb-opp-card__meta">
          <Badge tone={bandTone(o.intent_band)}>
            {t(`radar.today.band.${o.intent_band}`)} · {o.intent_score}
          </Badge>
          {o.status !== "qualified" ? (
            <Badge tone={statusTone(o.status)}>{t(`radar.today.status.${o.status}`)}</Badge>
          ) : null}
          {o.region_detected ? (
            <span
              className={
                o.region_match === "mismatch"
                  ? "hb-opp-region--mismatch"
                  : o.region_match === "unknown"
                    ? "hb-opp-region--unknown"
                    : undefined
              }
            >
              {o.region_detected}
            </span>
          ) : (
            <span className="hb-opp-region--unknown">{t("radar.today.regionUnknown")}</span>
          )}
          <span className="radar-card__meta">{formatTimeAgo(o.posted_at)}</span>
        </div>
      </header>

      <p className="hb-opp-card__text">{o.text}</p>
      <p className="radar-card__meta">
        @{o.author_handle}
        {o.matched_service ? ` · ${o.matched_service}` : ""}
        {o.matched_terms?.length ? ` · ${o.matched_terms.join("、")}` : ""}
      </p>

      {shownReply ? (
        <pre className="radar-card__reply">{shownReply}</pre>
      ) : null}

      {reasonsOpen ? (
        <ul className="hb-opp-reasons">
          {(o.reasons ?? []).map((r) => (
            <li key={r.dimension} className="hb-opp-reason">
              <strong>{t(`radar.today.dim.${r.dimension}`)}</strong>
              <span>{r.score}</span>
              <span>{r.reason}</span>
            </li>
          ))}
        </ul>
      ) : null}

      {replyOpen ? (
        <div className="hb-reply-variants">
          <p className="hb-radar-section__hint">{t("radar.today.reply.hint")}</p>
          <div className="hb-radar-actions">
            {REPLY_VARIANTS.map((v) => (
              <Button
                key={v}
                type="button"
                variant="secondary"
                disabled={busy === `reply-${o.id}`}
                onClick={() => onGenerateReply(v)}
              >
                {t(`radar.today.reply.variant.${v}`)}
              </Button>
            ))}
            {shownReply ? (
              <Button type="button" variant="ghost" onClick={onCopyReply}>
                {t("radar.today.reply.copy")}
              </Button>
            ) : null}
            {activeReply && !activeReply.used_at ? (
              <>
                <Button
                  type="button"
                  variant="ghost"
                  disabled={busy === `mark-${o.id}`}
                  onClick={() => onMarkUsed("manual_copy")}
                >
                  {t("radar.today.reply.markCopy")}
                </Button>
                {!isDm ? (
                  <Button
                    type="button"
                    variant="secondary"
                    disabled={busy === `mark-${o.id}` || !canSendOutbox}
                    title={canSendOutbox ? undefined : t("radar.today.reply.needAccount")}
                    onClick={() => onMarkUsed("outbox")}
                  >
                    {t("radar.today.reply.markOutbox")}
                  </Button>
                ) : null}
              </>
            ) : null}
            {!isDm && !canSendOutbox && activeReply && !activeReply.used_at ? (
              <span className="hb-radar-section__hint">
                {t("radar.today.reply.needAccount")}{" "}
                <Link to="/app/crew">{t("nav.crew")}</Link>
              </span>
            ) : null}
            {activeReply?.used_at ? (
              <span className="hb-radar-section__hint">
                {activeReply.sent_channel === "outbox" && activeReply.outbox_id
                  ? t("radar.today.reply.usedOutbox")
                  : t("radar.today.reply.used")}
              </span>
            ) : null}
          </div>
        </div>
      ) : null}

      {overrideOpen ? (
        <div className="hb-radar-actions">
          {(["high", "mid", "low"] as IntentBand[]).map((b) => (
            <Button
              key={b}
              type="button"
              variant="secondary"
              disabled={busy === `override-${o.id}` || o.intent_band === b}
              onClick={() => onOverrideBand(b)}
            >
              {t(`radar.today.band.${b}`)}
            </Button>
          ))}
        </div>
      ) : null}

      <div className="radar-card__actions">
        <a className="hb-btn hb-btn--ghost" href={o.permalink} target="_blank" rel="noreferrer">
          {t("radar.today.action.open")}
        </a>
        {!isDone ? (
          <>
            <Button
              type="button"
              disabled={busy === `accept-${o.id}`}
              onClick={onAccept}
            >
              {t("radar.today.action.accept")}
            </Button>
            <Button
              type="button"
              variant="ghost"
              disabled={busy === `dismiss-${o.id}`}
              onClick={onDismiss}
            >
              {t("radar.today.action.dismiss")}
            </Button>
          </>
        ) : null}
        <Button type="button" variant="secondary" onClick={() => setReplyOpen((v) => !v)}>
          {replyOpen ? t("radar.today.action.hideReply") : t("radar.today.action.reply")}
        </Button>
        <Button type="button" variant="ghost" onClick={() => setReasonsOpen((v) => !v)}>
          {reasonsOpen ? t("radar.today.action.hideReasons") : t("radar.today.action.reasons")}
        </Button>
        <Button type="button" variant="ghost" onClick={() => setOverrideOpen((v) => !v)}>
          {t("radar.today.action.override")}
        </Button>
      </div>
    </article>
  );
}

export function RadarTodayPage() {
  const { t } = useI18n();
  const repos = useRepos();
  const formatErr = useFormatApiError();
  const [data, setData] = useState<RadarToday | null>(null);
  const [err, setErr] = useState<string | null>(null);
  const [msg, setMsg] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);
  const [lowOpen, setLowOpen] = useState(false);
  const [busy, setBusy] = useState("");
  const [replies, setReplies] = useState<Record<string, ReplyVariant>>({});
  const [accounts, setAccounts] = useState<ThreadsAccount[]>([]);
  const [sendAccountId, setSendAccountId] = useState("");
  const [importOpen, setImportOpen] = useState(false);
  const [exploreOpen, setExploreOpen] = useState(false);

  const load = useCallback(async () => {
    const next = await repos.radar.getToday();
    setData(next);
  }, [repos.radar]);

  useEffect(() => {
    let alive = true;
    setLoading(true);
    load()
      .then(() => {
        if (alive) setErr(null);
      })
      .catch((e) => {
        if (alive) setErr(formatErr(e));
      })
      .finally(() => {
        if (alive) setLoading(false);
      });
    return () => {
      alive = false;
    };
  }, [load]); // eslint-disable-line react-hooks/exhaustive-deps

  // 一鍵送出要知道用哪個帳號；多數人只有一個已連帳號，預設選第一個可用的即可。
  useEffect(() => {
    void repos.accounts
      .list()
      .then((list) => {
        const usable = list.filter((a) => a.is_usable);
        setAccounts(usable);
        setSendAccountId((prev) => (prev && usable.some((a) => a.id === prev) ? prev : usable[0]?.id || ""));
      })
      .catch(() => undefined);
  }, [repos.accounts]);

  async function run(key: string, action: () => Promise<void>, okMessage?: string) {
    setBusy(key);
    setErr(null);
    setMsg(null);
    try {
      await action();
      if (okMessage) setMsg(okMessage);
    } catch (e) {
      setErr(formatErr(e));
    } finally {
      setBusy("");
    }
  }

  async function accept(id: string) {
    await run(`accept-${id}`, async () => {
      await repos.radar.acceptOpportunity(id);
      await load();
    }, t("radar.today.msg.accepted"));
  }

  async function dismiss(id: string) {
    await run(`dismiss-${id}`, async () => {
      await repos.radar.dismissOpportunity(id);
      await load();
    }, t("radar.today.msg.dismissed"));
  }

  async function generateReply(id: string, variant: ReplyVariantKind) {
    await run(`reply-${id}`, async () => {
      const r = await repos.radar.createReply(id, variant);
      setReplies((m) => ({ ...m, [id]: r }));
    }, t("radar.today.msg.replyReady"));
  }

  async function markUsed(id: string, channel: "outbox" | "manual_copy") {
    const r = replies[id] || data?.high.concat(data.mid, data.low).find((o) => o.id === id)?.default_reply;
    if (!r?.id) {
      setErr(t("radar.today.msg.needReply"));
      return;
    }
    await run(`mark-${id}`, async () => {
      const res = await repos.radar.markReplyUsed(
        id,
        r.id,
        channel,
        channel === "outbox" ? sendAccountId : undefined,
      );
      setReplies((m) => ({ ...m, [id]: res.reply }));
      if (res.health_advice) setMsg(res.health_advice);
    }, channel === "outbox" ? t("radar.today.msg.sent") : t("radar.today.msg.marked"));
  }

  async function overrideBand(id: string, band: IntentBand) {
    await run(`override-${id}`, async () => {
      await repos.radar.overrideOpportunity(id, { band });
      await load();
    }, t("radar.today.msg.overridden"));
  }

  async function copyReply(text: string) {
    try {
      await navigator.clipboard.writeText(text);
      setMsg(t("radar.today.msg.copied"));
    } catch {
      setErr(t("radar.today.msg.copyFail"));
    }
  }

  const action = data ? emptyAction(data.empty_reason) : null;

  return (
    <>
      <PageHeader title={t("radar.today.title")} />

      <div className="hb-radar-actions hb-radar-actions--toolbar">
        <Link className="hb-btn hb-btn--secondary" to="/app/radar/watches">
          {t("radar.today.link.watches")}
        </Link>
        <Button
          type="button"
          variant="secondary"
          onClick={() => {
            setExploreOpen((v) => !v);
            if (!exploreOpen) setImportOpen(false);
          }}
        >
          {exploreOpen ? t("radar.explore.close") : t("radar.explore.open")}
        </Button>
        <Button
          type="button"
          variant="secondary"
          onClick={() => {
            setImportOpen((v) => !v);
            if (!importOpen) setExploreOpen(false);
          }}
        >
          {importOpen ? t("radar.import.close") : t("radar.import.open")}
        </Button>
        <Link className="hb-btn hb-btn--ghost" to="/app/crm">
          {t("radar.today.link.crm")}
        </Link>
        {accounts.length > 1 ? (
          <Select
            name="radar-send-account"
            label={t("radar.today.sendAccount")}
            value={sendAccountId}
            onChange={(e) => setSendAccountId(e.target.value)}
          >
            {accounts.map((a) => (
              <option key={a.id} value={a.id}>
                @{a.username}
              </option>
            ))}
          </Select>
        ) : null}
      </div>

      {exploreOpen ? <ExplorePanel onExplored={() => void load()} /> : null}
      {importOpen ? <ManualImportPanel onImported={() => void load()} /> : null}

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

      {loading && !data ? <p className="hb-radar-section__hint">{t("common.loading")}</p> : null}

      {data ? (
        <section className="hb-radar-page">
          <div className="hb-radar-stats">
            <div className="hb-radar-stat">
              <span className="hb-radar-stat__value">{data.stats.total}</span>
              <span className="hb-radar-stat__label">{t("radar.today.stats.total")}</span>
            </div>
            <div className="hb-radar-stat">
              <span className="hb-radar-stat__value">{data.stats.high}</span>
              <span className="hb-radar-stat__label">{t("radar.today.stats.high")}</span>
            </div>
            <div className="hb-radar-stat">
              <span className="hb-radar-stat__value">{data.stats.mid}</span>
              <span className="hb-radar-stat__label">{t("radar.today.stats.mid")}</span>
            </div>
            <div className="hb-radar-stat">
              <span className="hb-radar-stat__value">{data.stats.low}</span>
              <span className="hb-radar-stat__label">{t("radar.today.stats.low")}</span>
            </div>
          </div>

          {data.truncated_count > 0 ? (
            <p className="hb-radar-quota hb-radar-quota--full" role="status">
              {t("radar.today.truncated", { n: data.truncated_count })}
            </p>
          ) : null}

          {data.last_swept_at ? (
            <p className="hb-radar-section__hint">
              {t("radar.today.lastSwept", { at: formatTimeAgo(data.last_swept_at) })}
            </p>
          ) : null}

          {data.stats.total === 0 ? (
            <EmptyState
              title={data.empty_hint || t("radar.today.empty.title")}
              description={
                data.empty_reason
                  ? t(`radar.today.empty.reason.${data.empty_reason}`)
                  : t("radar.today.empty.fallback")
              }
              action={
                action ? (
                  <Link className="hb-btn hb-btn--secondary" to={action.to}>
                    {t(action.labelKey)}
                  </Link>
                ) : null
              }
            />
          ) : (
            <>
              <div className="hb-radar-section">
                <h2 className="hb-radar-section__title">
                  {t("radar.today.group.high")} ({data.high.length})
                </h2>
                {data.high.length === 0 ? (
                  <p className="hb-radar-section__hint">{t("radar.today.group.empty")}</p>
                ) : (
                  data.high.map((o) => (
                    <OppCard
                      key={o.id}
                      o={o}
                      reply={replies[o.id]}
                      busy={busy}
                      canSendOutbox={Boolean(sendAccountId)}
                      onAccept={() => void accept(o.id)}
                      onDismiss={() => void dismiss(o.id)}
                      onGenerateReply={(v) => void generateReply(o.id, v)}
                      onMarkUsed={(ch) => void markUsed(o.id, ch)}
                      onCopyReply={() =>
                        void copyReply(replies[o.id]?.text || o.default_reply?.text || "")
                      }
                      onOverrideBand={(b) => void overrideBand(o.id, b)}
                    />
                  ))
                )}
              </div>

              <div className="hb-radar-section">
                <h2 className="hb-radar-section__title">
                  {t("radar.today.group.mid")} ({data.mid.length})
                </h2>
                {data.mid.length === 0 ? (
                  <p className="hb-radar-section__hint">{t("radar.today.group.empty")}</p>
                ) : (
                  data.mid.map((o) => (
                    <OppCard
                      key={o.id}
                      o={o}
                      reply={replies[o.id]}
                      busy={busy}
                      canSendOutbox={Boolean(sendAccountId)}
                      onAccept={() => void accept(o.id)}
                      onDismiss={() => void dismiss(o.id)}
                      onGenerateReply={(v) => void generateReply(o.id, v)}
                      onMarkUsed={(ch) => void markUsed(o.id, ch)}
                      onCopyReply={() =>
                        void copyReply(replies[o.id]?.text || o.default_reply?.text || "")
                      }
                      onOverrideBand={(b) => void overrideBand(o.id, b)}
                    />
                  ))
                )}
              </div>

              <div className="hb-radar-section">
                <button
                  type="button"
                  className="hb-btn hb-btn--ghost"
                  onClick={() => setLowOpen((v) => !v)}
                  aria-expanded={lowOpen}
                >
                  {t("radar.today.group.low")} ({data.low.length}) ·{" "}
                  {lowOpen ? t("radar.today.group.collapse") : t("radar.today.group.expand")}
                </button>
                {lowOpen
                  ? data.low.map((o) => (
                      <OppCard
                        key={o.id}
                        o={o}
                        reply={replies[o.id]}
                        busy={busy}
                        canSendOutbox={Boolean(sendAccountId)}
                        onAccept={() => void accept(o.id)}
                        onDismiss={() => void dismiss(o.id)}
                        onGenerateReply={(v) => void generateReply(o.id, v)}
                        onMarkUsed={(ch) => void markUsed(o.id, ch)}
                      onCopyReply={() =>
                          void copyReply(replies[o.id]?.text || o.default_reply?.text || "")
                        }
                        onOverrideBand={(b) => void overrideBand(o.id, b)}
                      />
                    ))
                  : null}
              </div>
            </>
          )}
        </section>
      ) : null}
    </>
  );
}
