import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Button } from "../Button";

describe("Button", () => {
  // Basic rendering tests
  describe("Rendering", () => {
    it("renders button with children", () => {
      render(<Button>Click me</Button>);
      expect(
        screen.getByRole("button", { name: "Click me" })
      ).toBeInTheDocument();
    });

    it("renders with custom className", () => {
      render(<Button className="custom-class">Button</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveClass("custom-class");
    });

    it("renders with different variants", () => {
      const { rerender } = render(<Button variant="primary">Primary</Button>);
      let button = screen.getByRole("button");
      expect(button).toHaveClass("bg-primary");

      rerender(<Button variant="secondary">Secondary</Button>);
      button = screen.getByRole("button");
      expect(button).toHaveClass("bg-secondary");

      rerender(<Button variant="outline">Outline</Button>);
      button = screen.getByRole("button");
      expect(button).toHaveClass("border-input");

      rerender(<Button variant="ghost">Ghost</Button>);
      button = screen.getByRole("button");
      expect(button).toHaveClass("hover:bg-accent");

      rerender(<Button variant="danger">Danger</Button>);
      button = screen.getByRole("button");
      expect(button).toHaveClass("bg-destructive");
    });

    it("renders with different sizes", () => {
      const { rerender } = render(<Button size="sm">Small</Button>);
      let button = screen.getByRole("button");
      expect(button).toHaveClass("h-9");

      rerender(<Button size="md">Medium</Button>);
      button = screen.getByRole("button");
      expect(button).toHaveClass("h-10");

      rerender(<Button size="lg">Large</Button>);
      button = screen.getByRole("button");
      expect(button).toHaveClass("h-11");
    });
  });

  // Props tests
  describe("Props", () => {
    it("handles disabled state", () => {
      render(<Button disabled>Disabled</Button>);
      const button = screen.getByRole("button");
      expect(button).toBeDisabled();
      expect(button).toHaveClass("disabled:opacity-50");
    });

    it("handles loading state", () => {
      render(<Button loading>Loading</Button>);
      const button = screen.getByRole("button");
      expect(button).toBeDisabled();
      expect(screen.getByRole("status")).toBeInTheDocument();
    });

    it("renders with fullWidth", () => {
      render(<Button fullWidth>Full Width</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveClass("w-full");
    });

    it("renders with leftIcon", () => {
      const icon = <span data-testid="left-icon">←</span>;
      render(<Button leftIcon={icon}>With Icon</Button>);
      expect(screen.getByTestId("left-icon")).toBeInTheDocument();
    });

    it("renders with rightIcon", () => {
      const icon = <span data-testid="right-icon">→</span>;
      render(<Button rightIcon={icon}>With Icon</Button>);
      expect(screen.getByTestId("right-icon")).toBeInTheDocument();
    });

    it("renders with both icons", () => {
      const leftIcon = <span data-testid="left-icon">←</span>;
      const rightIcon = <span data-testid="right-icon">→</span>;
      render(
        <Button leftIcon={leftIcon} rightIcon={rightIcon}>
          With Icons
        </Button>
      );
      expect(screen.getByTestId("left-icon")).toBeInTheDocument();
      expect(screen.getByTestId("right-icon")).toBeInTheDocument();
    });
  });

  // Accessibility tests
  describe("Accessibility", () => {
    it("supports aria-label", () => {
      render(<Button ariaLabel="Custom label">Button</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveAttribute("aria-label", "Custom label");
    });

    it("supports aria-describedby", () => {
      render(<Button ariaDescribedBy="description">Button</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveAttribute("aria-describedby", "description");
    });

    it("supports aria-expanded", () => {
      render(<Button ariaExpanded={true}>Button</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveAttribute("aria-expanded", "true");
    });

    it("supports aria-pressed", () => {
      render(<Button ariaPressed={true}>Button</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveAttribute("aria-pressed", "true");
    });

    it("has proper focus management", async () => {
      const user = userEvent.setup();
      render(<Button>Focusable</Button>);
      const button = screen.getByRole("button");

      await user.tab();
      expect(button).toHaveFocus();
    });

    it("announces loading state to screen readers", () => {
      render(<Button loading>Loading Button</Button>);
      const loadingIndicator = screen.getByRole("status");
      expect(loadingIndicator).toHaveAttribute("aria-label", "Loading");
    });
  });

  // Interaction tests
  describe("Interactions", () => {
    it("calls onClick when clicked", async () => {
      const handleClick = jest.fn();
      const user = userEvent.setup();
      render(<Button onClick={handleClick}>Click me</Button>);

      await user.click(screen.getByRole("button"));
      expect(handleClick).toHaveBeenCalledTimes(1);
    });

    it("does not call onClick when disabled", async () => {
      const handleClick = jest.fn();
      const user = userEvent.setup();
      render(
        <Button onClick={handleClick} disabled>
          Disabled
        </Button>
      );

      await user.click(screen.getByRole("button"));
      expect(handleClick).not.toHaveBeenCalled();
    });

    it("does not call onClick when loading", async () => {
      const handleClick = jest.fn();
      const user = userEvent.setup();
      render(
        <Button onClick={handleClick} loading>
          Loading
        </Button>
      );

      await user.click(screen.getByRole("button"));
      expect(handleClick).not.toHaveBeenCalled();
    });

    it("supports keyboard navigation (Enter)", async () => {
      const handleClick = jest.fn();
      const user = userEvent.setup();
      render(<Button onClick={handleClick}>Button</Button>);

      const button = screen.getByRole("button");
      button.focus();
      await user.keyboard("{Enter}");
      expect(handleClick).toHaveBeenCalledTimes(1);
    });

    it("supports keyboard navigation (Space)", async () => {
      const handleClick = jest.fn();
      const user = userEvent.setup();
      render(<Button onClick={handleClick}>Button</Button>);

      const button = screen.getByRole("button");
      button.focus();
      await user.keyboard(" ");
      expect(handleClick).toHaveBeenCalledTimes(1);
    });
  });

  // Button type tests
  describe("Button types", () => {
    it("renders as submit button", () => {
      render(<Button type="submit">Submit</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveAttribute("type", "submit");
    });

    it("renders as reset button", () => {
      render(<Button type="reset">Reset</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveAttribute("type", "reset");
    });

    it("defaults to button type", () => {
      render(<Button>Button</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveAttribute("type", "button");
    });
  });

  // Edge cases
  describe("Edge cases", () => {
    it("handles empty children", () => {
      render(<Button />);
      const button = screen.getByRole("button");
      expect(button).toBeInTheDocument();
      expect(button).toHaveTextContent("");
    });

    it("handles multiple class names", () => {
      render(<Button className="class1 class2">Button</Button>);
      const button = screen.getByRole("button");
      expect(button).toHaveClass("class1", "class2");
    });

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