import { describe, it, expect, vi, beforeEach } from "vitest";
import userEvent from "@testing-library/user-event";
import type { UrlUpdateEvent } from "nuqs/adapters/testing";
import { fireEvent, renderWithProviders, screen, waitFor, within } from "../../../../../tests/test-utils";
import { ProjectsPage } from "./ProjectsPage";
import { ProjectResponse } from "@/app/(dashboard)/hooks/projects/useProjects";

const mockUseProjects = vi.fn();
vi.mock("@/app/(dashboard)/hooks/projects/useProjects", () => ({
  useProjects: () => mockUseProjects(),
}));

const mockUseTeams = vi.fn();
vi.mock("@/app/(dashboard)/hooks/teams/useTeams", () => ({
  useTeams: () => mockUseTeams(),
}));

// Stub modals and the detail page to keep tests focused on the list page
vi.mock("./ProjectModals/CreateProjectModal", () => ({
  CreateProjectModal: ({ isOpen }: { isOpen: boolean }) => (isOpen ? <div data-testid="create-modal" /> : null),
}));

vi.mock("./ProjectDetailsPage", () => ({
  ProjectDetail: ({ projectId, onBack }: { projectId: string; onBack: () => void }) => (
    <div data-testid="project-detail">
      {projectId}
      <button onClick={onBack}>Back to projects</button>
    </div>
  ),
}));

const mockProjects: ProjectResponse[] = [
  {
    project_id: "proj-1",
    project_alias: "Alpha Project",
    description: "First project",
    team_id: "team-1",
    budget_id: null,
    metadata: null,
    models: ["gpt-4", "claude-3"],
    spend: 5.0,
    model_spend: null,
    model_rpm_limit: null,
    model_tpm_limit: null,
    blocked: false,
    object_permission_id: null,
    created_at: "2024-01-01T00:00:00Z",
    created_by: "user-1",
    updated_at: "2024-01-01T00:00:00Z",
    updated_by: "user-1",
    litellm_budget_table: null,
  },
  {
    project_id: "proj-2",
    project_alias: "Beta Project",
    description: "Second project",
    team_id: "team-2",
    budget_id: null,
    metadata: null,
    models: [],
    spend: 0,
    model_spend: null,
    model_rpm_limit: null,
    model_tpm_limit: null,
    blocked: true,
    object_permission_id: null,
    created_at: "2024-02-01T00:00:00Z",
    created_by: "user-2",
    updated_at: "2024-02-01T00:00:00Z",
    updated_by: "user-2",
    litellm_budget_table: null,
  },
];

describe("ProjectsPage", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockUseTeams.mockReturnValue({ data: [], isLoading: false });
  });

  it("should render the Projects heading", () => {
    mockUseProjects.mockReturnValue({ data: [], isLoading: false });
    renderWithProviders(<ProjectsPage />);
    expect(screen.getByRole("heading", { name: /projects/i })).toBeInTheDocument();
    expect(screen.getByText("Manage projects within your teams")).toBeInTheDocument();
    expect(document.querySelector(".lucide-folder")).not.toBeNull();
  });

  it("should show a 'Create Project' button", () => {
    mockUseProjects.mockReturnValue({ data: [], isLoading: false });
    renderWithProviders(<ProjectsPage />);
    expect(screen.getByRole("button", { name: /create project/i })).toBeInTheDocument();
  });

  it("should render the projects table", () => {
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />);
    expect(screen.getByText("Alpha Project")).toBeInTheDocument();
    expect(screen.getByText("Beta Project")).toBeInTheDocument();
  });

  it("should show the model count for each project", () => {
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />);
    // proj-1 has 2 models, proj-2 has 0
    expect(screen.getByText("2")).toBeInTheDocument();
    expect(screen.getByText("0")).toBeInTheDocument();
  });

  it("should display 'Active' tag for non-blocked projects", () => {
    mockUseProjects.mockReturnValue({ data: [mockProjects[0]], isLoading: false });
    renderWithProviders(<ProjectsPage />);
    expect(screen.getByText("Active")).toBeInTheDocument();
  });

  it("should display 'Blocked' tag for blocked projects", () => {
    mockUseProjects.mockReturnValue({ data: [mockProjects[1]], isLoading: false });
    renderWithProviders(<ProjectsPage />);
    expect(screen.getByText("Blocked")).toBeInTheDocument();
  });

  it("should open the create modal when 'Create Project' is clicked", async () => {
    const user = userEvent.setup();
    mockUseProjects.mockReturnValue({ data: [], isLoading: false });
    renderWithProviders(<ProjectsPage />);
    await user.click(screen.getByRole("button", { name: /create project/i }));
    expect(screen.getByTestId("create-modal")).toBeInTheDocument();
  });

  it("should show the project detail view when a project ID is clicked", async () => {
    const user = userEvent.setup();
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />);
    await user.click(screen.getByText("proj-1"));
    expect(screen.getByTestId("project-detail")).toHaveTextContent("proj-1");
  });

  it("should filter displayed projects when the search input has a value", async () => {
    const user = userEvent.setup();
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />);
    fireEvent.change(screen.getByPlaceholderText(/search projects/i), { target: { value: "Alpha" } });
    await waitFor(() => {
      expect(screen.getByText("Alpha Project")).toBeInTheDocument();
      expect(screen.queryByText("Beta Project")).not.toBeInTheDocument();
    });
  });

  it("should show the total project count in the pagination", () => {
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />);
    expect(screen.getByTestId("pagination-range")).toHaveTextContent("Showing 1-2 of 2");
  });

  it("should show skeleton rows while projects are loading", () => {
    mockUseProjects.mockReturnValue({ data: undefined, isLoading: true });
    renderWithProviders(<ProjectsPage />);
    expect(screen.getAllByTestId("skeleton-row").length).toBeGreaterThan(0);
  });

  it("should show the empty state when there are no projects", () => {
    mockUseProjects.mockReturnValue({ data: [], isLoading: false });
    renderWithProviders(<ProjectsPage />);
    expect(screen.getByText("No projects yet")).toBeInTheDocument();
  });

  it("should show the filtered empty state when a search matches nothing", async () => {
    const user = userEvent.setup();
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />);
    fireEvent.change(screen.getByPlaceholderText(/search projects/i), { target: { value: "zzz-no-match" } });
    await waitFor(() => {
      expect(screen.getByText("No matching projects")).toBeInTheDocument();
    });
  });

  it("should sort by name when the Name header is clicked", async () => {
    const user = userEvent.setup();
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />);

    await user.click(screen.getByRole("button", { name: /^name$/i }));
    let rows = screen.getAllByRole("row").slice(1);
    expect(within(rows[0]).getByText("Alpha Project")).toBeInTheDocument();

    await user.click(screen.getByRole("button", { name: /^name$/i }));
    rows = screen.getAllByRole("row").slice(1);
    expect(within(rows[0]).getByText("Beta Project")).toBeInTheDocument();
  });

  it("should reset to the first page when the search text changes", async () => {
    const user = userEvent.setup();
    const manyProjects = Array.from({ length: 12 }, (_, i) => ({
      ...mockProjects[0],
      project_id: `proj-${i + 1}`,
      project_alias: `Project ${String(i + 1).padStart(2, "0")}`,
    }));
    mockUseProjects.mockReturnValue({ data: manyProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />);

    await user.click(screen.getByTestId("pagination-next"));
    expect(screen.getByTestId("pagination-page")).toHaveTextContent("Page 2 of 2");

    fireEvent.change(screen.getByPlaceholderText(/search projects/i), { target: { value: "Project 01" } });
    await waitFor(() => {
      expect(screen.getByText("Project 01")).toBeInTheDocument();
      expect(screen.getByTestId("pagination-page")).toHaveTextContent("Page 1 of 1");
    });
  });

  it("should open the detail view directly from a ?project= deep link", () => {
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />, { searchParams: "?project=proj-2" });

    expect(screen.getByTestId("project-detail")).toHaveTextContent("proj-2");
    expect(screen.queryByRole("heading", { name: /projects/i })).not.toBeInTheDocument();
  });

  it("should push ?project= as a new history entry when a project is opened", async () => {
    const user = userEvent.setup();
    const onUrlUpdate = vi.fn<(event: UrlUpdateEvent) => void>();
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />, { onUrlUpdate });

    await user.click(screen.getByText("proj-1"));

    await waitFor(() => {
      expect(onUrlUpdate).toHaveBeenLastCalledWith(
        expect.objectContaining({
          queryString: "?project=proj-1",
          options: expect.objectContaining({ history: "push" }),
        }),
      );
    });
  });

  it("should clear ?project= and return to the list when the detail view is closed", async () => {
    const user = userEvent.setup();
    const onUrlUpdate = vi.fn<(event: UrlUpdateEvent) => void>();
    mockUseProjects.mockReturnValue({ data: mockProjects, isLoading: false });
    renderWithProviders(<ProjectsPage />, { searchParams: "?project=proj-1", onUrlUpdate });

    await user.click(screen.getByRole("button", { name: /back to projects/i }));

    await waitFor(() => {
      expect(onUrlUpdate).toHaveBeenLastCalledWith(expect.objectContaining({ queryString: "" }));
    });
    expect(onUrlUpdate.mock.calls.at(-1)?.[0].options.history).toBe("replace");
    expect(screen.queryByTestId("project-detail")).not.toBeInTheDocument();
    expect(screen.getByText("Alpha Project")).toBeInTheDocument();
  });

  it("should resolve team alias from the teams list in the Team column", () => {
    mockUseTeams.mockReturnValue({
      data: [{ team_id: "team-1", team_alias: "Engineering", models: [] }],
      isLoading: false,
    });
    mockUseProjects.mockReturnValue({ data: [mockProjects[0]], isLoading: false });
    renderWithProviders(<ProjectsPage />);
    expect(screen.getByText("Engineering")).toBeInTheDocument();
  });
});
