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 { TextArea } from "./TextArea"
import { TextField } from "react-aria-components"

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

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

// Render TextArea standalone for testing its own behavior
const renderTextAreaStandalone = (
  props: React.ComponentProps<typeof TextArea>
) => {
  return render(<TextArea {...props} />)
}

describe("TextArea", () => {
  describe("props", () => {
    it("applies required attribute", () => {
      renderTextArea({ label: "Description", required: true })
      expect(screen.getByRole("textbox")).toHaveProperty("required", true)
    })

    it("applies placeholder when provided", () => {
      renderTextArea({ label: "Description", placeholder: "Enter description" })
      expect(screen.getByRole("textbox").getAttribute("placeholder")).toBe(
        "Enter description"
      )
    })

    it("applies empty placeholder by default", () => {
      renderTextArea({ label: "Description" })
      expect(screen.getByRole("textbox").getAttribute("placeholder")).toBe("")
    })

    it("applies custom id", () => {
      renderTextAreaStandalone({ label: "Description", id: "custom-id" })
      expect(screen.getByRole("textbox").getAttribute("id")).toBe("custom-id")
    })
  })

  describe("label", () => {
    it("renders label when provided", () => {
      renderTextArea({ label: "Description" })
      expect(screen.getByText("Description")).toBeTruthy()
    })

    it("does not render label when not provided", () => {
      renderTextArea({})
      expect(screen.queryByText("Description")).toBeNull()
    })
  })

  describe("controlled textarea", () => {
    it("displays the controlled value", () => {
      renderTextArea({
        label: "Description",
        value: "Some text content",
        onChange: vi.fn(),
      })
      expect(screen.getByRole("textbox")).toHaveProperty(
        "value",
        "Some text content"
      )
    })

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

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

      expect(onChange).toHaveBeenCalled()
    })

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

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

      expect(textarea).toHaveProperty("value", "initial")
    })
  })

  describe("ref forwarding", () => {
    it("forwards ref to the textarea element", () => {
      const ref = { current: null as HTMLTextAreaElement | null }
      render(
        <TextField>
          <TextArea label="Description" ref={ref} />
        </TextField>
      )
      expect(ref.current).toBeInstanceOf(HTMLTextAreaElement)
    })

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