import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { ApiError } from "../data/live/http";
import { KEYS } from "../data/mock/keys";
import type { Opportunity, RadarWatch } from "../domain/types";
import { I18nProvider } from "../i18n/I18nContext";
import { RadarOpportunitiesPage } from "./RadarOpportunitiesPage";

const backend = vi.hoisted(() => ({
  error: null as unknown,
  calls: [] as Array<Record<string, unknown>>,
  accepted: [] as string[],
  reviews: [] as Array<{ id: string; patch: Record<string, unknown> }>,
  sweeps: [] as string[],
  result: null as { list: Opportunity[]; total: number } | null,
  byScope: null as null | ((filter: Record<string, unknown>) => { list: Opportunity[]; total: number }),
  watches: [] as RadarWatch[],
  jobResult: null as null | ((id: string) => { status: "succeeded" | "failed" | "running"; progress_summary: string; error?: string }),
  lastSweptAt: Date.now() * 1e6,
}));

function opportunity(): Opportunity {
  return {
    id: "opp-archive",
    source: "threads",
    external_id: "shared-post",
    permalink: "https://www.threads.net/@buyer/post/shared-post",
    author_handle: "buyer",
    text: "正在找適合敏感肌的日常修護產品",
    posted_at: Date.now() * 1e6,
    status: "qualified",
    intent_score: 88,
    intent_band: "high",
    reasons: [
      { dimension: "authenticity", score: 30, reason: "真實求助" },
      { dimension: "intent", score: 30, reason: "有購買意圖" },
      { dimension: "region", score: 10, reason: "未知" },
      { dimension: "freshness", score: 15, reason: "新貼文" },
      { dimension: "fit", score: 3, reason: "適配" },
    ],
    region_match: "unknown",
    freshness_hours: 1,
    matched_terms: ["敏感肌"],
    primary_product_label: "舒緩精華",
    created_at: Date.now() * 1e6,
  };
}

vi.mock("../data/DataContext", () => ({
  useRepos: (() => {
    const repos = {
    scout: {
      async listBrands() { return [{ id: "b1", display_name: "澄光品牌", brief: "" }]; },
      async listProducts() { return [{ id: "p1", brand_id: "b1", label: "舒緩精華", product_context: "", match_tags: [], pain_points: [], provider_capability_terms: [], provider_exclude_terms: [], created_at: 1, updated_at: 1 }]; },
    },
    jobs: {
      async get(id: string) {
        const configured = backend.jobResult?.(id);
        return {
          id,
          template_type: "radar_sweep",
          status: configured?.status ?? "succeeded",
          progress_summary: configured?.progress_summary ?? "雷達巡檢完成 · 新建 1 · 再次命中 0 · 判定 1 · 截斷 0",
          error: configured?.error,
          progress_percent: 100,
          created_at: 1,
          updated_at: 1,
        };
      },
    },
    radar: {
      async listOpportunities(filter: Record<string, unknown>) {
        backend.calls.push(filter);
        if (backend.error) throw backend.error;
        if (backend.byScope) return backend.byScope(filter);
        return backend.result ?? { list: [opportunity()], total: 1 };
      },
      async listWatches() {
        return {
          list: backend.watches,
          total: backend.watches.length,
          active_count: backend.watches.filter((w) => w.status === "active").length,
          max_active: 5,
          profile_exists: true,
        };
      },
      async getToday() {
        return {
          stats: { total: 0, high: 0, mid: 0, low: 0 },
          high: [], mid: [], low: [],
          truncated_count: 0,
          last_swept_at: backend.lastSweptAt,
        };
      },
      async triggerWatchSweep(id: string) {
        backend.sweeps.push(id);
        return { job_id: `job-${id}` };
      },
      async listSweeps() {
        return { list: [], total: 0 };
      },
      async acceptOpportunity(id: string) {
        backend.accepted.push(id);
        return { opportunity_id: id, contact_id: "contact-buyer", status: "accepted" };
      },
      async updateOpportunityReviewState(id: string, patch: Record<string, unknown>) {
        backend.reviews.push({ id, patch });
        return { ...opportunity(), id, review_state: patch.state } as Opportunity;
      },
    },
    };
    return () => repos;
  })(),
}));

function renderPage(path = "/app/radar/opportunities?brand_id=b1&product_id=p1&sort=posted") {
  return render(
    <MemoryRouter initialEntries={[path]}>
      <I18nProvider><RadarOpportunitiesPage /></I18nProvider>
    </MemoryRouter>,
  );
}

beforeEach(() => {
  localStorage.setItem(KEYS.uiPrefs, JSON.stringify({ locale: "zh-TW", currency: "TWD", theme: "system" }));
  backend.error = null;
  backend.calls = [];
  backend.accepted = [];
  backend.reviews = [];
  backend.sweeps = [];
  backend.result = null;
  backend.byScope = null;
  backend.jobResult = null;
  backend.lastSweptAt = Date.now() * 1e6;
  backend.watches = [{
    id: "w1",
    terms: ["敏感肌"],
    exclude_terms: [],
    regions: [],
    status: "active",
    last_swept_at: backend.lastSweptAt,
    created_at: 1,
    updated_at: 1,
  }];
});

describe("RadarOpportunitiesPage", () => {
  it("shows scheduled patrol, last run, and run-now on the primary workplace", async () => {
    renderPage("/app/radar");
    expect(await screen.findByTestId("radar-patrol-desk")).toBeTruthy();
    expect(screen.getByText("每日定時巡邏：開著")).toBeTruthy();
    expect(screen.getByText(/上次巡邏：/)).toBeTruthy();
    expect(screen.getByRole("button", { name: "立即巡邏" })).toBeTruthy();
    fireEvent.click(screen.getByRole("button", { name: "立即巡邏" }));
    await waitFor(() => expect(backend.sweeps).toEqual(["w1"]));
  });

  it("runs every active watch and reports a real patrol failure", async () => {
    backend.watches.push({
      ...backend.watches[0],
      id: "w2",
      terms: ["求推薦"],
    });
    backend.jobResult = (id) => id === "job-w2"
      ? { status: "failed", progress_summary: "今天沒巡到：Chrome 登入已過期。" }
      : { status: "succeeded", progress_summary: "雷達巡檢完成 · 新建 1" };

    renderPage("/app/radar");
    fireEvent.click(await screen.findByRole("button", { name: "立即巡邏" }));

    await waitFor(() => expect(backend.sweeps).toEqual(["w1", "w2"]));
    expect(await screen.findByRole("alert")).toHaveTextContent("今天沒巡到：Chrome 登入已過期。");
    expect(screen.queryByText("這一輪巡邏跑完了。找到的痛點會留在下面。")).toBeNull();
  });

  it("restores URL filters and removes product when brand is cleared", async () => {
    renderPage();
    expect(await screen.findByText("正在找適合敏感肌的日常修護產品")).toBeTruthy();
    expect(backend.calls[0]).toMatchObject({ brand_id: "b1", product_id: "p1", sort: "newest", review_state: "pending", time_scope: "today" });
    expect(screen.getByRole("link", { name: "查看 Threads 原文" }).getAttribute("href")).toContain("shared-post");

    fireEvent.change(await screen.findByRole("combobox", { name: "品牌" }), { target: { value: "" } });
    await waitFor(() => expect(backend.calls.at(-1)).toMatchObject({ brand_id: undefined, product_id: undefined }));
  });

  it("shows a real API error without flashing an empty success", async () => {
    backend.error = new ApiError("商機服務暫時不可用", 501010, 503);
    renderPage();
    expect(await screen.findByRole("alert")).toBeTruthy();
    expect(screen.queryByText("目前沒有符合條件的結果")).toBeNull();
  });

  it("sends server-side sort and time scope changes while retaining review state", async () => {
    renderPage();
    await screen.findByText("正在找適合敏感肌的日常修護產品");
    fireEvent.change(screen.getByRole("combobox", { name: "先看哪些" }), { target: { value: "newest" } });
    fireEvent.change(screen.getByRole("combobox", { name: "看哪段時間" }), { target: { value: "7d" } });
    fireEvent.click(screen.getByRole("button", { name: "已看過" }));
    await waitFor(() => expect(backend.calls.at(-1)).toMatchObject({ sort: "newest", time_scope: "7d", review_state: "completed" }));
  });

  it("explains a filtered empty result instead of rendering a blank page", async () => {
    backend.result = { list: [], total: 0 };
    renderPage();
    expect(await screen.findByText("這個篩選下沒有結果")).toBeTruthy();
    expect(screen.getAllByText(/近 7 天/).length).toBeGreaterThan(0);
  });

  it("widens today to 7d when patrol hits are older posts", async () => {
    const older = opportunity();
    older.id = "older-hit";
    older.text = "舊文但是這輪才判定";
    backend.result = { list: [], total: 0 };
    backend.byScope = (filter: Record<string, unknown>) => {
      if (filter.time_scope === "7d" && filter.review_state === "pending") {
        return { list: [older], total: 1 };
      }
      return { list: [], total: 0 };
    };
    renderPage("/app/radar");
    expect(await screen.findByText("舊文但是這輪才判定")).toBeTruthy();
    expect(await screen.findByText(/已改看近 7 天/)).toBeTruthy();
  });

  it("explains an unfiltered empty workplace in patrol language", async () => {
    backend.result = { list: [], total: 0 };
    renderPage("/app/radar");
    expect(await screen.findByTestId("radar-patrol-desk")).toBeTruthy();
    expect(await screen.findByText("這輪有巡，但沒找到符合的痛點")).toBeTruthy();
    expect(screen.getAllByText(/近 7 天/).length).toBeGreaterThan(0);
    expect(screen.getAllByText(/全部/).length).toBeGreaterThan(0);
  });

  it("default actions are keep or discard; joining 名單 is optional", async () => {
    renderPage();
    await screen.findByText("正在找適合敏感肌的日常修護產品");

    expect(screen.getByRole("button", { name: "留下" })).toBeTruthy();
    expect(screen.getByRole("button", { name: "丟掉" })).toBeTruthy();
    expect(screen.getByRole("button", { name: "加入名單（可選）" })).toBeTruthy();

    fireEvent.click(screen.getByRole("button", { name: "加入名單（可選）" }));
    await screen.findByText("已加入名單。這步是可選的，之後要追蹤再去名單即可。");
    expect(backend.accepted).toEqual(["opp-archive"]);
    expect(screen.getByRole("link", { name: "前往名單" }).getAttribute("href")).toBe("/app/crm?contact=contact-buyer");
  });

  it("留下不會誤建名單", async () => {
    renderPage();
    await screen.findByText("正在找適合敏感肌的日常修護產品");
    fireEvent.click(screen.getByRole("button", { name: "留下" }));

    await screen.findByText("已留下。沒有建立名單。");
    expect(backend.accepted).toEqual([]);
    expect(backend.reviews).toEqual([{ id: "opp-archive", patch: { state: "completed" } }]);
  });
});
