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

function mountGroup(props: Record<string, unknown> = {}) {
  return mount(RadioGroup, {
    props: { defaultValue: "apple", ...props },
    slots: {
      default: () => [
        h(RadioGroupItem, { value: "apple" }),
        h(RadioGroupItem, { value: "banana" }),
        h(RadioGroupItem, { value: "cherry" }),
      ],
    },
  })
}

describe("RadioGroup", () => {
  describe("group rendering", () => {
    it("mounts without error", () => {
      const wrapper = mountGroup()
      expect(wrapper.exists()).toBe(true)
    })

    it("renders role=radiogroup root", () => {
      const wrapper = mountGroup()
      expect(wrapper.find('[role="radiogroup"]').exists()).toBe(true)
    })

    it("renders all child items with role=radio", () => {
      const wrapper = mountGroup()
      expect(wrapper.findAll('[role="radio"]')).toHaveLength(3)
    })

    it("has base group classes", () => {
      const wrapper = mountGroup()
      const root = wrapper.find('[role="radiogroup"]')
      expect(root.classes()).toContain("grid")
      expect(root.classes()).toContain("gap-2")
    })

    it("accepts custom class on the group", () => {
      const wrapper = mountGroup({ class: "flex-row inline-flex" })
      const root = wrapper.find('[role="radiogroup"]')
      expect(root.classes()).toContain("inline-flex")
      expect(root.classes()).toContain("flex-row")
    })
  })

  describe("default selection", () => {
    it("marks the defaultValue item as checked", () => {
      const wrapper = mountGroup()
      const items = wrapper.findAll('[role="radio"]')
      const checked = items.find((i) => i.attributes("data-state") === "checked")
      expect(checked).toBeTruthy()
      expect(checked?.attributes("value")).toBe("apple")
    })

    it("marks all other items as unchecked", () => {
      const wrapper = mountGroup()
      const items = wrapper.findAll('[role="radio"]')
      const unchecked = items.filter((i) => i.attributes("data-state") === "unchecked")
      expect(unchecked).toHaveLength(2)
    })

    it("renders all items unchecked when no defaultValue", () => {
      const wrapper = mount(RadioGroup, {
        slots: {
          default: () => [
            h(RadioGroupItem, { value: "a" }),
            h(RadioGroupItem, { value: "b" }),
          ],
        },
      })
      const items = wrapper.findAll('[role="radio"]')
      for (const item of items) {
        expect(item.attributes("data-state")).toBe("unchecked")
      }
    })
  })

  describe("v-model / controlled", () => {
    it("reflects controlled modelValue", () => {
      const wrapper = mountGroup({ modelValue: "banana" })
      const checked = wrapper
        .findAll('[role="radio"]')
        .find((i) => i.attributes("data-state") === "checked")
      expect(checked?.attributes("value")).toBe("banana")
    })

    it("updates when modelValue prop changes", async () => {
      const wrapper = mountGroup({ modelValue: "apple" })
      await wrapper.setProps({ modelValue: "cherry" })
      const checked = wrapper
        .findAll('[role="radio"]')
        .find((i) => i.attributes("data-state") === "checked")
      expect(checked?.attributes("value")).toBe("cherry")
    })

    it("emits update:modelValue when an item is clicked", async () => {
      const wrapper = mountGroup({ modelValue: "apple" })
      const items = wrapper.findAll('[role="radio"]')
      await items[1]!.trigger("click")
      expect(wrapper.emitted("update:modelValue")).toBeTruthy()
      expect(wrapper.emitted("update:modelValue")![0]).toEqual(["banana"])
    })
  })

  describe("RadioGroupItem", () => {
    it("has base item classes", () => {
      const wrapper = mountGroup()
      const item = wrapper.find('[role="radio"]')
      const classes = item.classes()
      for (const cls of [
        "peer",
        "aspect-square",
        "h-4",
        "w-4",
        "rounded-full",
        "border",
        "border-primary",
        "text-primary",
      ]) {
        expect(classes).toContain(cls)
      }
    })

    it("has focus-visible classes on item", () => {
      const wrapper = mountGroup()
      const item = wrapper.find('[role="radio"]')
      expect(item.classes()).toContain("focus-visible:ring-2")
      expect(item.classes()).toContain("focus-visible:ring-ring")
      expect(item.classes()).toContain("focus-visible:ring-offset-2")
    })

    it("has disabled classes on item", () => {
      const wrapper = mountGroup()
      const item = wrapper.find('[role="radio"]')
      expect(item.classes()).toContain("disabled:cursor-not-allowed")
      expect(item.classes()).toContain("disabled:opacity-50")
    })

    it("accepts custom class on item", () => {
      const wrapper = mount(RadioGroup, {
        props: { defaultValue: "x" },
        slots: {
          default: () => [
            h(RadioGroupItem, { value: "x", class: "size-6 my-custom" }),
          ],
        },
      })
      const item = wrapper.find('[role="radio"]')
      expect(item.classes()).toContain("my-custom")
      expect(item.classes()).toContain("size-6")
    })
  })

  describe("disabled state", () => {
    it("forwards disabled on the group", () => {
      const wrapper = mountGroup({ disabled: true })
      const items = wrapper.findAll('[role="radio"]')
      for (const item of items) {
        expect(item.attributes("disabled")).toBe("")
      }
    })

    it("forwards disabled on a single item", () => {
      const wrapper = mount(RadioGroup, {
        props: { defaultValue: "a" },
        slots: {
          default: () => [
            h(RadioGroupItem, { value: "a" }),
            h(RadioGroupItem, { value: "b", disabled: true }),
          ],
        },
      })
      const items = wrapper.findAll('[role="radio"]')
      expect(items[0]!.attributes("disabled")).toBeUndefined()
      expect(items[1]!.attributes("disabled")).toBe("")
    })
  })
})
