import { createRef } from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { chevronDown } from "@simplybusiness/icons";
import { Icon } from "../Icon";
import { Button } from ".";

const CLASS_NAME = "mobius-button";

describe("Button", () => {
  it("should render without errors", () => {
    const error = vi.spyOn(global.console, "error");
    const onPress = vi.fn();
    render(<Button onPress={onPress}>Sample text</Button>);
    expect(error).not.toHaveBeenCalled();
  });

  it("should call the onPress", async () => {
    const onPress = vi.fn();
    const { getByText } = render(<Button onPress={onPress}>Press</Button>);
    await userEvent.click(getByText("Press"));
    expect(onPress).toHaveBeenCalled();
  });

  it("should call the onClick", async () => {
    const onClick = vi.fn();
    const { getByText } = render(<Button onClick={onClick}>Click</Button>);
    await userEvent.click(getByText("Click"));
    expect(onClick).toHaveBeenCalled();
  });

  describe("given icon is provided", () => {
    it("renders the icon", () => {
      const { container } = render(
        <Button>
          <Icon icon={chevronDown} />
          Icon on the left
        </Button>,
      );
      const icon = container.querySelector("[data-icon='chevron-down']");

      expect(icon).toBeInTheDocument();
    });
  });

  describe("given the button is set to 'loading'", () => {
    it("should render the loading icon", () => {
      const BUTTON_TEXT = "Click me";

      const { container } = render(
        <Button isLoading>
          {BUTTON_TEXT}
          <Icon icon={chevronDown} />
        </Button>,
      );

      const button = screen.getByRole("button");
      const loadingIcon = container.querySelector("[data-icon='loading']");
      const loadingText = screen.getByText("Loading");

      expect(loadingIcon).toBeInTheDocument();
      expect(loadingText).toBeInTheDocument();
      expect(button).toHaveClass("--is-loading");
    });
  });

  describe("given the button is set to 'loading' and 'success'", () => {
    it("renders only 'loading' state", () => {
      render(<Button isLoading isSuccess />);

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

      expect(button).toHaveClass("--is-loading");
      expect(button).not.toHaveClass("--is-success");
    });
  });

  describe("keyboard interactions", () => {
    it("activates on Enter key", async () => {
      const onPress = vi.fn();
      render(<Button onPress={onPress}>Click</Button>);
      await userEvent.tab();
      await userEvent.keyboard("[Enter]");
      expect(onPress).toHaveBeenCalled();
    });

    it("activates on Space key", async () => {
      const onPress = vi.fn();
      render(<Button onPress={onPress}>Click</Button>);
      await userEvent.tab();
      await userEvent.keyboard("[Space]");
      expect(onPress).toHaveBeenCalled();
    });
  });

  describe("when disabled", () => {
    it("does not fire onPress", async () => {
      const onPress = vi.fn();
      render(
        <Button onPress={onPress} isDisabled>
          Click
        </Button>,
      );
      await userEvent.click(screen.getByRole("button"));
      expect(onPress).not.toHaveBeenCalled();
    });

    it("does not fire onClick", async () => {
      const onClick = vi.fn();
      render(
        <Button onClick={onClick} isDisabled>
          Click
        </Button>,
      );
      await userEvent.click(screen.getByRole("button"));
      expect(onClick).not.toHaveBeenCalled();
    });

    it("has aria-disabled attribute", () => {
      render(<Button isDisabled>Click</Button>);
      expect(screen.getByRole("button")).toHaveAttribute(
        "aria-disabled",
        "true",
      );
    });

    it("is not focusable", () => {
      render(<Button isDisabled>Click</Button>);
      expect(screen.getByRole("button")).toHaveAttribute("tabIndex", "-1");
    });
  });

  describe("uses correct class names", () => {
    it("uses Mobius class", () => {
      render(<Button />);
      expect(screen.getByRole("button")).toHaveClass("mobius");
    });

    it("uses base Mobius class", () => {
      render(<Button />);
      expect(screen.getByRole("button")).toHaveClass(CLASS_NAME);
    });

    it("includes disabled class name", () => {
      render(<Button isDisabled />);
      expect(screen.getByRole("button")).toHaveClass("--is-disabled");
    });

    it("includes custom class name", () => {
      render(<Button className="my-class" />);

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

      expect(button).toHaveClass(CLASS_NAME);
      expect(button).toHaveClass("my-class");
    });

    it("uses --variant-link class", () => {
      render(<Button variant="link" />);

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

      expect(button).toHaveClass("--variant-link");
    });

    it.each(["sm", "md", "lg"] as const)("applies --size-%s class", size => {
      render(<Button size={size} />);
      expect(screen.getByRole("button")).toHaveClass(`--size-${size}`);
    });

    it("applies --is-loading class when isLoading is true", () => {
      render(<Button isLoading>Loading</Button>);
      expect(screen.getByRole("button")).toHaveClass("--is-loading");
    });

    it("applies --is-success class when isSuccess is true", () => {
      render(<Button isSuccess>Done</Button>);
      expect(screen.getByRole("button")).toHaveClass("--is-success");
    });
  });

  it("forwards ref", () => {
    const ref = createRef<HTMLButtonElement>();
    render(<Button ref={ref}>Text</Button>);
    expect(ref.current).toBeInstanceOf(HTMLButtonElement);
  });
});
