import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "../utils/testHelpers";
import { TabloDetailsPage } from "./tablo-details";

if (!HTMLElement.prototype.hasPointerCapture) {
  HTMLElement.prototype.hasPointerCapture = () => false;
}

if (!HTMLElement.prototype.setPointerCapture) {
  HTMLElement.prototype.setPointerCapture = () => undefined;
}

if (!HTMLElement.prototype.releasePointerCapture) {
  HTMLElement.prototype.releasePointerCapture = () => undefined;
}

const mutateUpdateTablo = vi.fn();
const mutateUpdateTask = vi.fn();
const mutateCreateTask = vi.fn();
const mutateCreateEtape = vi.fn();
const mutateUpdateEtape = vi.fn();
const mutateDeleteEtape = vi.fn();
const mutateDeleteTask = vi.fn();

const tablosData = [
  {
    id: "tablo-1",
    name: "Test Tablo",
    color: "bg-blue-500",
    image: null,
    created_at: "2026-01-01T00:00:00.000Z",
    deleted_at: null,
    position: 0,
    status: "todo",
    user_id: "user-1",
    is_admin: true,
    access_level: "admin",
  },
];

const layoutData: unknown = {
  version: 1,
  leftZone: ["myTasks", "description"],
  rightZone: ["files", "info"],
};

vi.mock("../hooks/useChatUnread", () => ({
  useChatUnread: () => ({
    unreadCounts: [],
    getUnreadCount: () => 0,
    hasUnread: () => false,
  }),
}));

vi.mock("../hooks/tablos", () => ({
  useTablosList: () => ({
    data: tablosData,
    isLoading: false,
  }),
  useTabloMembers: () => ({
    data: [
      {
        id: "user-1",
        name: "Test User",
        email: "john@example.com",
        avatar_url: null,
        is_admin: true,
      },
    ],
  }),
  useTabloOverviewLayout: () => ({
    data: layoutData,
  }),
  useUpdateTablo: () => ({
    mutate: mutateUpdateTablo,
  }),
}));

vi.mock("../hooks/tablo_invites", () => ({
  usePendingTabloInvitesByTablo: () => ({
    data: [],
  }),
  useCancelTabloInvite: () => ({
    mutate: vi.fn(),
    isPending: false,
  }),
}));

vi.mock("../hooks/client_invites", () => ({
  usePendingClientInvites: () => ({
    data: [],
  }),
  useCreateClientInvite: () => ({
    mutate: vi.fn(),
    isPending: false,
  }),
  useCancelClientInvite: () => ({
    mutate: vi.fn(),
    isPending: false,
  }),
}));

vi.mock("../hooks/invite", () => ({
  useInviteUser: () => ({
    mutate: vi.fn(),
    isPending: false,
  }),
}));

vi.mock("../hooks/events", () => ({
  useEventsByTablo: () => ({
    data: [],
    isLoading: false,
    error: null,
  }),
}));

vi.mock("../hooks/tasks", () => ({
  useAllTasks: () => ({
    data: [
      {
        id: "task-1",
        tablo_id: "tablo-1",
        assignee_id: "user-1",
        title: "Task A",
        status: "todo",
        parent_task_id: "etape-1",
      },
    ],
  }),
  useTabloEtapes: () => ({
    data: [
      {
        id: "etape-1",
        tablo_id: "tablo-1",
        title: "Kickoff",
        description: null,
        status: "todo",
        assignee_id: null,
        position: 0,
        created_at: "2026-01-01T00:00:00.000Z",
        updated_at: "2026-01-01T00:00:00.000Z",
        is_parent: true,
        parent_task_id: null,
        due_date: null,
      },
    ],
  }),
  useUpdateTask: () => ({
    mutate: mutateUpdateTask,
  }),
  useDeleteTask: () => ({
    mutate: mutateDeleteTask,
  }),
  useTask: () => ({
    data: null,
  }),
  useCreateEtape: () => ({
    mutateAsync: mutateCreateEtape,
    isPending: false,
  }),
  useUpdateEtape: () => ({
    mutateAsync: mutateUpdateEtape,
    isPending: false,
  }),
  useDeleteEtape: () => ({
    mutateAsync: mutateDeleteEtape,
    isPending: false,
  }),
  useCreateTask: () => ({
    mutate: mutateCreateTask,
  }),
  useUpdateTaskPositions: () => ({
    mutate: vi.fn(),
  }),
}));

vi.mock("../hooks/tablo_data", () => ({
  useTabloFileNames: () => ({
    data: {
      fileNames: [],
    },
  }),
  useDownloadTabloFile: () => ({
    mutateAsync: vi.fn(),
  }),
  useUploadTabloFile: () => ({
    mutateAsync: vi.fn(),
  }),
  useDeleteTabloFile: () => ({
    mutateAsync: vi.fn(),
  }),
}));

vi.mock("../hooks/tablo_folders", () => ({
  useTabloFolders: () => ({
    data: [],
    isLoading: false,
    error: null,
  }),
  useCreateTabloFolder: () => ({
    mutateAsync: vi.fn(),
    isPending: false,
  }),
  useUpdateTabloFolder: () => ({
    mutateAsync: vi.fn(),
    isPending: false,
  }),
  useDeleteTabloFolder: () => ({
    mutateAsync: vi.fn(),
  }),
}));

vi.mock("../providers/UserStoreProvider", async (importOriginal) => {
  const actual = await importOriginal<typeof import("../providers/UserStoreProvider")>();
  return {
    ...actual,
    useUser: () => ({
      id: "user-1",
      name: "Test User",
      avatar_url: null,
    }),
  };
});

describe("TabloDetailsPage overview layout", () => {
  it("keeps the add etape action enabled before typing", async () => {
    const user = userEvent.setup();

    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    await user.click(screen.getByRole("button", { name: "Étapes" }));

    expect(screen.getByRole("button", { name: "Ajouter une étape" })).toBeEnabled();
  });

  it("creates an etape from the etapes tab", async () => {
    const user = userEvent.setup();
    mutateCreateEtape.mockResolvedValueOnce(undefined);

    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    await user.click(screen.getByRole("button", { name: "Étapes" }));
    await user.type(screen.getByPlaceholderText("Nom de la nouvelle étape..."), "Kickoff");
    await user.click(screen.getByRole("button", { name: "Ajouter une étape" }));

    expect(mutateCreateEtape).toHaveBeenCalledWith({
      tabloId: "tablo-1",
      title: "Kickoff",
      position: 1,
    });
  });

  it("updates an etape from the etapes tab", async () => {
    const user = userEvent.setup();
    mutateUpdateEtape.mockResolvedValueOnce(undefined);

    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    await user.click(screen.getByRole("button", { name: "Étapes" }));
    await user.click(screen.getByRole("button", { name: "Modifier l'étape Kickoff" }));
    await user.clear(screen.getByDisplayValue("Kickoff"));
    await user.type(screen.getByPlaceholderText("Nom de l'étape"), "Planification");
    await user.click(screen.getByRole("button", { name: "Enregistrer l'étape" }));

    expect(mutateUpdateEtape).toHaveBeenCalledWith({
      id: "etape-1",
      tabloId: "tablo-1",
      title: "Planification",
    });
  });

  it("deletes an etape from the etapes tab", async () => {
    const user = userEvent.setup();
    mutateDeleteEtape.mockResolvedValueOnce(undefined);
    const confirmSpy = vi.spyOn(window, "confirm").mockReturnValue(true);

    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    await user.click(screen.getByRole("button", { name: "Étapes" }));
    await user.click(screen.getByRole("button", { name: "Supprimer l'étape Kickoff" }));

    expect(confirmSpy).toHaveBeenCalled();
    expect(mutateDeleteEtape).toHaveBeenCalledWith({
      id: "etape-1",
      tabloId: "tablo-1",
    });

    confirmSpy.mockRestore();
  });

  it("deletes a task from the inline kanban action", async () => {
    const user = userEvent.setup();

    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    await user.click(screen.getByRole("button", { name: "Tâches" }));
    await user.click(screen.getByRole("button", { name: "Supprimer la tâche Task A" }));

    expect(mutateDeleteTask).toHaveBeenCalledWith("task-1");
  });

  it("restores the task etape when reopening the same edit modal", async () => {
    const user = userEvent.setup();

    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1?section=tasks",
      path: "/tablos/:tabloId",
    });

    await user.click(screen.getByText("Task A"));
    await user.click(screen.getByRole("combobox", { name: "Étape" }));
    await user.click(screen.getByRole("option", { name: "Aucune" }));
    await user.click(screen.getByRole("button", { name: "Annuler" }));

    await user.click(screen.getByText("Task A"));

    expect(screen.getByRole("combobox", { name: "Étape" })).toHaveTextContent("Kickoff");
  });

  it("assigns the current user when creating from mes tâches in overview", async () => {
    const user = userEvent.setup();

    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    await user.click(screen.getByRole("button", { name: "Ajouter" }));

    await waitFor(() => {
      expect(screen.getByRole("combobox", { name: "Assigné à" })).toHaveTextContent("Test User");
    });

    await user.type(screen.getByLabelText("Titre *"), "Nouvelle tâche");
    await user.click(screen.getByRole("button", { name: "Créer" }));

    expect(mutateCreateTask).toHaveBeenCalledWith(
      expect.objectContaining({
        tablo_id: "tablo-1",
        title: "Nouvelle tâche",
        assignee_id: "user-1",
      })
    );
  });

  it("renders overview cards in persisted left-zone order", () => {
    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    const tasksHeading = screen.getByText("Mes tâches");
    const descriptionHeading = screen.getByText("Description du projet");

    expect(tasksHeading.compareDocumentPosition(descriptionHeading)).toBe(
      Node.DOCUMENT_POSITION_FOLLOWING
    );
  });

  it("uses a dominant overview first column and purple primary actions", () => {
    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    expect(screen.getByTestId("single-tablo-overview-grid")).toHaveClass(
      "lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]"
    );
    expect(screen.getByRole("link", { name: "Discussion" })).toHaveClass("bg-[#804EEC]");
    expect(screen.getByRole("button", { name: "Inviter" })).toHaveClass(
      "border-[#804EEC]",
      "text-[#804EEC]"
    );
    expect(screen.getByRole("button", { name: "Modifier la mise en page" })).toHaveClass(
      "border-[#804EEC]",
      "text-[#804EEC]"
    );
    expect(screen.getByRole("button", { name: "Ajouter" })).toHaveClass("bg-[#804EEC]");
    expect(screen.getByRole("button", { name: "Ouvrir" })).toHaveClass("text-[#804EEC]");
  });

  it("shows layout edit toggle for admin users", () => {
    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    expect(screen.getByRole("button", { name: "Modifier la mise en page" })).toBeInTheDocument();
  });

  it("uses the client password invite UI in the share dialog", async () => {
    const user = userEvent.setup();

    renderWithProviders(<TabloDetailsPage />, {
      route: "/tablos/tablo-1",
      path: "/tablos/:tabloId",
    });

    await user.click(screen.getByRole("button", { name: "Inviter" }));

    expect(screen.getByText("Accès client")).toBeInTheDocument();
    expect(screen.getByPlaceholderText("Email du client")).toBeInTheDocument();
    expect(screen.getByText(/définir son mot de passe/i)).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Envoyer l'invitation" })).toBeInTheDocument();
    expect(screen.queryByText("Inviter un utilisateur")).not.toBeInTheDocument();
  });
});
