import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { Repos } from "../data/repos";
import type { Job, ScoutHomeworkRecord, ScoutRun, ScoutRunBrief } from "../domain/types";
import { I18nProvider } from "../i18n/I18nContext";
import { ensureCatalog, translate } from "../lib/i18n/messages";
import { scoutPostFixture } from "../test/scoutFixtures";
import { ScoutPage } from "./ScoutPage";

const harness = vi.hoisted(() => ({
  repos: null as Repos | null,
  failLoad: false,
  jobs: [] as Job[],
  listRunPostsCalls: 0,
}));

vi.mock("../data/DataContext", () => ({
  useRepos: () => harness.repos,
  useData: () => ({
    repos: harness.repos,
    dataSource: "live" as const,
    refresh: () => {},
    tick: 0,
  }),
}));

vi.mock("../data/JobLiveContext", () => ({
  useJobLive: () => ({
    jobs: harness.jobs,
    activeJobs: [],
    reload: async () => {},
    revision: 0,
  }),
}));

function buildRepos(): Repos {
  const run: ScoutRun = {
    id: "run-fixture-1",
    job_id: "job-fixture-1",
    theme_key: "activity|主題",
    theme_label: "主題",
    intent: "主題",
    mode: "activity",
    target_count: 1,
    status: "succeeded",
    searched_count: 1,
    duplicate_count: 0,
    irrelevant_count: 0,
    eligible_count: 2,
    pending_count: 2,
    shortfall_count: 0,
    shortfall_reasons: [],
    created_at: 1_700_000_001_000_000_000,
  };
  const post = scoutPostFixture({ run_id: run.id });
  const unknownPost = scoutPostFixture({
    id: "scout-post-unknown-time",
    run_id: run.id,
    permalink: "https://www.threads.net/@topic_user/post/scout-post-unknown-time",
    posted_at: undefined,
    created_at: 1_700_000_002_000_000_000,
  });
  const scout = {
    async listRuns() {
      if (harness.failLoad) throw new Error("fixture load failed");
      return {
        list: [run],
        pagination: { page: 1, pageSize: 10, total: 1, totalPages: 1 },
      };
    },
    async listRunPosts() {
      harness.listRunPostsCalls += 1;
      if (harness.failLoad) throw new Error("fixture load failed");
      return {
        list: [post, unknownPost],
        pagination: { page: 1, pageSize: 8, total: 2, totalPages: 1 },
        run,
      };
    },
    async listHomework() {
      if (harness.failLoad) throw new Error("fixture load failed");
      return [];
    },
    async saveHomework(record: ScoutHomeworkRecord) {
      return record;
    },
    async prepareBrief(): Promise<ScoutRunBrief> {
      return {
        intent: "主題",
        mode: "activity",
        brand_id: null,
        product_id: null,
        pains: [],
        tags: ["主題"],
        periphery: [],
        scan_terms: ["主題"],
        theme_key: "activity|主題",
        theme_label: "主題",
      };
    },
    async runScanFromBrief() {
      throw new Error("scan not configured in fixture");
    },
    async removePost() {},
    async removeRun() {},
  };
  const jobs = {
    async list() { return { list: [], pagination: { page: 1, pageSize: 10, total: 0, totalPages: 0 } }; },
    async get() { return null; },
    async startDemo() { throw new Error("not used"); },
    async remove() {},
  };
  return { scout, jobs } as unknown as Repos;
}

function renderPage() {
  return render(
    <MemoryRouter>
      <I18nProvider>
        <ScoutPage />
      </I18nProvider>
    </MemoryRouter>,
  );
}

describe("ScoutPage baseline harness", () => {
  // 英文字典是動態載入的，斷言前先確保它到位
  beforeAll(async () => {
    await ensureCatalog("en");
  });

  beforeEach(() => {
    harness.failLoad = false;
    harness.repos = buildRepos();
    harness.jobs = [];
    harness.listRunPostsCalls = 0;
    localStorage.clear();
  });

  it("renders a loaded activity post through the repository seam", async () => {
    renderPage();
    expect((await screen.findAllByText("這是一則可供海巡測試的主題貼文")).length).toBeGreaterThan(0);
    expect(screen.getByText(translate("en", "scout.runs"))).toBeTruthy();
    expect(screen.getAllByText(/Posted/).length).toBeGreaterThan(0);
    expect(screen.getAllByText(translate("en", "scout.postedUnknown")).length).toBeGreaterThan(0);
  });

  it("shows a readable load error without turning it into an empty success", async () => {
    harness.failLoad = true;
    renderPage();
    await waitFor(() => {
      expect(screen.getByText("fixture load failed")).toBeTruthy();
    });
    expect(screen.queryByText("這是一則可供海巡測試的主題貼文")).toBeNull();
  });

  it("keeps the selected run and result page when a scan completes", async () => {
    const repos = buildRepos();
    const queuedJob: Job = {
      id: "job-new",
      template_type: "scout_scan",
      status: "queued",
      progress_summary: "queued",
      progress_percent: 0,
      created_at: 1_700_000_003_000_000_000,
      updated_at: 1_700_000_003_000_000_000,
    };
    const completedJob = { ...queuedJob, status: "succeeded" as const, updated_at: queuedJob.updated_at + 1 };
    repos.scout.runScanFromBrief = async () => ({ job: queuedJob, run: { ...(await repos.scout.listRuns()).list[0]!, id: "run-new" } });
    repos.jobs.get = async () => completedJob;
    harness.repos = repos;

    renderPage();
    await screen.findAllByText("這是一則可供海巡測試的主題貼文");
    const initialCalls = harness.listRunPostsCalls;
    fireEvent.change(screen.getByLabelText(translate("en", "scout.keyword")), { target: { value: "主題" } });
    fireEvent.click(screen.getByRole("button", { name: translate("en", "scout.planKeywords") }));
    await screen.findByText(translate("en", "scout.confirmScan"));
    fireEvent.click(screen.getByRole("button", { name: translate("en", "scout.confirmScan") }));
    await waitFor(() => expect(screen.getByText(translate("en", "scout.newRunReady"))).toBeTruthy());
    expect(harness.listRunPostsCalls).toBe(initialCalls);
    expect(screen.getAllByText("這是一則可供海巡測試的主題貼文").length).toBeGreaterThan(0);
  });

  it("offers an immediate patrol that still prepares focused search terms", async () => {
    const repos = buildRepos();
    const queuedJob: Job = {
      id: "job-immediate",
      template_type: "scout_scan",
      status: "queued",
      progress_summary: "queued",
      progress_percent: 0,
      created_at: 1_700_000_003_000_000_000,
      updated_at: 1_700_000_003_000_000_000,
    };
    const runScan = vi.fn(async (brief: ScoutRunBrief) => ({
      job: queuedJob,
      run: { ...(await repos.scout.listRuns()).list[0]!, id: "run-immediate", intent: brief.intent },
    }));
    repos.scout.runScanFromBrief = runScan;
    harness.repos = repos;

    renderPage();
    await screen.findAllByText("這是一則可供海巡測試的主題貼文");
    fireEvent.change(screen.getByLabelText(translate("en", "scout.keyword")), { target: { value: "主題" } });
    fireEvent.click(screen.getByRole("button", { name: translate("en", "scout.startImmediate") }));

    await waitFor(() => expect(runScan).toHaveBeenCalledTimes(1));
    expect(runScan.mock.calls[0]?.[0].scan_terms.length).toBeGreaterThan(0);
    expect(screen.getByText(/Patrol job .*queued/)).toBeTruthy();
  });

  it("keeps the batch visible when deleting a running batch conflicts", async () => {
    const repos = buildRepos();
    repos.scout.removeRun = async () => {
      throw Object.assign(new Error("run is busy"), { code: 409031 });
    };
    harness.repos = repos;
    vi.spyOn(window, "confirm").mockReturnValue(true);
    renderPage();
    await screen.findAllByText("這是一則可供海巡測試的主題貼文");
    fireEvent.click(screen.getByRole("button", { name: translate("en", "scout.deleteRun") }));
    await waitFor(() => expect(screen.getByText(translate("en", "scout.err.runBusy"))).toBeTruthy());
    expect(screen.getByText(translate("en", "scout.runs"))).toBeTruthy();
    vi.restoreAllMocks();
  });

  it("keeps the current post when it was already removed elsewhere", async () => {
    const repos = buildRepos();
    repos.scout.removePost = async () => {
      throw Object.assign(new Error("not found"), { code: 404001 });
    };
    harness.repos = repos;
    vi.spyOn(window, "confirm").mockReturnValue(true);
    renderPage();
    await screen.findAllByText("這是一則可供海巡測試的主題貼文");
    fireEvent.click(screen.getByRole("button", { name: /^Delete$/ }));
    await waitFor(() => expect(screen.getByText(translate("en", "scout.err.notFound"))).toBeTruthy());
    expect(screen.getAllByText("這是一則可供海巡測試的主題貼文").length).toBeGreaterThan(0);
    vi.restoreAllMocks();
  });

  it("backs up from an emptied result page after deleting its last post", async () => {
    const repos = buildRepos();
    const baseRun = (await repos.scout.listRuns()).list[0]!;
    const allPosts = Array.from({ length: 9 }, (_, index) =>
      scoutPostFixture({
        id: `page-post-${index + 1}`,
        run_id: baseRun.id,
        text: `分頁貼文 ${index + 1}`,
        posted_at: 1_700_000_000_000_000_000 + index,
      }),
    );
    let removed = false;
    repos.scout.listRunPosts = async (_runId, query) => {
      const current = removed ? allPosts.slice(0, 8) : allPosts;
      const page = query?.page ?? 1;
      const pageSize = query?.pageSize ?? 8;
      return {
        list: current.slice((page - 1) * pageSize, page * pageSize),
        pagination: { page, pageSize, total: current.length, totalPages: Math.ceil(current.length / pageSize) },
        run: baseRun,
      };
    };
    repos.scout.removePost = async () => {
      removed = true;
    };
    harness.repos = repos;
    vi.spyOn(window, "confirm").mockReturnValue(true);
    renderPage();
    await screen.findAllByText("分頁貼文 1");
    fireEvent.click(screen.getByRole("button", { name: translate("en", "pager.next") }));
    await screen.findAllByText("分頁貼文 9");
    fireEvent.click(screen.getByRole("button", { name: /^Delete$/ }));
    await waitFor(() => expect(screen.getAllByText("分頁貼文 1").length).toBeGreaterThan(0));
    expect(screen.queryByText("分頁貼文 9")).toBeNull();
    vi.restoreAllMocks();
  });
});
