import { describe, expect, it, vi, afterEach } from "vitest"
import { render, screen, fireEvent, cleanup } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { Input } from "./Input"
import { TextField } from "react-aria-components"

afterEach(() => {
  cleanup()
})

// Wrap Input in TextField for proper React Aria context
const renderInput = (props: React.ComponentProps<typeof Input>) => {
  return render(
    <TextField>
      <Input {...props} />
    </TextField>
  )
}

// Render Input standalone (without TextField) for testing Input's own behavior
const renderInputStandalone = (props: React.ComponentProps<typeof Input>) => {
  return render(<Input {...props} />)
}

describe("Input", () => {
  describe("props", () => {
    it("applies required attribute", () => {
      renderInput({ label: "Email", required: true })
      expect(screen.getByRole("textbox")).toHaveProperty("required", true)
    })

    it("applies readOnly attribute", () => {
      renderInput({ label: "Email", readOnly: true })
      expect(screen.getByRole("textbox")).toHaveProperty("readOnly", true)
    })

    it("applies placeholder for floating label when provided", () => {
      renderInput({ label: "Email", placeholder: "Enter email" })
      expect(screen.getByRole("textbox").getAttribute("placeholder")).toBe(
        "Enter email"
      )
    })

    it("applies empty placeholder for top label by default", () => {
      renderInput({ label: "Email", labelPosition: "top" })
      expect(screen.getByRole("textbox").getAttribute("placeholder")).toBe("")
    })

    it("applies custom id", () => {
      // Use standalone render since TextField overrides id via context
      renderInputStandalone({ label: "Email", id: "custom-id" })
      expect(screen.getByRole("textbox").getAttribute("id")).toBe("custom-id")
    })

    it("applies aria-describedby", () => {
      renderInput({ label: "Email", "aria-describedby": "error-message" })
      expect(screen.getByRole("textbox").getAttribute("aria-describedby")).toBe(
        "error-message"
      )
    })
  })

  describe("clear content button", () => {
    it("does not show clear button when showClearContentIcon is false", () => {
      renderInput({
        label: "Email",
        value: "test",
        onChange: vi.fn(),
        showClearContentIcon: false,
      })
      expect(screen.queryByLabelText("Clear content")).toBeNull()
    })

    it("does not show clear button when input is empty", () => {
      renderInput({
        label: "Email",
        value: "",
        onChange: vi.fn(),
        showClearContentIcon: true,
      })
      expect(screen.queryByLabelText("Clear content")).toBeNull()
    })

    it("shows clear button when input has value and showClearContentIcon is true", () => {
      renderInput({
        label: "Email",
        value: "test",
        onChange: vi.fn(),
        showClearContentIcon: true,
      })
      expect(screen.getByLabelText("Clear content")).toBeTruthy()
    })
  })

  describe("icons", () => {
    it("renders left icon when provided", () => {
      renderInput({
        label: "Search",
        // eslint-disable-next-line formatjs/no-literal-string-in-jsx
        leftIcon: <span data-testid="left-icon">🔍</span>,
      })
      expect(screen.getByTestId("left-icon")).toBeTruthy()
    })

    it("renders right icon when provided", () => {
      renderInput({
        label: "Password",
        // eslint-disable-next-line formatjs/no-literal-string-in-jsx
        rightIcon: <span data-testid="right-icon">👁</span>,
      })
      expect(screen.getByTestId("right-icon")).toBeTruthy()
    })

    it("hides right icon when clear button is shown", () => {
      renderInput({
        label: "Email",
        value: "test",
        onChange: vi.fn(),
        showClearContentIcon: true,
        // eslint-disable-next-line formatjs/no-literal-string-in-jsx
        rightIcon: <span data-testid="right-icon">👁</span>,
      })
      expect(screen.queryByTestId("right-icon")).toBeNull()
      expect(screen.getByLabelText("Clear content")).toBeTruthy()
    })

    it("shows right icon when clear button condition not met", () => {
      renderInput({
        label: "Email",
        value: "",
        onChange: vi.fn(),
        showClearContentIcon: true,
        // eslint-disable-next-line formatjs/no-literal-string-in-jsx
        rightIcon: <span data-testid="right-icon">👁</span>,
      })
      expect(screen.getByTestId("right-icon")).toBeTruthy()
    })
  })

  describe("controlled input", () => {
    it("displays the controlled value", () => {
      renderInput({
        label: "Email",
        value: "test@example.com",
        onChange: vi.fn(),
      })
      expect(screen.getByRole("textbox")).toHaveProperty(
        "value",
        "test@example.com"
      )
    })

    it("calls onChange when typing", async () => {
      const onChange = vi.fn()
      renderInput({ label: "Email", value: "", onChange })

      const input = screen.getByRole("textbox")
      await userEvent.type(input, "a")

      expect(onChange).toHaveBeenCalled()
    })

    it("does not change value without onChange updating it", () => {
      const onChange = vi.fn()
      renderInput({ label: "Email", value: "initial", onChange })

      const input = screen.getByRole("textbox")
      fireEvent.change(input, { target: { value: "changed" } })

      // Value stays the same because it's controlled
      expect(input).toHaveProperty("value", "initial")
    })
  })

  describe("ref forwarding", () => {
    it("forwards ref to the input element", () => {
      const ref = { current: null as HTMLInputElement | null }
      render(
        <TextField>
          <Input label="Email" ref={ref} />
        </TextField>
      )
      expect(ref.current).toBeInstanceOf(HTMLInputElement)
    })

    it("allows focusing via ref", () => {
      const ref = { current: null as HTMLInputElement | null }
      render(
        <TextField>
          <Input label="Email" ref={ref} />
        </TextField>
      )
      ref.current?.focus()
      expect(document.activeElement).toBe(ref.current)
    })
  })
})
