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

describe("Input", () => {
  // Basic rendering tests
  describe("Rendering", () => {
    it("renders input with default props", () => {
      render(<Input />);
      const input = screen.getByRole("textbox");
      expect(input).toBeInTheDocument();
      expect(input).toHaveAttribute("type", "text");
    });

    it("renders with custom className", () => {
      render(<Input className="custom-class" />);
      const input = screen.getByRole("textbox");
      expect(input.parentElement?.parentElement).toHaveClass("custom-class");
    });

    it("renders with different input types", () => {
      const { rerender } = render(<Input type="email" />);
      let input = screen.getByRole("textbox");
      expect(input).toHaveAttribute("type", "email");

      rerender(<Input type="password" />);
      input = screen.getByDisplayValue("") as HTMLInputElement;
      expect(input).toHaveAttribute("type", "password");

      rerender(<Input type="number" />);
      input = screen.getByRole("spinbutton");
      expect(input).toHaveAttribute("type", "number");
    });

    it("renders with different sizes", () => {
      const { rerender } = render(<Input size="sm" />);
      let input = screen.getByRole("textbox");
      expect(input).toHaveClass("h-8");

      rerender(<Input size="md" />);
      input = screen.getByRole("textbox");
      expect(input).toHaveClass("h-10");

      rerender(<Input size="lg" />);
      input = screen.getByRole("textbox");
      expect(input).toHaveClass("h-12");
    });
  });

  // Props tests
  describe("Props", () => {
    it("handles placeholder", () => {
      render(<Input placeholder="Enter text" />);
      const input = screen.getByPlaceholderText("Enter text");
      expect(input).toBeInTheDocument();
    });

    it("handles value and defaultValue", () => {
      const { unmount } = render(
        <Input value="controlled" onChange={() => {}} />
      );
      let input = screen.getByDisplayValue("controlled");
      expect(input).toBeInTheDocument();

      unmount();
      render(<Input defaultValue="uncontrolled" />);
      input = screen.getByDisplayValue("uncontrolled");
      expect(input).toBeInTheDocument();
    });

    it("handles disabled state", () => {
      render(<Input disabled />);
      const input = screen.getByRole("textbox");
      expect(input).toBeDisabled();
    });

    it("handles readOnly state", () => {
      render(<Input readOnly />);
      const input = screen.getByRole("textbox");
      expect(input).toHaveAttribute("readonly");
    });

    it("handles required state", () => {
      render(<Input required />);
      const input = screen.getByRole("textbox");
      expect(input).toHaveAttribute("required");
    });

    it("handles error state", () => {
      render(<Input error errorMessage="Error message" />);
      const input = screen.getByRole("textbox");
      expect(input).toHaveClass("border-destructive");
      expect(screen.getByText("Error message")).toBeInTheDocument();
    });
  });

  // Label and help text tests
  describe("Label and Help Text", () => {
    it("renders with label", () => {
      render(<Input label="Username" />);
      const label = screen.getByText("Username");
      const input = screen.getByRole("textbox");
      expect(label).toBeInTheDocument();
      expect(input).toHaveAttribute("id");
      expect(label).toHaveAttribute("for", input.getAttribute("id"));
    });

    it("renders required indicator with label", () => {
      render(<Input label="Username" required />);
      expect(screen.getByText("*")).toBeInTheDocument();
    });

    it("renders help text", () => {
      render(<Input helpText="Enter your username" />);
      expect(screen.getByText("Enter your username")).toBeInTheDocument();
    });

    it("renders error message with proper ARIA", () => {
      render(<Input error errorMessage="Username is required" />);
      const errorElement = screen.getByText("Username is required");
      expect(errorElement).toHaveAttribute("role", "alert");
      expect(errorElement).toHaveAttribute("aria-live", "polite");
    });
  });

  // Icon tests
  describe("Icons", () => {
    it("renders with left icon", () => {
      const icon = <span data-testid="left-icon">🔍</span>;
      render(<Input leftIcon={icon} />);
      expect(screen.getByTestId("left-icon")).toBeInTheDocument();
    });

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

  // Password toggle tests
  describe("Password Toggle", () => {
    it("renders password toggle button", () => {
      render(<Input type="password" showPasswordToggle />);
      const toggleButton = screen.getByLabelText("Show password");
      expect(toggleButton).toBeInTheDocument();
    });

    it("toggles password visibility", async () => {
      const user = userEvent.setup();
      render(<Input type="password" showPasswordToggle />);

      const input = screen.getByRole("textbox");
      const toggleButton = screen.getByLabelText("Show password");

      expect(input).toHaveAttribute("type", "password");

      await user.click(toggleButton);
      expect(input).toHaveAttribute("type", "text");
      expect(screen.getByLabelText("Hide password")).toBeInTheDocument();

      const hideButton = screen.getByLabelText("Hide password");
      await user.click(hideButton);
      expect(input).toHaveAttribute("type", "password");
      expect(screen.getByLabelText("Show password")).toBeInTheDocument();
    });
  });

  // Interaction tests
  describe("Interactions", () => {
    it("calls onChange when value changes", async () => {
      const handleChange = jest.fn();
      const user = userEvent.setup();
      render(<Input onChange={handleChange} />);

      const input = screen.getByRole("textbox");
      await user.type(input, "hello");

      expect(handleChange).toHaveBeenCalledTimes(5); // Called for each character
    });

    it("calls onFocus when focused", async () => {
      const handleFocus = jest.fn();
      const user = userEvent.setup();
      render(<Input onFocus={handleFocus} />);

      const input = screen.getByRole("textbox");
      await user.click(input);

      expect(handleFocus).toHaveBeenCalledTimes(1);
    });

    it("calls onBlur when blurred", async () => {
      const handleBlur = jest.fn();
      const user = userEvent.setup();
      render(<Input onBlur={handleBlur} />);

      const input = screen.getByRole("textbox");
      await user.click(input);
      await user.tab();

      expect(handleBlur).toHaveBeenCalledTimes(1);
    });
  });

  // Accessibility tests
  describe("Accessibility", () => {
    it("has proper ARIA attributes for error state", () => {
      render(<Input error errorMessage="Invalid input" />);
      const input = screen.getByRole("textbox");
      expect(input).toHaveAttribute("aria-invalid", "true");
    });

    it("links help text with aria-describedby", () => {
      render(<Input helpText="Help text" />);
      const input = screen.getByRole("textbox");
      const helpTextId = input.getAttribute("aria-describedby");
      expect(helpTextId).toBeTruthy();
      expect(screen.getByText("Help text")).toHaveAttribute("id", helpTextId);
    });

    it("supports keyboard navigation", async () => {
      const user = userEvent.setup();
      render(<Input />);
      const input = screen.getByRole("textbox");

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

  // Edge cases
  describe("Edge cases", () => {
    it("forwards ref correctly", () => {
      const ref = React.createRef<HTMLInputElement>();
      render(<Input ref={ref} />);
      expect(ref.current).toBeInstanceOf(HTMLInputElement);
    });

    it("handles multiple class names", () => {
      render(<Input className="class1 class2" />);
      const container =
        screen.getByRole("textbox").parentElement?.parentElement;
      expect(container).toHaveClass("class1", "class2");
    });
  });
});
