import { useCallback, useEffect, useState, type ReactNode } from "react";
import { Link, useSearchParams } from "react-router-dom";
import { PageHeader } from "../components/layout/PageHeader";
import { OpportunityInboxCard } from "../components/radar/OpportunityInboxCard";
import { OpportunityDetailDrawer } from "../components/radar/OpportunityDetailDrawer";
import { SweepFunnelSummary } from "../components/radar/SweepFunnelSummary";
import { Button, EmptyState, Select } from "../components/ui";
import { useRepos } from "../data/DataContext";
import type { Brand, BrandProduct, JobStatus, Opportunity, OpportunityRemovalReason, OpportunityReviewState, OpportunityTimeScope, RadarSweep, RadarToday, RadarWatch } from "../domain/types";
import { useFormatApiError } from "../lib/apiErrors";
import { formatLocalDateTime } from "../lib/time";
import "../styles/radar.css";

const PAGE_SIZE = 20;

function normalizeSort(value: string | null): string {
  if (value === "posted") return "newest";
  if (value === "score") return "recommended";
  return value || "recommended";
}

export function RadarOpportunitiesPage() {
  const repos = useRepos();
  const formatError = useFormatApiError();
  const [params, setParams] = useSearchParams();
  const [list, setList] = useState<Opportunity[]>([]);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(() => Math.max(1, Number(params.get("page")) || 1));
  const [band, setBand] = useState(params.get("band") || "");
  const [state, setState] = useState(params.get("match_state") || "");
  const [brandId, setBrandId] = useState(params.get("brand_id") || "");
  const [productId, setProductId] = useState(params.get("product_id") || "");
  const [sort, setSort] = useState(() => normalizeSort(params.get("sort")));
  const [reviewState, setReviewState] = useState<OpportunityReviewState>((params.get("review_state") as OpportunityReviewState) || "pending");
  const [timeScope, setTimeScope] = useState<OpportunityTimeScope>((params.get("time_scope") as OpportunityTimeScope) || "today");
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [notice, setNotice] = useState<{ text: string; contactId?: string } | null>(null);
  const [brands, setBrands] = useState<Brand[]>([]);
  const [products, setProducts] = useState<BrandProduct[]>([]);
  const [selected, setSelected] = useState<Opportunity | null>(null);
  const [busyId, setBusyId] = useState<string | null>(null);
  const [sweeping, setSweeping] = useState(false);
  const [lastSweep, setLastSweep] = useState<RadarSweep | null>(null);
  const [watches, setWatches] = useState<RadarWatch[]>([]);
  const [activeCount, setActiveCount] = useState(0);
  const [watchTotal, setWatchTotal] = useState(0);
  const [todayMeta, setTodayMeta] = useState<RadarToday | null>(null);
  const [advancedOpen, setAdvancedOpen] = useState(() => Boolean(params.get("band") || params.get("match_state") || params.get("brand_id") || params.get("product_id")));
  const scout = (repos as unknown as { scout?: { listBrands: () => Promise<Brand[]>; listProducts: (id: string) => Promise<BrandProduct[]> } }).scout;

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

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

  const loadPatrol = useCallback(async () => {
    const [watchRes, today] = await Promise.all([
      repos.radar.listWatches(1, 50).catch(() => null),
      repos.radar.getToday().catch(() => null),
    ]);
    if (watchRes) {
      setWatches(watchRes.list);
      setActiveCount(watchRes.active_count);
      setWatchTotal(watchRes.total);
    }
    setTodayMeta(today);
    if (typeof repos.radar.listSweeps === "function") {
      const sweeps = await repos.radar.listSweeps(1, 1).catch(() => null);
      setLastSweep(sweeps?.list[0] ?? null);
    }
  }, [repos.radar]);

  const load = useCallback(async () => {
    setLoading(true);
    const query = (scope: OpportunityTimeScope, review: OpportunityReviewState = reviewState) =>
      repos.radar.listOpportunities({
        page: scope === timeScope ? page : 1,
        pageSize: PAGE_SIZE,
        band: band || undefined,
        match_state: state || undefined,
        brand_id: brandId || undefined,
        product_id: productId || undefined,
        review_state: review,
        time_scope: scope,
        sort,
      });
    try {
      let result = await query(timeScope);
      const canWiden = reviewState === "pending" && timeScope === "today" && !band && !state && !brandId && !productId;
      if (canWiden && result.total === 0) {
        const week = await query("7d");
        if (week.total > 0) {
          setTimeScope("7d");
          setPage(1);
          const next = new URLSearchParams(typeof window === "undefined" ? "" : window.location.search);
          next.set("time_scope", "7d");
          next.delete("page");
          setParams(next, { replace: true });
          result = week;
          setNotice({ text: `巡邏結果不是都在「今天發的文」。已改看近 7 天（${week.total} 筆）。任務上的判定／新建數字包含同一篇再命中，不一定全是新卡片。` });
        } else {
          const all = await query("all");
          if (all.total > 0) {
            setTimeScope("all");
            setPage(1);
            const next = new URLSearchParams(typeof window === "undefined" ? "" : window.location.search);
            next.set("time_scope", "all");
            next.delete("page");
            setParams(next, { replace: true });
            result = all;
            setNotice({ text: `近 7 天沒有待處理結果，已改看全部（${all.total} 筆）。` });
          } else {
            const seen = await query("7d", "completed");
            if (seen.total > 0) {
              setNotice({ text: `這輪判定到的 ${seen.total} 筆已在「已看過」，所以「新找到」是空的。任務數字含再次命中的舊文。` });
            }
          }
        }
      }
      setList(result.list);
      setTotal(result.total);
      setError("");
    } catch (e) {
      setError(formatError(e));
    } finally {
      setLoading(false);
    }
  }, [repos.radar, page, band, state, brandId, productId, sort, reviewState, timeScope, formatError, setParams]);

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

  async function updateReviewState(
    opportunity: Opportunity,
    patch: { state: OpportunityReviewState; removal_reason?: OpportunityRemovalReason; removal_note?: string },
    successText: string,
  ) {
    setBusyId(opportunity.id);
    setNotice(null);
    try {
      const updated = await repos.radar.updateOpportunityReviewState(opportunity.id, patch);
      setSelected((current) => current?.id === updated.id ? null : current);
      await load();
      setError("");
      setNotice({ text: successText });
    } catch (e) {
      setError(formatError(e));
    } finally {
      setBusyId(null);
    }
  }

  async function acceptOpportunity(opportunity: Opportunity) {
    setBusyId(opportunity.id);
    setNotice(null);
    try {
      const result = await repos.radar.acceptOpportunity(opportunity.id);
      setSelected((current) => current?.id === opportunity.id ? null : current);
      await load();
      setError("");
      setNotice({
        text: "已加入名單。這步是可選的，之後要追蹤再去名單即可。",
        contactId: result.contact_id,
      });
    } catch (e) {
      setError(formatError(e));
    } finally {
      setBusyId(null);
    }
  }

  async function waitForJob(jobId: string): Promise<{ status: JobStatus; progress_summary: string; error: string; timedOut: boolean }> {
    const deadline = Date.now() + 120_000;
    let last: { status: JobStatus; progress_summary: string; error: string } = {
      status: "queued",
      progress_summary: "立即巡邏已排程 · 等待 worker",
      error: "",
    };
    while (Date.now() < deadline) {
      const job = await repos.jobs.get(jobId);
      if (job) {
        last = { status: job.status, progress_summary: job.progress_summary, error: job.error || "" };
        if (job.status === "succeeded" || job.status === "failed" || job.status === "cancelled") {
          return { ...last, timedOut: false };
        }
      }
      await new Promise((resolve) => window.setTimeout(resolve, 1500));
    }
    return { ...last, timedOut: true };
  }

  async function runNow() {
    const active = watches.filter((watch) => watch.status === "active");
    if (!active.length) {
      setError("沒有開著的每日巡邏。先設定要巡的產品與關鍵字，或恢復一組訂閱。");
      return;
    }
    setSweeping(true);
    setError("");
    setNotice(null);
    try {
      const jobIds: string[] = [];
      for (const watch of active) {
        const res = await repos.radar.triggerWatchSweep(watch.id);
        if (res.job_id) jobIds.push(res.job_id);
      }
      if (!jobIds.length) {
        setError("沒有排到巡邏任務。");
        return;
      }
      setNotice({ text: "立即巡邏進行中… 跑完才會把痛點列在下面。" });
      // All active watches were queued together, so observe them together too.
      // Waiting serially made the page look frozen for up to 120s per watch.
      const finished = await Promise.all(jobIds.map((id) => waitForJob(id)));
      const failed = finished.find((job) => job.status === "failed");
      const cancelled = finished.find((job) => job.status === "cancelled");
      const running = finished.filter((job) => job.timedOut || job.status === "pending" || job.status === "queued" || job.status === "running" || job.status === "cancel_requested");
      const terminalError = failed
        ? failed.progress_summary || failed.error || "巡邏失敗。"
        : cancelled
          ? "巡邏已取消，不會誤顯示為已完成。可再按一次立即巡邏。"
          : "";
      if (typeof repos.radar.listSweeps === "function") {
        const sweeps = await repos.radar.listSweeps(1, 1).catch(() => null);
        setLastSweep(sweeps?.list[0] ?? null);
      }
      await Promise.all([load(), loadPatrol()]);
      const summary = finished.map((job) => job.progress_summary).filter(Boolean).join(" ");
      // load() clears stale list errors on a successful refresh. Re-apply the
      // job outcome afterwards so the actual patrol failure stays visible.
      if (terminalError) {
        setError(terminalError);
        setNotice(null);
      } else if (running.length > 0) {
        setNotice({ text: `已排入 ${jobIds.length} 組巡邏，目前仍在後台執行。可先離開這頁，完成後結果會留在這裡。` });
      } else {
        setNotice({
          text: summary.includes("新建")
            ? `${summary} 不是今天發的文也會留在下面。`
            : "這一輪巡邏跑完了。找到的痛點會留在下面。",
        });
      }
    } catch (e) {
      setError(formatError(e));
    } finally {
      setSweeping(false);
    }
  }

  function writeParams(changes: Record<string, string>) {
    const next = new URLSearchParams(params);
    for (const [name, value] of Object.entries(changes)) {
      if (value) next.set(name, value);
      else next.delete(name);
    }
    setParams(next, { replace: true });
  }

  function resetFilters() {
    setBand("");
    setState("");
    setBrandId("");
    setProductId("");
    setSort("recommended");
    setTimeScope("today");
    setAdvancedOpen(false);
    setPage(1);
    const next = new URLSearchParams();
    if (reviewState !== "pending") next.set("review_state", reviewState);
    setParams(next, { replace: true });
  }

  const lastSweptAt = Math.max(
    todayMeta?.last_swept_at ?? 0,
    ...watches.map((watch) => watch.last_swept_at ?? 0),
  );
  const scheduledOn = activeCount > 0;
  const advancedFilterCount = [band, state, brandId, productId].filter(Boolean).length;
  const filtered = Boolean(advancedFilterCount || sort !== "recommended" || timeScope !== "today");
  const pageCount = Math.max(1, Math.ceil(total / PAGE_SIZE));

  function emptyCopy(): { title: string; description: string; action?: ReactNode } {
    if (filtered) {
      return {
        title: reviewState === "pending" ? "這個篩選下沒有結果" : reviewState === "completed" ? "目前沒有已看過的結果" : "目前沒有已丟掉的結果",
        description: "清除篩選或改看其他時間範圍。巡邏剛跑完的結果也可能在「近 7 天」或「全部」。",
        action: <Button type="button" variant="ghost" onClick={resetFilters}>清除篩選</Button>,
      };
    }
    if (reviewState === "completed") {
      return { title: "還沒有已看過的結果", description: "切回「新找到」繼續看巡邏到的痛點。" };
    }
    if (reviewState === "removed") {
      return { title: "還沒有丟掉的結果", description: "切回「新找到」繼續看巡邏到的痛點。" };
    }
    if (watchTotal === 0) {
      return {
        title: "還沒設定巡邏",
        description: "先選產品與客人會搜的關鍵字。設好後可立即巡邏，每日定時巡邏也會接著跑。",
        action: <Link className="hb-btn hb-btn--secondary" to="/app/radar/watches">設定巡邏</Link>,
      };
    }
    if (!scheduledOn) {
      return {
        title: "每日定時巡邏關著",
        description: "立即巡邏與每日定時都還在這個頁面。恢復至少一組訂閱後，兩個都能用；關掉其中一個不會藏掉另一個。",
        action: <Link className="hb-btn hb-btn--secondary" to="/app/radar/watches">打開每日定時巡邏</Link>,
      };
    }
    if (!lastSweptAt) {
      return {
        title: "還沒巡邏過",
        description: "每日定時巡邏已開著，也可現在按「立即巡邏」。不是空白收件匣，只是第一輪還沒跑完。",
        action: <Button type="button" onClick={() => void runNow()} disabled={sweeping}>{sweeping ? "巡邏中…" : "立即巡邏"}</Button>,
      };
    }
    if (todayMeta?.empty_reason === "sweep_failed") {
      const crawlerDead = /crawler session|Chrome crawler|Chrome 登入已過期/i.test(
        `${todayMeta.empty_hint || ""} ${lastSweep?.failed_reason || ""}`,
      );
      return {
        title: "上一輪巡邏沒跑完",
        description: todayMeta.empty_hint || "巡邏失敗。可再按立即巡邏，或改看近 7 天／全部。",
        action: (
          <>
            {crawlerDead ? <Link className="hb-btn hb-btn--secondary" to="/app/settings">重新同步 Chrome</Link> : null}
            <Button type="button" onClick={() => void runNow()} disabled={sweeping}>{sweeping ? "巡邏中…" : "再巡一次"}</Button>
          </>
        ),
      };
    }
    if (lastSweep && lastSweep.hit_count === 0) {
      return {
        title: "搜尋沒撈到貼文",
        description: "門檻前就空了：關鍵字太長、太產品名、或 Threads 查無結果。改成客人會打的 2–4 字痛點詞再巡。",
        action: <Link className="hb-btn hb-btn--secondary" to="/app/radar/watches">改關鍵字</Link>,
      };
    }
    return {
      title: "這輪有巡，但沒找到符合的痛點",
      description: lastSweep
        ? `搜尋命中 ${lastSweep.hit_count}、判定 ${lastSweep.judged_count}、新建 ${lastSweep.created_count}。不是今天發的文可改看近 7 天／全部。`
        : "新文章或產品對得上的需求會出現在這裡。也可改看「近 7 天」或「全部」，或調整要巡的關鍵字。",
      action: <Button type="button" variant="ghost" onClick={() => { setTimeScope("7d"); setPage(1); writeParams({ time_scope: "7d", page: "" }); }}>看近 7 天</Button>,
    };
  }

  const empty = emptyCopy();

  return (
    <>
      <PageHeader title="商機" />

      <section className="hb-radar-patrol" data-testid="radar-patrol-desk" aria-label="巡邏狀態">
        <div className="hb-radar-patrol__status">
          <p>
            <strong>{scheduledOn ? "每日定時巡邏：開著" : "每日定時巡邏：關著"}</strong>
            <span>每天台北 06:00 自動巡一輪。關掉立即巡邏不會停每日定時。</span>
          </p>
          <p>
            <strong>{lastSweptAt ? `上次巡邏：${formatLocalDateTime(lastSweptAt)}` : "還沒巡邏過"}</strong>
            <span>{scheduledOn ? `啟用中 ${activeCount} 組` : watchTotal ? "訂閱都暫停了，立即巡邏也需要至少一組開著" : "還沒設定要巡的產品與關鍵字"}</span>
          </p>
        </div>
        <div className="hb-radar-patrol__actions">
          <Button type="button" onClick={() => void runNow()} disabled={sweeping || !scheduledOn}>
            {sweeping ? "巡邏中…" : "立即巡邏"}
          </Button>
          <Link className="hb-btn hb-btn--ghost" to="/app/radar/watches">設定巡邏</Link>
          <small>Chrome 失效時會備援改用 API；只有 provider 成功回傳才計點。</small>
        </div>
      </section>
      {lastSweep ? <SweepFunnelSummary sweep={lastSweep} /> : null}

      <section className="hb-radar-intro">
        <div>
          <strong>巡邏到痛點就看這裡</strong>
          <p>先讀「為什麼推薦」，留下或丟掉即可。加入名單是可選的，不是看結果的必要步驟。</p>
        </div>
      </section>

      <section className="hb-radar-filter-panel" aria-label="商機結果">
        <div className="hb-radar-filter-panel__head">
          <div className="hb-radar-inbox-tabs" role="tablist" aria-label="結果狀態">
            {(["pending", "completed", "removed"] as OpportunityReviewState[]).map((value) => (
              <Button key={value} type="button" variant={reviewState === value ? "primary" : "ghost"} aria-pressed={reviewState === value} onClick={() => {
                setReviewState(value); setPage(1); writeParams({ review_state: value === "pending" ? "" : value, page: "" });
              }}>
                {value === "pending" ? "新找到" : value === "completed" ? "已看過" : "已丟掉"}
              </Button>
            ))}
            <span>共 {total} 筆</span>
          </div>
          {filtered ? <Button type="button" variant="ghost" onClick={resetFilters}>清除篩選</Button> : <span>預設先看今天剛巡到的結果</span>}
        </div>
        <div className="hb-radar-inbox-essential-filters">
          <Select name="all-time-scope" label="看哪段時間" value={timeScope} onChange={(e) => {
            const value = e.target.value as OpportunityTimeScope;
            setTimeScope(value); setPage(1); writeParams({ time_scope: value === "today" ? "" : value, page: "" });
          }}>
            <option value="today">今天</option><option value="7d">近 7 天</option><option value="all">全部</option>
          </Select>
          <Select name="all-sort" label="先看哪些" value={sort} onChange={(e) => {
            setSort(e.target.value); setPage(1); writeParams({ sort: e.target.value === "recommended" ? "" : e.target.value, page: "" });
          }}>
            <option value="recommended">最對得上產品</option><option value="newest">最新貼文</option><option value="oldest">最舊貼文</option><option value="product_fit">最符合產品</option><option value="demand_intent">需求最明確</option>
          </Select>
          <Button type="button" variant="ghost" aria-expanded={advancedOpen} onClick={() => setAdvancedOpen((value) => !value)}>
            {advancedOpen ? "收起更多篩選" : `更多篩選${advancedFilterCount ? `（${advancedFilterCount}）` : ""}`}
          </Button>
        </div>
        {advancedOpen ? <div className="hb-radar-filter-grid hb-radar-filter-grid--all" aria-label="更多篩選">
          {brands.length ? (
            <Select name="all-brand" label="品牌" value={brandId} onChange={(e) => {
              const value = e.target.value;
              setBrandId(value); setProductId(""); setPage(1);
              writeParams({ brand_id: value, product_id: "", page: "" });
            }}>
              <option value="">全部品牌</option>
              {brands.map((b) => <option key={b.id} value={b.id}>{b.display_name}</option>)}
            </Select>
          ) : null}
          {products.length ? (
            <Select name="all-product" label="產品" value={productId} onChange={(e) => {
              setProductId(e.target.value); setPage(1);
              writeParams({ product_id: e.target.value, page: "" });
            }}>
              <option value="">全部產品</option>
              {products.map((p) => <option key={p.id} value={p.id}>{p.label}</option>)}
            </Select>
          ) : null}
          <Select name="all-band" label="商機意向" value={band} onChange={(e) => {
            setBand(e.target.value); setPage(1); writeParams({ band: e.target.value, page: "" });
          }}>
            <option value="">全部意向</option><option value="high">高意向</option><option value="mid">中意向</option><option value="low">低意向</option>
          </Select>
          <Select name="all-state" label="產品匹配" value={state} onChange={(e) => {
            setState(e.target.value); setPage(1); writeParams({ match_state: e.target.value, page: "" });
          }}>
            <option value="">全部狀態</option><option value="eligible">可跟進</option><option value="weak">弱適配</option><option value="excluded">已排除</option><option value="generic">未指定產品</option><option value="stale">超過 14 天</option>
          </Select>
        </div> : null}
      </section>

      {error ? <p className="hb-banner-error" role="alert">{error}</p> : null}
      {notice ? <div className="hb-banner-ok" role="status"><span>{notice.text}</span>{notice.contactId ? <Link to={`/app/crm?contact=${encodeURIComponent(notice.contactId)}`}>前往名單</Link> : null}</div> : null}
      {loading ? <p className="hb-radar-section__hint" role="status">正在整理巡邏結果…</p> : null}
      {!loading && !error && !list.length ? (
        <EmptyState title={empty.title} description={empty.description} action={empty.action} />
      ) : null}

      {!error ? (
        <section className="hb-radar-page hb-radar-all-results">
          {list.map((o) => <OpportunityInboxCard
            key={o.id}
            opportunity={o}
            busy={busyId === o.id}
            onOpen={setSelected}
            onAccept={(item) => void acceptOpportunity(item)}
            onComplete={(item) => void updateReviewState(item, { state: "completed" }, "已留下。沒有建立名單。")}
            onRemove={(item, input) => void updateReviewState(item, { state: "removed", removal_reason: input.reason, removal_note: input.note }, "已丟掉。可從「已丟掉」還原。")}
            onRestore={(item) => void updateReviewState(item, { state: item.previous_review_state === "completed" ? "completed" : "pending" }, "已還原。")}
          />)}
          {total > PAGE_SIZE ? (
            <div className="hb-radar-pager">
              <Button variant="ghost" disabled={page <= 1} onClick={() => { const next = page - 1; setPage(next); writeParams({ page: String(next) }); }}>上一頁</Button>
              <span>第 {page} 頁／共 {pageCount} 頁</span>
              <Button variant="ghost" disabled={page >= pageCount} onClick={() => { const next = page + 1; setPage(next); writeParams({ page: String(next) }); }}>下一頁</Button>
            </div>
          ) : null}
        </section>
      ) : null}
      {selected ? <OpportunityDetailDrawer
        opportunity={selected}
        busy={busyId === selected.id}
        onClose={() => setSelected(null)}
        onAccept={(item) => void acceptOpportunity(item)}
        onComplete={(item) => void updateReviewState(item, { state: "completed" }, "已留下。沒有建立名單。")}
      /> : null}
    </>
  );
}
