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 { useI18n } from "../i18n/I18nContext";
import { useFormatApiError } from "../lib/apiErrors";
import { sanitizeReviewCopy } from "../lib/reviewCopy";
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 { t, locale } = useI18n();
  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: t("radar.inbox.msg.widened7d", { n: 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: t("radar.inbox.msg.widenedAll", { n: all.total }) });
          } else {
            const seen = await query("7d", "completed");
            if (seen.total > 0) {
              setNotice({ text: t("radar.inbox.msg.alreadyReviewed", { n: 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, t]);

  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: t("radar.inbox.msg.accepted"),
        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: t("radar.inbox.msg.waitingWorker"),
      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(t("radar.inbox.err.noActive"));
      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(t("radar.inbox.err.noJob"));
        return;
      }
      setNotice({ text: t("radar.inbox.msg.running") });
      // 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 || t("radar.inbox.err.failed")
        : cancelled
          ? t("radar.inbox.err.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: t("radar.inbox.msg.queuedN", { n: jobIds.length }) });
      } else {
        setNotice({
          text: /新建|created/i.test(summary)
            ? t("radar.inbox.msg.doneWithSummary", { summary })
            : t("radar.inbox.msg.done"),
        });
      }
    } 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: t(
          reviewState === "pending"
            ? "radar.inbox.empty.filteredPending"
            : reviewState === "completed"
              ? "radar.inbox.empty.filteredCompleted"
              : "radar.inbox.empty.filteredRemoved",
        ),
        description: t("radar.inbox.empty.filteredHint"),
        action: <Button type="button" variant="ghost" onClick={resetFilters}>{t("radar.inbox.clearFilters")}</Button>,
      };
    }
    if (reviewState === "completed") {
      return { title: t("radar.inbox.empty.noCompleted"), description: t("radar.inbox.empty.noCompletedHint") };
    }
    if (reviewState === "removed") {
      return { title: t("radar.inbox.empty.noRemoved"), description: t("radar.inbox.empty.noRemovedHint") };
    }
    if (watchTotal === 0) {
      return {
        title: t("radar.inbox.empty.noWatchesTitle"),
        description: t("radar.inbox.empty.noWatchesHint"),
        action: <Link className="hb-btn hb-btn--secondary" to="/app/radar/watches">{t("radar.inbox.setupWatches")}</Link>,
      };
    }
    if (!scheduledOn) {
      return {
        title: t("radar.inbox.empty.pausedTitle"),
        description: t("radar.inbox.empty.pausedHint"),
        action: <Link className="hb-btn hb-btn--secondary" to="/app/radar/watches">{t("radar.inbox.empty.openSchedule")}</Link>,
      };
    }
    if (!lastSweptAt) {
      return {
        title: t("radar.inbox.empty.neverTitle"),
        description: t("radar.inbox.empty.neverHint"),
        action: <Button type="button" onClick={() => void runNow()} disabled={sweeping}>{sweeping ? t("radar.inbox.sweeping") : t("radar.inbox.sweepNow")}</Button>,
      };
    }
    if (todayMeta?.empty_reason === "sweep_failed") {
      const crawlerDead = /crawler session|Chrome crawler|Chrome 登入已過期/i.test(
        `${todayMeta.empty_hint || ""} ${lastSweep?.failed_reason || ""}`,
      );
      return {
        title: t("radar.inbox.empty.failedTitle"),
        description: sanitizeReviewCopy(
          todayMeta.empty_hint || t("radar.empty.sweepFailedHint"),
          locale,
        ),
        action: (
          <>
            {crawlerDead ? (
              <Link className="hb-btn hb-btn--secondary" to="/app/settings">
                {t("radar.reconnectSearch")}
              </Link>
            ) : null}
            <Button type="button" onClick={() => void runNow()} disabled={sweeping}>{sweeping ? t("radar.inbox.sweeping") : t("radar.inbox.sweepAgain")}</Button>
          </>
        ),
      };
    }
    if (lastSweep && lastSweep.hit_count === 0) {
      return {
        title: t("radar.inbox.empty.noHitsTitle"),
        description: t("radar.inbox.empty.noHitsHint"),
        action: <Link className="hb-btn hb-btn--secondary" to="/app/radar/watches">{t("radar.inbox.empty.editTerms")}</Link>,
      };
    }
    return {
      title: t("radar.inbox.empty.noFitTitle"),
      description: lastSweep
        ? t("radar.inbox.empty.noFitStats", { hits: lastSweep.hit_count, judged: lastSweep.judged_count, created: lastSweep.created_count })
        : t("radar.inbox.empty.noFitHint"),
      action: <Button type="button" variant="ghost" onClick={() => { setTimeScope("7d"); setPage(1); writeParams({ time_scope: "7d", page: "" }); }}>{t("radar.inbox.see7d")}</Button>,
    };
  }

  const empty = emptyCopy();

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

      <section className="hb-radar-patrol" data-testid="radar-patrol-desk" aria-label={t("radar.inbox.patrolAria")}>
        <div className="hb-radar-patrol__status">
          <p>
            <strong>{scheduledOn ? t("radar.inbox.scheduledOn") : t("radar.inbox.scheduledOff")}</strong>
            <span>{t("radar.inbox.scheduleHint")}</span>
          </p>
          <p>
            <strong>{lastSweptAt ? t("radar.inbox.lastSweep", { time: formatLocalDateTime(lastSweptAt) }) : t("radar.inbox.neverSwept")}</strong>
            <span>{scheduledOn ? t("radar.inbox.activeWatches", { n: activeCount }) : watchTotal ? t("radar.inbox.allPaused") : t("radar.inbox.noWatches")}</span>
          </p>
        </div>
        <div className="hb-radar-patrol__actions">
          <Button type="button" onClick={() => void runNow()} disabled={sweeping || !scheduledOn}>
            {sweeping ? t("radar.inbox.sweeping") : t("radar.inbox.sweepNow")}
          </Button>
          <Link className="hb-btn hb-btn--ghost" to="/app/radar/watches">{t("radar.inbox.setupWatches")}</Link>
          <small>{t("radar.patrol.searchFallback")}</small>
        </div>
      </section>
      {lastSweep ? <SweepFunnelSummary sweep={lastSweep} /> : null}

      <section className="hb-radar-intro">
        <div>
          <strong>{t("radar.inbox.introTitle")}</strong>
          <p>{t("radar.inbox.introBody")}</p>
        </div>
      </section>

      <section className="hb-radar-filter-panel" aria-label={t("radar.inbox.resultsAria")}>
        <div className="hb-radar-filter-panel__head">
          <div className="hb-radar-inbox-tabs" role="tablist" aria-label={t("radar.inbox.tabsAria")}>
            {(["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: "" });
              }}>
                {t(`radar.inbox.tab.${value}`)}
              </Button>
            ))}
            <span>{t("radar.inbox.total", { n: total })}</span>
          </div>
          {filtered ? <Button type="button" variant="ghost" onClick={resetFilters}>{t("radar.inbox.clearFilters")}</Button> : <span>{t("radar.inbox.defaultToday")}</span>}
        </div>
        <div className="hb-radar-inbox-essential-filters">
          <Select name="all-time-scope" label={t("radar.inbox.timeScope")} 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">{t("radar.inbox.time.today")}</option><option value="7d">{t("radar.inbox.time.7d")}</option><option value="all">{t("radar.inbox.time.all")}</option>
          </Select>
          <Select name="all-sort" label={t("radar.inbox.sort")} value={sort} onChange={(e) => {
            setSort(e.target.value); setPage(1); writeParams({ sort: e.target.value === "recommended" ? "" : e.target.value, page: "" });
          }}>
            <option value="recommended">{t("radar.inbox.sort.recommended")}</option><option value="newest">{t("radar.inbox.sort.newest")}</option><option value="oldest">{t("radar.inbox.sort.oldest")}</option><option value="product_fit">{t("radar.inbox.sort.productFit")}</option><option value="demand_intent">{t("radar.inbox.sort.demandIntent")}</option>
          </Select>
          <Button type="button" variant="ghost" aria-expanded={advancedOpen} onClick={() => setAdvancedOpen((value) => !value)}>
            {advancedOpen ? t("radar.inbox.hideFilters") : advancedFilterCount ? t("radar.inbox.moreFiltersN", { n: advancedFilterCount }) : t("radar.inbox.moreFilters")}
          </Button>
        </div>
        {advancedOpen ? <div className="hb-radar-filter-grid hb-radar-filter-grid--all" aria-label={t("radar.inbox.moreFiltersAria")}>
          {brands.length ? (
            <Select name="all-brand" label={t("radar.inbox.brand")} value={brandId} onChange={(e) => {
              const value = e.target.value;
              setBrandId(value); setProductId(""); setPage(1);
              writeParams({ brand_id: value, product_id: "", page: "" });
            }}>
              <option value="">{t("radar.inbox.allBrands")}</option>
              {brands.map((b) => <option key={b.id} value={b.id}>{b.display_name}</option>)}
            </Select>
          ) : null}
          {products.length ? (
            <Select name="all-product" label={t("radar.inbox.product")} value={productId} onChange={(e) => {
              setProductId(e.target.value); setPage(1);
              writeParams({ product_id: e.target.value, page: "" });
            }}>
              <option value="">{t("radar.inbox.allProducts")}</option>
              {products.map((p) => <option key={p.id} value={p.id}>{p.label}</option>)}
            </Select>
          ) : null}
          <Select name="all-band" label={t("radar.inbox.band")} value={band} onChange={(e) => {
            setBand(e.target.value); setPage(1); writeParams({ band: e.target.value, page: "" });
          }}>
            <option value="">{t("radar.inbox.allBands")}</option><option value="high">{t("radar.inbox.band.high")}</option><option value="mid">{t("radar.inbox.band.mid")}</option><option value="low">{t("radar.inbox.band.low")}</option>
          </Select>
          <Select name="all-state" label={t("radar.inbox.match")} value={state} onChange={(e) => {
            setState(e.target.value); setPage(1); writeParams({ match_state: e.target.value, page: "" });
          }}>
            <option value="">{t("radar.inbox.allStates")}</option><option value="eligible">{t("radar.inbox.state.eligible")}</option><option value="weak">{t("radar.inbox.state.weak")}</option><option value="excluded">{t("radar.inbox.state.excluded")}</option><option value="generic">{t("radar.inbox.state.generic")}</option><option value="stale">{t("radar.inbox.state.stale")}</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)}`}>{t("radar.inbox.goCrm")}</Link> : null}</div> : null}
      {loading ? <p className="hb-radar-section__hint" role="status">{t("radar.inbox.loading")}</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" }, t("radar.inbox.msg.kept"))}
            onRemove={(item, input) => void updateReviewState(item, { state: "removed", removal_reason: input.reason, removal_note: input.note }, t("radar.inbox.msg.removed"))}
            onRestore={(item) => void updateReviewState(item, { state: item.previous_review_state === "completed" ? "completed" : "pending" }, t("radar.inbox.msg.restored"))}
          />)}
          {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) }); }}>{t("radar.inbox.prevPage")}</Button>
              <span>{t("radar.inbox.pageOf", { page, pages: pageCount })}</span>
              <Button variant="ghost" disabled={page >= pageCount} onClick={() => { const next = page + 1; setPage(next); writeParams({ page: String(next) }); }}>{t("radar.inbox.nextPage")}</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" }, t("radar.inbox.msg.kept"))}
      /> : null}
    </>
  );
}
