import { fireEvent, render, screen } from "@testing-library/react";
import { DropdownMenu } from ".";

const CLASS_NAME = "mobius-dropdown-menu";
const TRIGGER_CLASS_NAME = "mobius-dropdown-menu__trigger";
const LIST_CLASS_NAME = "mobius-dropdown-menu__list";
const ITEM_CLASS_NAME = "mobius-dropdown-menu__item";
const ACTIVE_ITEM_CLASS_NAME = "--is-active";

describe("DropdownMenu", () => {
  it("should render without errors", () => {
    const view = render(
      <DropdownMenu>
        <DropdownMenu.Item>Home</DropdownMenu.Item>
      </DropdownMenu>,
    );
    expect(view).toBeTruthy();
  });

  it("should use the provided elementType", () => {
    const { container } = render(
      <DropdownMenu elementType="span">
        <DropdownMenu.Item>Home</DropdownMenu.Item>
      </DropdownMenu>,
    );
    expect(container.firstElementChild?.tagName).toBe("SPAN");
  });

  describe("given elementType", () => {
    it("is not applied as element attribute", () => {
      const { container } = render(
        <DropdownMenu elementType="ul">
          <DropdownMenu.Item elementType="li">Home</DropdownMenu.Item>
        </DropdownMenu>,
      );
      expect(container.firstChild).not.toHaveAttribute("elementtype", "ul");
      expect(screen.queryByRole("menuitem")).not.toHaveAttribute(
        "elementtype",
        "li",
      );
    });
  });

  it("should use the provided label", () => {
    const label = "Menu";

    render(
      <DropdownMenu label={label}>
        <DropdownMenu.Item>Home</DropdownMenu.Item>
      </DropdownMenu>,
    );
    expect(screen.getByText(label)).toBeInTheDocument();
  });

  it("should use the provided trigger", () => {
    const triggerText = "My custom trigger";

    const Trigger = () => <span>{triggerText}</span>;

    render(
      <DropdownMenu trigger={<Trigger />}>
        <DropdownMenu.Item>Home</DropdownMenu.Item>
      </DropdownMenu>,
    );

    expect(screen.getByText(triggerText)).toBeInTheDocument();
  });

  it("should use the onClick handler on menu item", () => {
    const customHandler = vi.fn();

    render(
      <DropdownMenu>
        <DropdownMenu.Item onClick={customHandler}>Home</DropdownMenu.Item>
      </DropdownMenu>,
    );

    const trigger = screen.getByRole("button");

    fireEvent.click(trigger);

    const item = screen.getByRole("menuitem");

    fireEvent.click(item);

    expect(customHandler).toHaveBeenCalled();
  });

  it("should close menu upon item selection", () => {
    render(
      <DropdownMenu>
        <DropdownMenu.Item>Home</DropdownMenu.Item>
      </DropdownMenu>,
    );

    const trigger = screen.getByRole("button");

    fireEvent.click(trigger);

    const item = screen.getByRole("menuitem");

    fireEvent.click(item);

    expect(trigger).toHaveAttribute("aria-expanded", "false");
  });

  describe("uses correct class names", () => {
    it("uses Mobius class", () => {
      const { container } = render(
        <DropdownMenu>
          <DropdownMenu.Item>Home</DropdownMenu.Item>
        </DropdownMenu>,
      );
      expect(container.firstChild).toHaveClass("mobius");
    });

    it("uses base Mobius class", () => {
      const { container } = render(
        <DropdownMenu>
          <DropdownMenu.Item>Home</DropdownMenu.Item>
        </DropdownMenu>,
      );
      expect(container.firstChild).toHaveClass(CLASS_NAME);
    });

    it("includes custom class name", () => {
      const { container } = render(
        <DropdownMenu className="my-class">
          <DropdownMenu.Item>Home</DropdownMenu.Item>
        </DropdownMenu>,
      );
      expect(container.firstChild).toHaveClass("my-class");
      expect(container.firstChild).toHaveClass(CLASS_NAME);
    });

    it("uses trigger class name", () => {
      render(
        <DropdownMenu className="my-class">
          <DropdownMenu.Item>Home</DropdownMenu.Item>
        </DropdownMenu>,
      );
      expect(screen.getByRole("button")).toHaveClass(TRIGGER_CLASS_NAME);
    });

    it("uses list class name", () => {
      render(
        <DropdownMenu className="my-class">
          <DropdownMenu.Item>Home</DropdownMenu.Item>
        </DropdownMenu>,
      );
      const trigger = screen.getByRole("button");

      fireEvent.click(trigger);

      expect(screen.getByRole("menu")).toHaveClass(LIST_CLASS_NAME);
    });

    it("uses list item class name for text", () => {
      const existingClassName = "custom-class";

      render(
        <DropdownMenu className="my-class">
          <DropdownMenu.Item className={existingClassName}>
            Home
          </DropdownMenu.Item>
        </DropdownMenu>,
      );
      const trigger = screen.getByRole("button");

      fireEvent.click(trigger);

      const menuItem = screen.getByRole("menuitem").firstChild;

      expect(menuItem).toHaveClass(ITEM_CLASS_NAME);
      expect(menuItem).toHaveClass(existingClassName);
    });

    it("uses list item class name for provided component", () => {
      const existingClassName = "home-link";

      render(
        <DropdownMenu className="my-class">
          <DropdownMenu.Item>
            <a href="/home" className={existingClassName}>
              Home
            </a>
          </DropdownMenu.Item>
        </DropdownMenu>,
      );
      const trigger = screen.getByRole("button");

      fireEvent.click(trigger);

      const link = screen.getByRole("link");

      expect(link).toHaveClass(ITEM_CLASS_NAME);
      expect(link).toHaveClass(existingClassName);
    });

    it("uses list item active class name", () => {
      render(
        <DropdownMenu className="my-class">
          <DropdownMenu.Item>Home</DropdownMenu.Item>
        </DropdownMenu>,
      );
      const trigger = screen.getByRole("button");

      fireEvent.click(trigger);

      const item = screen.getByRole("menuitem");

      fireEvent.click(item);

      expect(item.firstChild).toHaveClass(ACTIVE_ITEM_CLASS_NAME);
    });
  });
});
