import type { ComponentProps } from "react";
import { render, screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { DatasetTableToolbar } from "./DatasetTableToolbar";
import { DatasetTableViewOptions } from "./DatasetTableViewOptions";
import { createMockTable, openColumnsMenu } from "./test-utils";

function renderToolbar(
  overrides: Partial<ComponentProps<typeof DatasetTableToolbar>> = {},
) {
  const props = {
    table: createMockTable(),
    viewMode: "active" as const,
    hasActiveFilters: false,
    activeTags: [],
    activeStatuses: [],
    showFavoritesOnly: false,
    searchInput: "",
    allTags: ["vision", "audio"],
    isUpdatingTags: false,
    isMutatingDatasets: false,
    setShowFavoritesOnly: vi.fn(),
    setViewMode: vi.fn(),
    setSearchInput: vi.fn(),
    handleTagToggle: vi.fn(),
    handleStatusToggle: vi.fn(),
    clearFilters: vi.fn(),
    resetColumnVisibilityToDefault: vi.fn(),
    showAllColumns: vi.fn(),
    onColumnVisibilityChange: vi.fn(),
    onDeleteTag: vi.fn().mockResolvedValue(undefined),
    onRenameTag: vi.fn().mockResolvedValue(undefined),
    onMergeTag: vi.fn().mockResolvedValue(undefined),
    onEmptyTrash: vi.fn(),
    onImportDataset: vi.fn(),
    ...overrides,
  };

  render(<DatasetTableToolbar {...props} />);
  return props;
}

function renderViewOptions(
  overrides: Partial<ComponentProps<typeof DatasetTableViewOptions>> = {},
) {
  const props = {
    table: createMockTable(),
    resetColumnVisibilityToDefault: vi.fn(),
    showAllColumns: vi.fn(),
    onColumnVisibilityChange: vi.fn(),
    ...overrides,
  };

  render(<DatasetTableViewOptions {...props} />);
  return props;
}

describe("DatasetTableToolbar", () => {
  beforeEach(() => {
    Element.prototype.scrollIntoView = vi.fn();
  });

  it("calls setSearchInput from the search input", async () => {
    const props = renderToolbar();
    const user = userEvent.setup();

    await user.type(screen.getByPlaceholderText("Filter datasets..."), "Alpha");

    await waitFor(() => {
      expect(props.setSearchInput).toHaveBeenCalled();
    });
  });

  it("calls setShowFavoritesOnly from the favorites toggle", async () => {
    const props = renderToolbar();
    const user = userEvent.setup();

    await user.click(screen.getByRole("button", { name: /Favorites Only/i }));

    expect(props.setShowFavoritesOnly).toHaveBeenCalledWith(
      true,
      expect.any(Object),
    );
  });

  it("shows the reset button only when filters are active", () => {
    const { rerender } = render(
      <DatasetTableToolbar
        table={createMockTable()}
        viewMode="active"
        hasActiveFilters={false}
        activeTags={[]}
        activeStatuses={[]}
        showFavoritesOnly={false}
        searchInput=""
        allTags={["vision"]}
        isUpdatingTags={false}
        isMutatingDatasets={false}
        setShowFavoritesOnly={vi.fn()}
        setViewMode={vi.fn()}
        setSearchInput={vi.fn()}
        handleTagToggle={vi.fn()}
        handleStatusToggle={vi.fn()}
        clearFilters={vi.fn()}
        resetColumnVisibilityToDefault={vi.fn()}
        showAllColumns={vi.fn()}
        onColumnVisibilityChange={vi.fn()}
        onDeleteTag={vi.fn().mockResolvedValue(undefined)}
        onRenameTag={vi.fn().mockResolvedValue(undefined)}
        onMergeTag={vi.fn().mockResolvedValue(undefined)}
        onEmptyTrash={vi.fn()}
        onImportDataset={vi.fn()}
      />,
    );

    expect(
      screen.queryByRole("button", { name: "Reset" }),
    ).not.toBeInTheDocument();

    rerender(
      <DatasetTableToolbar
        table={createMockTable()}
        viewMode="active"
        hasActiveFilters
        activeTags={["vision"]}
        activeStatuses={[]}
        showFavoritesOnly={false}
        searchInput="Alpha"
        allTags={["vision"]}
        isUpdatingTags={false}
        isMutatingDatasets={false}
        setShowFavoritesOnly={vi.fn()}
        setViewMode={vi.fn()}
        setSearchInput={vi.fn()}
        handleTagToggle={vi.fn()}
        handleStatusToggle={vi.fn()}
        clearFilters={vi.fn()}
        resetColumnVisibilityToDefault={vi.fn()}
        showAllColumns={vi.fn()}
        onColumnVisibilityChange={vi.fn()}
        onDeleteTag={vi.fn().mockResolvedValue(undefined)}
        onRenameTag={vi.fn().mockResolvedValue(undefined)}
        onMergeTag={vi.fn().mockResolvedValue(undefined)}
        onEmptyTrash={vi.fn()}
        onImportDataset={vi.fn()}
      />,
    );

    expect(screen.getByRole("button", { name: "Reset" })).toBeInTheDocument();
  });

  it("calls clearFilters from the reset button", async () => {
    const props = renderToolbar({
      hasActiveFilters: true,
      activeTags: ["vision"],
      searchInput: "Alpha",
    });
    const user = userEvent.setup();

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

    expect(props.clearFilters).toHaveBeenCalledTimes(1);
  });

  it("delegates status selection changes", async () => {
    const props = renderToolbar();
    const user = userEvent.setup();

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

    expect(props.handleStatusToggle).toHaveBeenCalledWith("Completed");
  });
});

describe("DatasetTableViewOptions", () => {
  it("keeps non-hideable columns disabled", async () => {
    renderViewOptions();
    const user = userEvent.setup();

    const menu = await openColumnsMenu(user);
    const nameCheckbox = within(menu).getByRole("menuitemcheckbox", {
      name: "Name",
    });

    expect(nameCheckbox).toHaveAttribute("aria-disabled", "true");
  });

  it("calls onColumnVisibilityChange for hideable columns", async () => {
    const props = renderViewOptions({
      table: createMockTable([{ id: "tags", label: "Tags", visible: true }]),
    });
    const user = userEvent.setup();

    const menu = await openColumnsMenu(user);
    await user.click(
      within(menu).getByRole("menuitemcheckbox", { name: "Tags" }),
    );

    expect(props.onColumnVisibilityChange).toHaveBeenCalledWith("tags", false);
  });

  it("calls show all and reset default actions", async () => {
    const props = renderViewOptions();
    const user = userEvent.setup();

    let menu = await openColumnsMenu(user);
    await user.click(within(menu).getByRole("menuitem", { name: /Show All/i }));

    expect(props.showAllColumns).toHaveBeenCalledTimes(1);

    menu = await openColumnsMenu(user);
    await user.click(
      within(menu).getByRole("menuitem", { name: /Reset Default/i }),
    );

    expect(props.resetColumnVisibilityToDefault).toHaveBeenCalledTimes(1);
  });
});
