import React from "react";
import "@testing-library/jest-dom";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";

import ApplicationChecklist from "./components/ApplicationChecklist";
import { api } from "./api";

jest.mock("./api", () => ({
  api: {
    get: jest.fn(),
    post: jest.fn(),
    patch: jest.fn(),
    put: jest.fn(),
    delete: jest.fn(),
    interceptors: { request: { use: jest.fn() }, response: { use: jest.fn() } },
  },
  getApiErrorMessage: (_e: any, fallback?: string) => fallback || "Request failed.",
}));

const mockedApi = api as jest.Mocked<typeof api>;

const item = (over: Partial<any> = {}) => ({
  id: 1,
  systemKey: "prepare-cv",
  title: "Prepare a CV for this role",
  description: "Attach a CV variant tailored to this application.",
  category: "preparation",
  status: "pending",
  section: "cv",
  sortOrder: 0,
  isSystemGenerated: true,
  isAutoCompleted: false,
  completedAt: null,
  ...over,
});

const checklist = (items: any[]) => ({
  items,
  progress: {
    total: items.filter((i) => i.status !== "dismissed").length,
    completed: items.filter((i) => i.status === "done").length,
    dismissed: items.filter((i) => i.status === "dismissed").length,
    percent: 0,
  },
});

beforeEach(() => {
  jest.clearAllMocks();
  mockedApi.get.mockResolvedValue({
    data: checklist([
      item(),
      item({ id: 2, systemKey: "create-cover-letter", title: "Create a cover letter", status: "done", isAutoCompleted: true }),
      item({ id: 3, systemKey: null, title: "Ask Sara for a referral", category: "custom", isSystemGenerated: false, description: null }),
    ]),
  } as any);
});

test("renders the checklist grouped by category with progress", async () => {
  render(<ApplicationChecklist jobId={7} />);

  expect(await screen.findByText("Prepare a CV for this role")).toBeInTheDocument();
  expect(screen.getByText("Before applying")).toBeInTheDocument();
  expect(screen.getByText("Your own tasks")).toBeInTheDocument();
  expect(screen.getByText("1 of 3 done")).toBeInTheDocument();
  // Items already satisfied by an existing readiness signal are marked as detected, not hand-ticked.
  expect(screen.getByText("Detected")).toBeInTheDocument();
});

test("completing an item patches its status", async () => {
  mockedApi.patch.mockResolvedValue({ data: item({ status: "done" }) } as any);

  render(<ApplicationChecklist jobId={7} />);
  fireEvent.click(await screen.findByRole("checkbox", { name: "Prepare a CV for this role" }));

  await waitFor(() =>
    expect(mockedApi.patch).toHaveBeenCalledWith("/jobapplications/7/checklist/1", { status: "done" }));
});

test("un-ticking a completed item sends it back to pending", async () => {
  mockedApi.patch.mockResolvedValue({ data: item({ id: 2, status: "pending" }) } as any);

  render(<ApplicationChecklist jobId={7} />);
  fireEvent.click(await screen.findByRole("checkbox", { name: "Create a cover letter" }));

  await waitFor(() =>
    expect(mockedApi.patch).toHaveBeenCalledWith("/jobapplications/7/checklist/2", { status: "pending" }));
});

test("adding a custom task posts the title", async () => {
  mockedApi.post.mockResolvedValue({ data: item({ id: 9, systemKey: null, isSystemGenerated: false }) } as any);

  render(<ApplicationChecklist jobId={7} />);
  fireEvent.change(await screen.findByLabelText(/Add your own task/i), { target: { value: "Email the hiring manager" } });
  fireEvent.click(screen.getByRole("button", { name: "Add" }));

  await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith(
    "/jobapplications/7/checklist",
    { title: "Email the hiring manager", description: undefined, category: undefined },
  ));
});

test("reordering sends the new id order", async () => {
  mockedApi.put.mockResolvedValue({ data: checklist([]) } as any);

  render(<ApplicationChecklist jobId={7} />);
  fireEvent.click(await screen.findByRole("button", { name: "Move down: Prepare a CV for this role" }));

  await waitFor(() =>
    expect(mockedApi.put).toHaveBeenCalledWith("/jobapplications/7/checklist/order", [2, 1, 3]));
});

test("removing an item calls delete", async () => {
  mockedApi.delete.mockResolvedValue({ data: undefined } as any);

  render(<ApplicationChecklist jobId={7} />);
  fireEvent.click(await screen.findByRole("button", { name: "Remove: Ask Sara for a referral" }));

  await waitFor(() => expect(mockedApi.delete).toHaveBeenCalledWith("/jobapplications/7/checklist/3"));
});
