import { describe, it, expect } from "vitest"
import { mount } from "@vue/test-utils"
import { Input } from "."

describe("Input", () => {
  describe("default rendering", () => {
    it("renders as an input element", () => {
      const wrapper = mount(Input)
      expect(wrapper.element.tagName).toBe("INPUT")
    })

    it("has base layout classes", () => {
      const wrapper = mount(Input)
      const classes = wrapper.classes()
      for (const cls of [
        "flex",
        "h-10",
        "w-full",
        "rounded-md",
        "border",
        "border-input",
        "bg-background",
        "px-3",
        "py-2",
        "text-sm",
      ]) {
        expect(classes).toContain(cls)
      }
    })

    it("has focus-visible classes", () => {
      const wrapper = mount(Input)
      const classes = wrapper.classes()
      for (const cls of [
        "focus-visible:outline-none",
        "focus-visible:ring-2",
        "focus-visible:ring-ring",
        "focus-visible:ring-offset-2",
      ]) {
        expect(classes).toContain(cls)
      }
    })

    it("has disabled classes", () => {
      const wrapper = mount(Input)
      expect(wrapper.classes()).toContain("disabled:cursor-not-allowed")
      expect(wrapper.classes()).toContain("disabled:opacity-50")
    })

    it("has placeholder classes", () => {
      const wrapper = mount(Input)
      expect(wrapper.classes()).toContain("placeholder:text-muted-foreground")
    })

    it("has file input classes", () => {
      const wrapper = mount(Input)
      const classes = wrapper.classes()
      for (const cls of [
        "file:border-0",
        "file:bg-transparent",
        "file:text-sm",
        "file:font-medium",
        "file:text-foreground",
      ]) {
        expect(classes).toContain(cls)
      }
    })
  })

  describe("v-model / modelValue", () => {
    it("accepts string modelValue", () => {
      const wrapper = mount(Input, { props: { modelValue: "hello" } })
      expect((wrapper.element as HTMLInputElement).value).toBe("hello")
    })

    it("accepts number modelValue", () => {
      const wrapper = mount(Input, { props: { modelValue: 42 } })
      expect((wrapper.element as HTMLInputElement).value).toBe("42")
    })

    it("uses defaultValue when no modelValue is provided", () => {
      const wrapper = mount(Input, { props: { defaultValue: "fallback" } })
      expect((wrapper.element as HTMLInputElement).value).toBe("fallback")
    })

    it("emits update:modelValue when typing", async () => {
      const wrapper = mount(Input, { props: { modelValue: "" } })
      await wrapper.setValue("typed")
      expect(wrapper.emitted("update:modelValue")).toBeTruthy()
      expect(wrapper.emitted("update:modelValue")![0]).toEqual(["typed"])
    })

    it("updates element value when modelValue prop changes", async () => {
      const wrapper = mount(Input, { props: { modelValue: "first" } })
      expect((wrapper.element as HTMLInputElement).value).toBe("first")
      await wrapper.setProps({ modelValue: "second" })
      expect((wrapper.element as HTMLInputElement).value).toBe("second")
    })
  })

  describe("type attribute forwarding", () => {
    it.each([
      ["text"],
      ["email"],
      ["password"],
      ["number"],
      ["tel"],
      ["url"],
      ["search"],
      ["file"],
    ] as const)("forwards type=\"%s\"", (type) => {
      const wrapper = mount(Input, { attrs: { type } })
      expect(wrapper.attributes("type")).toBe(type)
    })
  })

  describe("custom class", () => {
    it("merges custom class with base classes", () => {
      const wrapper = mount(Input, { props: { class: "my-custom" } })
      expect(wrapper.classes()).toContain("my-custom")
      expect(wrapper.classes()).toContain("flex")
      expect(wrapper.classes()).toContain("h-10")
    })

    it("allows overriding base classes via tailwind-merge", () => {
      const wrapper = mount(Input, { props: { class: "h-14 bg-blue-500" } })
      expect(wrapper.classes()).toContain("h-14")
      expect(wrapper.classes()).toContain("bg-blue-500")
      expect(wrapper.classes()).not.toContain("h-10")
      expect(wrapper.classes()).not.toContain("bg-background")
    })
  })

  describe("HTML attribute forwarding", () => {
    it("forwards disabled", () => {
      const wrapper = mount(Input, { attrs: { disabled: true } })
      expect(wrapper.attributes("disabled")).toBe("")
    })

    it("forwards readonly", () => {
      const wrapper = mount(Input, { attrs: { readonly: true } })
      expect(wrapper.attributes("readonly")).toBe("")
    })

    it("forwards required", () => {
      const wrapper = mount(Input, { attrs: { required: true } })
      expect(wrapper.attributes("required")).toBe("")
    })

    it("forwards placeholder", () => {
      const wrapper = mount(Input, { attrs: { placeholder: "Type here" } })
      expect(wrapper.attributes("placeholder")).toBe("Type here")
    })

    it("forwards id and name", () => {
      const wrapper = mount(Input, {
        attrs: { id: "email-field", name: "email" },
      })
      expect(wrapper.attributes("id")).toBe("email-field")
      expect(wrapper.attributes("name")).toBe("email")
    })

    it("forwards aria attributes", () => {
      const wrapper = mount(Input, {
        attrs: { "aria-label": "Email", "aria-describedby": "hint" },
      })
      expect(wrapper.attributes("aria-label")).toBe("Email")
      expect(wrapper.attributes("aria-describedby")).toBe("hint")
    })

    it("forwards maxlength", () => {
      const wrapper = mount(Input, { attrs: { maxlength: "10" } })
      expect(wrapper.attributes("maxlength")).toBe("10")
    })
  })

  describe("user interactions", () => {
    it("emits focus event", async () => {
      const wrapper = mount(Input)
      await wrapper.trigger("focus")
      expect(wrapper.emitted("focus")).toBeTruthy()
    })

    it("emits blur event", async () => {
      const wrapper = mount(Input)
      await wrapper.trigger("blur")
      expect(wrapper.emitted("blur")).toBeTruthy()
    })

    it("emits keydown event", async () => {
      const wrapper = mount(Input)
      await wrapper.trigger("keydown", { key: "Enter" })
      expect(wrapper.emitted("keydown")).toBeTruthy()
    })
  })
})
