import { useCallback, useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { PageHeader } from "../components/layout/PageHeader";
import { SERVICE_AREAS } from "../components/radar/serviceAreas";
import { WatchSuggestPanel } from "../components/radar/WatchSuggestPanel";
import { Badge, Button, EmptyState, Pager, Select, Textarea } from "../components/ui";
import { useRepos } from "../data/DataContext";
import type { Brand, BrandProduct, RadarSweep, RadarWatch, RadarWatchStatus, WatchTermSuggestion } from "../domain/types";
import type { DemandMap } from "../domain/types";
import { useI18n } from "../i18n/I18nContext";
import { useFormatApiError } from "../lib/apiErrors";
import { expandIncludeTerms, isThreadsSearchable } from "../lib/threadsTerm";
import { buildQueryPlan } from "../components/radar/QueryPlanPreview";
import { formatLocalDateTime } from "../lib/time";
import { ProductWatchForm } from "../components/radar/ProductWatchForm";
import { DemandMapEditor, type DemandMapPatch } from "../components/radar/DemandMapEditor";
import { SweepFunnelSummary } from "../components/radar/SweepFunnelSummary";

const PAGE_SIZE = 20;

type StatusFilter = "" | RadarWatchStatus;

type Draft = {
  /** 編輯中的訂閱 id；空字串＝新增 */
  id: string;
  terms: string;
  excludeTerms: string;
  regions: string[];
  enabled: boolean;
  brandId: string;
  productId: string;
};

const emptyDraft: Draft = { id: "", terms: "", excludeTerms: "", regions: [], enabled: true, brandId: "", productId: "" };

/** 關鍵字可以用換行、逗號或頓號分隔；使用者常常直接從別處貼過來。 */
function splitTerms(raw: string): string[] {
  return raw
    .split(/[\n,、]/)
    .map((s) => s.trim())
    .filter(Boolean);
}

function statusTone(status: RadarWatchStatus): "success" | "warning" | "neutral" {
  if (status === "active") return "success";
  if (status === "paused") return "warning";
  return "neutral";
}

/** 雷達訂閱：常駐關鍵字，每日自動巡。品牌／服務檔案是選項。 */
export function RadarWatchesPage() {
  const { t } = useI18n();
  const repos = useRepos();
  const formatError = useFormatApiError();

  const [watches, setWatches] = useState<RadarWatch[]>([]);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(1);
  const [statusFilter, setStatusFilter] = useState<StatusFilter>("");
  const [activeCount, setActiveCount] = useState(0);
  const [maxActive, setMaxActive] = useState(0);
  const [profileExists, setProfileExists] = useState(true);

  const [draft, setDraft] = useState<Draft>(emptyDraft);
  const [formOpen, setFormOpen] = useState(false);
  /** 這次編輯是「舊訂閱補綁品牌／產品」：選了品牌之後選單不能跟著消失，否則永遠選不到產品。 */
  const [rebinding, setRebinding] = useState(false);
  /**
   * 預設只問關鍵字與地區，和今日頁的開工表單一致。
   * 排除詞、品牌產品、需求地圖與關鍵字建議都是進階，收起來才不會第一次就被十個欄位擋住。
   */
  const [advancedOpen, setAdvancedOpen] = useState(false);
  const [loading, setLoading] = useState(true);
  const [busy, setBusy] = useState("");
  const [error, setError] = useState("");
  const [message, setMessage] = useState("");
  const [lastSweep, setLastSweep] = useState<RadarSweep | null>(null);
  const [justTriggeredFirstSweep, setJustTriggeredFirstSweep] = useState(false);
  const [brands, setBrands] = useState<Brand[]>([]);
  const [products, setProducts] = useState<BrandProduct[]>([]);
  const [demandMap, setDemandMap] = useState<DemandMap | null>(null);
  const [pendingDemandMapPatch, setPendingDemandMapPatch] = useState<DemandMapPatch | null>(null);
  const [demandMapLoading, setDemandMapLoading] = useState(false);
  const [demandMapError, setDemandMapError] = useState("");
  const scout = (repos as unknown as { scout?: { listBrands: () => Promise<Brand[]>; listProducts: (id: string) => Promise<BrandProduct[]> } }).scout;
  // The live repository always exposes Scout. Legacy page tests can inject only Radar;
  // retaining the generic fallback there keeps the compatibility path testable.
  const productContextAvailable = Boolean(scout);

  useEffect(() => {
    if (!productContextAvailable || !draft.productId) {
      setDemandMap(null);
      setPendingDemandMapPatch(null);
      setDemandMapError("");
      return;
    }
    let alive = true;
    setDemandMapLoading(true);
    setPendingDemandMapPatch(null);
    void repos.radar.getDemandMap(draft.productId)
      .then((map) => { if (alive) { setDemandMap(map); setDemandMapError(""); } })
      .catch((e) => { if (alive) setDemandMapError(formatError(e)); })
      .finally(() => { if (alive) setDemandMapLoading(false); });
    return () => { alive = false; };
  }, [draft.productId, productContextAvailable, repos.radar, formatError]);

  useEffect(() => {
    if (!formOpen || draft.id || !demandMap || demandMap.state !== "ready") return;
    setDraft((d) => {
      if (splitTerms(d.terms).length > 0) return d;
      const seeded = expandIncludeTerms(buildQueryPlan(demandMap).map((group) => group.terms.join(" ")));
      if (!seeded.length) return d;
      return { ...d, terms: seeded.join("\n") };
    });
  }, [demandMap, formOpen, draft.id]);

  useEffect(() => {
    if (!scout) return;
    void scout.listBrands().then(setBrands).catch(() => setBrands([]));
  }, [scout]);

  useEffect(() => {
    if (!scout || !draft.brandId) {
      setProducts([]);
      return;
    }
    void scout.listProducts(draft.brandId).then(setProducts).catch(() => setProducts([]));
  }, [scout, draft.brandId]);

  const load = useCallback(async () => {
    const res = await repos.radar.listWatches(page, PAGE_SIZE, statusFilter || undefined);
    setWatches(res.list);
    setTotal(res.total);
    setActiveCount(res.active_count);
    setMaxActive(res.max_active);
    setProfileExists(res.profile_exists);
  }, [repos, page, statusFilter]);

  useEffect(() => {
    let alive = true;
    setLoading(true);
    load()
      .then(() => {
        if (alive) setError("");
      })
      .catch((e) => {
        if (alive) setError(formatError(e));
      })
      .finally(() => {
        if (alive) setLoading(false);
      });
    return () => {
      alive = false;
    };
    // formatError 隨語言變動，掛上去會在切語言時多打一次 API。
  }, [load]); // eslint-disable-line react-hooks/exhaustive-deps

  const quotaFull = maxActive > 0 && activeCount >= maxActive;

  function openCreate() {
    setDraft(emptyDraft);
    setDemandMap(null);
    setPendingDemandMapPatch(null);
    setRebinding(false);
    setAdvancedOpen(false);
    setFormOpen(true);
    setMessage("");
    setError("");
  }

  function openEdit(w: RadarWatch) {
    setDraft({
      id: w.id,
      terms: w.terms.join("\n"),
      excludeTerms: w.exclude_terms.join("\n"),
      regions: w.regions,
      enabled: w.status === "active",
      brandId: w.brand_id || "",
      productId: w.product_id || "",
    });
    setPendingDemandMapPatch(null);
    setRebinding(!w.brand_id);
    // 編輯既有訂閱的人通常就是要改排除詞或補綁產品，這時候藏起來反而多一次點擊。
    setAdvancedOpen(true);
    setFormOpen(true);
    setMessage("");
    setError("");
  }

  function adopt(s: WatchTermSuggestion) {
    setDraft((d) => {
      const key = s.usage === "exclude" ? "excludeTerms" : "terms";
      const incoming = s.usage === "exclude" ? [s.term] : expandIncludeTerms([s.term]);
      const current = splitTerms(d[key]);
      const next = [...current];
      for (const term of incoming) {
        if (!next.some((x) => x.toLowerCase() === term.toLowerCase())) next.push(term);
      }
      if (next.length === current.length) return d;
      return { ...d, [key]: next.join("\n") };
    });
  }

  function adoptAll(list: WatchTermSuggestion[]) {
    for (const s of list) adopt(s);
  }

  function adoptQueries(queries: string[]) {
    setDraft((d) => {
      const incoming = expandIncludeTerms(queries);
      const current = splitTerms(d.terms);
      const next = [...current];
      for (const term of incoming) {
        if (!next.some((x) => x.toLowerCase() === term.toLowerCase())) next.push(term);
      }
      if (next.length === current.length) return d;
      return { ...d, terms: next.join("\n") };
    });
  }

  /** 一次動作 → 重讀清單：狀態變化會連動配額與 profile 提示，局部改 state 容易對不起來。 */
  async function run(
    key: string,
    action: () => Promise<void>,
    okMessage: string | (() => string),
  ): Promise<boolean> {
    setBusy(key);
    setError("");
    setMessage("");
    try {
      await action();
      await load();
      setMessage(typeof okMessage === "function" ? okMessage() : okMessage);
      return true;
    } catch (e) {
      // 配額等硬限制由後端回可讀原因（含上限與升級提示），前端不自行推測文案。
      setError(formatError(e));
      return false;
    } finally {
      setBusy("");
    }
  }

  async function save() {
    const terms = expandIncludeTerms(splitTerms(draft.terms));
    const excludeTerms = splitTerms(draft.excludeTerms);
    if (!terms.length) {
      setError(t("radar.watches.threadsRequired"));
      return;
    }
    if ((draft.brandId && !draft.productId) || (!draft.brandId && draft.productId)) {
      setError(t("radar.watches.needBrandProductShort"));
      return;
    }
    if (draft.productId && pendingDemandMapPatch?.state === "ready") {
      const savedMap = await persistDemandMap(pendingDemandMapPatch, false);
      if (!savedMap) return;
    }
    if (draft.id) {
      const ok = await run(
        "save",
        async () => {
          await repos.radar.updateWatch(draft.id, {
            terms,
            exclude_terms: excludeTerms,
            regions: draft.regions,
          });
          // 補綁的品牌／產品要跟著這次儲存一起生效，不能只在「綁定」按鈕生效。
          if (rebinding && draft.brandId && draft.productId) {
            await repos.radar.assignWatchProduct(draft.id, draft.brandId, draft.productId);
          }
        },
        t("radar.watches.updated"),
      );
      if (ok) {
        setRebinding(false);
        setFormOpen(false);
      }
      return;
    }
    let firstSweepTriggered = false;
    const ok = await run(
      "save",
      async () => {
        const created = await repos.radar.createWatch({
          terms,
          exclude_terms: excludeTerms,
          regions: draft.regions,
          enabled: draft.enabled,
          brand_id: draft.brandId || undefined,
          product_id: draft.productId || undefined,
        });
        firstSweepTriggered = Boolean(created.first_sweep_triggered);
      },
      () => (firstSweepTriggered ? t("radar.watches.createdFirstSweep") : t("radar.watches.created")),
    );
    setJustTriggeredFirstSweep(ok && firstSweepTriggered);
    // 失敗就留著表單，讓使用者改完再送（例如關鍵字太短、配額滿了要先暫停別的）。
    if (ok) setFormOpen(false);
  }

  async function persistDemandMap(patch: DemandMapPatch, showBusy = true): Promise<DemandMap | null> {
    if (showBusy) setBusy("demand-map");
    setDemandMapError("");
    try {
      const next = await repos.radar.updateDemandMap(draft.productId, patch);
      setDemandMap(next);
      setPendingDemandMapPatch(null);
      return next;
    } catch (e) {
      const message = formatError(e);
      setDemandMapError(message);
      setError(message);
      return null;
    } finally {
      if (showBusy) setBusy("");
    }
  }

  async function assignProduct() {
    if (!draft.id || !draft.brandId || !draft.productId) {
      setError(t("radar.watches.needBrandProductShort"));
      return;
    }
    await run("assign", async () => {
      await repos.radar.assignWatchProduct(draft.id, draft.brandId, draft.productId);
    }, t("radar.watches.assigned"));
    setRebinding(false);
    setFormOpen(false);
  }

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

      {!profileExists ? (
        <p className="hb-radar-section__hint" role="note">
          {t("radar.watches.profileOptional")}{" "}
          <Link to="/app/policy">{t("radar.watches.goProfile")}</Link>
        </p>
      ) : null}

      {error ? (
        <p className="hb-banner-error" role="alert">
          {error}
        </p>
      ) : null}
      {message ? (
        <p className="hb-banner-ok" role="status">
          {message}
          {justTriggeredFirstSweep ? (
            <>
              {" "}
              <Link to="/app/today">{t("today.radar.open")}</Link>
            </>
          ) : null}
        </p>
      ) : null}
      {lastSweep ? <SweepFunnelSummary sweep={lastSweep} /> : null}

      <section className="hb-radar-page">
        <div className="hb-radar-section">
          <p className={`hb-radar-quota${quotaFull ? " hb-radar-quota--full" : ""}`}>
            {t("radar.watches.quota", { used: activeCount, max: maxActive })}
            {quotaFull ? ` · ${t("radar.watches.quotaFull")}` : ""}
          </p>
          <div className="hb-radar-schedule" role="note">
            <div>
              <strong>{t("radar.watches.scheduleTitle")}</strong>
              <p>{t("radar.watches.scheduleHint")}</p>
            </div>
            <Link className="hb-btn hb-btn--ghost" to="/app/today">
              {t("radar.watches.openToday")}
            </Link>
          </div>
          <div className="hb-radar-actions">
            <Button type="button" onClick={openCreate} disabled={formOpen}>
              {t("radar.watches.add")}
            </Button>
            <Select
              name="radar-watch-status"
              label={t("radar.watches.filterStatus")}
              value={statusFilter}
              onChange={(e) => {
                setStatusFilter(e.target.value as StatusFilter);
                setPage(1);
              }}
            >
              <option value="">{t("radar.watches.statusAll")}</option>
              <option value="active">{t("radar.watches.status.active")}</option>
              <option value="paused">{t("radar.watches.status.paused")}</option>
              <option value="archived">{t("radar.watches.status.archived")}</option>
            </Select>
          </div>
        </div>

        {formOpen ? (
          <div className="hb-radar-section hb-radar-form">
            <h2 className="hb-radar-section__title">
              {draft.id ? t("radar.watches.editTitle") : t("radar.watches.newTitle")}
            </h2>
            <p className="hb-radar-section__hint">
              <span className="hb-field__required" aria-hidden="true">*</span> {t("radar.watches.requiredHint")}
            </p>
            <Textarea
              name="radar-watch-terms"
              label={t("radar.watches.terms")}
              hint={t("radar.watches.termsHint")}
              rows={4}
              required
              value={draft.terms}
              onChange={(e) => setDraft((d) => ({ ...d, terms: e.target.value }))}
              placeholder={t("radar.watches.termsPh")}
            />
            {splitTerms(draft.terms).some((term) => !isThreadsSearchable(term)) ? (
              <p className="hb-banner-ok" role="status">
                {t("radar.watches.threadsWarn")}
              </p>
            ) : null}

            <p className="hb-radar-section__hint">{t("radar.watches.regionsHint")}</p>
            <div className="hb-radar-area-grid">
              {SERVICE_AREAS.map((area) => {
                const on = draft.regions.includes(area.code);
                return (
                  <button
                    key={area.code}
                    type="button"
                    aria-pressed={on}
                    className={`hb-radar-chip${on ? " is-active" : ""}`}
                    onClick={() =>
                      setDraft((d) => ({
                        ...d,
                        regions: on
                          ? d.regions.filter((c) => c !== area.code)
                          : [...d.regions, area.code],
                      }))
                    }
                  >
                    {area.label}
                  </button>
                );
              })}
            </div>

            {draft.id ? null : (
              <label className="hb-radar-check">
                <input
                  type="checkbox"
                  checked={draft.enabled}
                  onChange={(e) => setDraft((d) => ({ ...d, enabled: e.target.checked }))}
                />
                <span>{t("radar.watches.enableNow")}</span>
              </label>
            )}

            <Button type="button" variant="ghost" aria-expanded={advancedOpen} onClick={() => setAdvancedOpen((v) => !v)}>
              {advancedOpen ? t("radar.watches.hideAdvanced") : t("radar.watches.showAdvanced")}
            </Button>

            {advancedOpen ? (
              <>
                {productContextAvailable && (!draft.id || rebinding) ? (
                  <>
                    <p className="hb-radar-section__hint">{t("radar.watches.optionalBrandHint")}</p>
                    <ProductWatchForm
                      brands={brands}
                      products={products}
                      brandId={draft.brandId}
                      productId={draft.productId}
                      onBrandChange={(brandId) => setDraft((d) => ({ ...d, brandId, productId: "" }))}
                      onProductChange={(productId) => setDraft((d) => ({ ...d, productId, terms: d.id ? d.terms : "" }))}
                    />
                  </>
                ) : null}
                {productContextAvailable && draft.productId ? (() => {
                  const selectedProduct = products.find((product) => product.id === draft.productId);
                  return selectedProduct ? (
                    <DemandMapEditor
                      product={selectedProduct}
                      map={demandMap}
                      loading={demandMapLoading}
                      error={demandMapError}
                      saving={busy === "demand-map"}
                      onDraftChange={setPendingDemandMapPatch}
                      onSave={(patch) => {
                        void persistDemandMap(patch);
                      }}
                    />
                  ) : null;
                })() : null}
                <Textarea
                  name="radar-watch-exclude"
                  label={t("radar.watches.excludeTerms")}
                  hint={t("radar.watches.excludeHint")}
                  rows={3}
                  value={draft.excludeTerms}
                  onChange={(e) => setDraft((d) => ({ ...d, excludeTerms: e.target.value }))}
                  placeholder={t("radar.watches.excludePh")}
                />
                <WatchSuggestPanel
                  onAdopt={adopt}
                  onAdoptAll={adoptAll}
                  onAdoptQueries={adoptQueries}
                  adopted={[...splitTerms(draft.terms), ...splitTerms(draft.excludeTerms)]}
                  context={draft.brandId && draft.productId ? { brand_id: draft.brandId, product_id: draft.productId } : undefined}
                  demandMap={pendingDemandMapPatch && demandMap ? { ...demandMap, ...pendingDemandMapPatch } : demandMap ?? undefined}
                />
              </>
            ) : null}

            <div className="hb-radar-actions">
              {draft.id && rebinding ? (
                <Button type="button" variant="secondary" onClick={() => void assignProduct()} disabled={busy === "assign" || !draft.brandId || !draft.productId}>
                  {busy === "assign" ? t("radar.watches.assigning") : t("radar.watches.assign")}
                </Button>
              ) : null}
              <Button type="button" onClick={() => void save()} disabled={busy === "save"}>
                {busy === "save" ? t("common.saving") : t("common.save")}
              </Button>
              <Button type="button" variant="ghost" onClick={() => setFormOpen(false)}>
                {t("common.cancel")}
              </Button>
            </div>
          </div>
        ) : null}

        {loading ? (
          <p className="text-muted">{t("common.loading")}</p>
        ) : watches.length === 0 && !error ? (
          <EmptyState
            title={statusFilter ? t("radar.watches.emptyFiltered") : t("radar.watches.empty")}
            description={statusFilter ? undefined : t("radar.watches.emptyHint")}
          />
        ) : watches.length === 0 ? null : (
          <>
            <div className="hb-radar-watch-list">
              {watches.map((w) => (
                <article
                  key={w.id}
                  className={`hb-radar-watch${w.status === "paused" ? " hb-radar-watch--paused" : ""}`}
                >
                  <div className="hb-radar-watch__terms">
                    {w.terms.map((term) => (
                      <span className="hb-radar-chip" key={`t-${term}`}>
                        {term}
                      </span>
                    ))}
                    {w.exclude_terms.map((term) => (
                      <span className="hb-radar-chip hb-radar-chip--exclude" key={`x-${term}`}>
                        −{term}
                      </span>
                    ))}
                  </div>
                  <div className="hb-radar-watch__meta">
                    <Badge tone={statusTone(w.status)}>{t(`radar.watches.status.${w.status}`)}</Badge>
                    {w.context_mode === "product" && w.product_label_snapshot ? (
                      <Badge tone="brand">{w.brand_name_snapshot} · {w.product_label_snapshot}</Badge>
                    ) : null}
                    {w.pause_reason === "product_unavailable" || w.pause_reason === "brand_unavailable" ? (
                      <Badge tone="danger">{t("radar.watches.productGone")}</Badge>
                    ) : null}
                    <span>
                      {w.regions.length
                        ? w.regions.join("、")
                        : t("radar.watches.regionsFromProfile")}
                    </span>
                    <span>
                      {w.last_swept_at
                        ? t("radar.watches.lastSwept", { at: formatLocalDateTime(w.last_swept_at) })
                        : t("radar.watches.neverSwept")}
                    </span>
                  </div>
                  {w.status === "archived" ? (
                    <div className="hb-radar-actions">
                      <Button
                        type="button"
                        variant="danger"
                        disabled={busy === `delete-${w.id}`}
                        onClick={() => {
                          if (!window.confirm(t("radar.watches.confirmDeleteArchived"))) return;
                          void run(
                            `delete-${w.id}`,
                            async () => {
                              await repos.radar.deleteArchivedWatch(w.id);
                            },
                            t("radar.watches.deletedArchived"),
                          );
                        }}
                      >
                        {busy === `delete-${w.id}` ? t("radar.watches.deletingArchived") : t("radar.watches.deleteArchived")}
                      </Button>
                    </div>
                  ) : (
                    <div className="hb-radar-actions">
                      <Button type="button" variant="ghost" onClick={() => openEdit(w)}>
                        {t("common.edit")}
                      </Button>
                      {w.status === "active" ? (
                        <>
                          <Button
                            type="button"
                            variant="secondary"
                            disabled={busy === `sweep-${w.id}`}
                            onClick={() =>
                              void (async () => {
                                setBusy(`sweep-${w.id}`);
                                setError("");
                                setMessage("");
                                try {
                                  const res = await repos.radar.triggerWatchSweep(w.id);
                                  await load();
                                  if (res.sweep_id && typeof repos.radar.listSweeps === "function") {
                                    const sweeps = await repos.radar.listSweeps(1, 1, w.id).catch(() => null);
                                    setLastSweep(sweeps?.list[0] ?? null);
                                  }
                                  setMessage(
                                    res.job_id
                                      ? t("radar.watches.sweepStarted", {
                                          job: res.job_id.slice(0, 8),
                                        })
                                      : t("radar.watches.sweepQueued"),
                                  );
                                } catch (e) {
                                  setError(formatError(e));
                                } finally {
                                  setBusy("");
                                }
                              })()
                            }
                          >
                            {t("radar.watches.sweepNow")}
                          </Button>
                          <Button
                            type="button"
                            variant="ghost"
                            disabled={busy === `pause-${w.id}`}
                            onClick={() =>
                              void run(
                                `pause-${w.id}`,
                                async () => {
                                  await repos.radar.pauseWatch(w.id);
                                },
                                t("radar.watches.paused"),
                              )
                            }
                          >
                            {t("radar.watches.pause")}
                          </Button>
                        </>
                      ) : (
                        <Button
                          type="button"
                          variant="ghost"
                          disabled={busy === `resume-${w.id}`}
                          onClick={() =>
                            void run(
                              `resume-${w.id}`,
                              async () => {
                                await repos.radar.resumeWatch(w.id);
                              },
                              t("radar.watches.resumed"),
                            )
                          }
                        >
                          {t("radar.watches.resume")}
                        </Button>
                      )}
                      <Button
                        type="button"
                        variant="danger"
                        disabled={busy === `archive-${w.id}`}
                        onClick={() => {
                          if (!window.confirm(t("radar.watches.confirmArchive"))) return;
                          void run(
                            `archive-${w.id}`,
                            async () => {
                              await repos.radar.archiveWatch(w.id);
                            },
                            t("radar.watches.archived"),
                          );
                        }}
                      >
                        {t("radar.watches.archive")}
                      </Button>
                    </div>
                  )}
                </article>
              ))}
            </div>
            <Pager total={total} page={page} pageSize={PAGE_SIZE} onPageChange={setPage} />
          </>
        )}
      </section>
    </>
  );
}
