import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { Contact } from "../domain/types";
import { I18nProvider } from "../i18n/I18nContext";
import { translate } from "../lib/i18n/messages";
import { CrmBoardPage } from "./CrmBoardPage";

const t = (key: string, params?: Record<string, string | number>) => translate("zh-TW", key, params);

const backend = vi.hoisted(() => ({
  contacts: [] as Contact[],
  filters: [] as Array<Record<string, unknown>>,
  deleted: [] as string[],
}));

vi.mock("../data/DataContext", () => {
  const repos = {
    crm: {
      async listContacts(filter: Record<string, unknown> = {}) {
        backend.filters.push(filter);
        const query = String(filter.query || "").toLowerCase();
        const stage = String(filter.stage || "");
        const followUp = filter.follow_up;
        const page = Number(filter.page || 1);
        const pageSize = Number(filter.pageSize || 20);
        let list = backend.contacts.filter((contact) =>
          (!query || `${contact.display_name || ""} ${contact.author_handle}`.toLowerCase().includes(query)) &&
          (!stage || contact.stage === stage) &&
          (followUp === undefined || contact.needs_follow_up === followUp),
        );
        if (filter.sort === "intent_score") {
          list = [...list].sort((a, b) => (b.top_intent_score || 0) - (a.top_intent_score || 0));
        }
        return {
          list: list.slice((page - 1) * pageSize, page * pageSize),
          total: list.length,
          stage_counts: [
            ...(["new_found", "engaged", "dm_sent", "replied", "quoted", "won", "lost"] as const).map((stageName) => ({
              stage: stageName,
              count: backend.contacts.filter((contact) => contact.stage === stageName).length,
            })),
            { stage: "needs_follow_up", count: backend.contacts.filter((contact) => contact.needs_follow_up).length },
          ],
        };
      },
      async getContact(id: string) {
        const contact = backend.contacts.find((item) => item.id === id);
        if (!contact) throw new Error("contact not found");
        return { contact, touches: [], opportunities: [] };
      },
      async deleteContact(id: string) {
        backend.deleted.push(id);
        backend.contacts = backend.contacts.filter((item) => item.id !== id);
      },
      updateStage: vi.fn(),
      setFollowUp: vi.fn(),
      addNote: vi.fn(),
      reportConversion: vi.fn(),
    },
  };
  return { useRepos: () => repos };
});

function contact(id: string, name: string, followUp = false): Contact {
  return {
    id,
    source_platform: "threads",
    author_handle: `${name.toLowerCase()}_threads`,
    display_name: name,
    stage: "new_found",
    needs_follow_up: followUp,
    follow_up_days: 3,
    opportunity_ids: [`opp-${id}`],
    opportunity_count: 1,
    top_intent_score: id === "alice" ? 91 : 70,
    created_at: 1,
    updated_at: 2,
  };
}

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

beforeEach(() => {
  localStorage.setItem(
    "harbor.ui.prefs",
    JSON.stringify({ locale: "zh-TW", currency: "TWD", theme: "system" }),
  );
  backend.contacts = [contact("alice", "Alice", true), contact("bob", "Bob")];
  backend.filters = [];
  backend.deleted = [];
  vi.spyOn(window, "confirm").mockReturnValue(true);
});

describe("CrmBoardPage", () => {
  it("待追蹤是篩選標記，不會把同一位聯絡人再畫成第二張卡", async () => {
    renderPage();

    await screen.findByText(t("crm.board.results", { n: 2 }));
    expect(screen.getAllByText("Alice")).toHaveLength(1);
    expect(backend.filters[0]).toMatchObject({ page: 1, pageSize: 20, sort: "last_touch_at" });

    fireEvent.change(screen.getByLabelText(t("crm.board.stageFilter")), {
      target: { value: "needs_follow_up" },
    });
    await screen.findByText(t("crm.board.results", { n: 1 }));
    expect(screen.getAllByText("Alice")).toHaveLength(1);
    expect(screen.queryByText("Bob")).toBeNull();
    expect(backend.filters.at(-1)).toMatchObject({ follow_up: true });
  });

  it("可搜尋並從工作名單移除，移除後不再顯示", async () => {
    renderPage();
    await screen.findByText(t("crm.board.results", { n: 2 }));

    fireEvent.change(screen.getByLabelText(t("crm.board.search")), { target: { value: "Bob" } });
    fireEvent.click(screen.getByRole("button", { name: t("common.search") }));
    await screen.findByText(t("crm.board.results", { n: 1 }));
    expect(backend.filters.at(-1)).toMatchObject({ query: "Bob" });
    expect(screen.queryByText("Alice")).toBeNull();

    const bobCard = screen.getByText("Bob").closest("button");
    if (!bobCard) throw new Error("Bob contact card not found");
    fireEvent.click(bobCard);
    await screen.findByText(t("crm.board.deleteTitle"));
    fireEvent.click(screen.getByRole("button", { name: t("crm.board.delete") }));

    await screen.findByText(t("crm.board.msg.deleted"));
    await waitFor(() => expect(screen.queryByText("Bob")).toBeNull());
    expect(backend.deleted).toEqual(["bob"]);
  });
});
