import { describe, it, expect, afterEach, beforeAll } from "vitest"
import { mount } from "@vue/test-utils"
import { h, nextTick } from "vue"
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectSeparator,
  SelectTrigger,
  SelectValue,
} from "."

beforeAll(() => {
  if (typeof global.ResizeObserver === "undefined") {
    global.ResizeObserver = class {
      observe(): void {}
      unobserve(): void {}
      disconnect(): void {}
    } as unknown as typeof ResizeObserver
  }
})

function mountSelect(rootProps: Record<string, unknown> = {}, triggerProps: Record<string, unknown> = {}) {
  return mount(Select, {
    props: rootProps,
    slots: {
      default: () => [
        h(SelectTrigger, triggerProps, {
          default: () => h(SelectValue, { placeholder: "Select a fruit..." }),
        }),
        h(SelectContent, null, {
          default: () => [
            h(SelectItem, { value: "apple" }, { default: () => "Apple" }),
            h(SelectItem, { value: "banana" }, { default: () => "Banana" }),
            h(SelectItem, { value: "cherry" }, { default: () => "Cherry" }),
          ],
        }),
      ],
    },
  })
}

describe("Select", () => {
  afterEach(() => {
    document.body.innerHTML = ""
  })

  describe("trigger rendering", () => {
    it("renders without errors", () => {
      const wrapper = mountSelect()
      expect(wrapper.exists()).toBe(true)
      wrapper.unmount()
    })

    it("renders the placeholder text", () => {
      const wrapper = mountSelect()
      expect(wrapper.text()).toContain("Select a fruit...")
      wrapper.unmount()
    })

    it("renders a trigger with role=combobox", () => {
      const wrapper = mountSelect()
      expect(wrapper.find('[role="combobox"]').exists()).toBe(true)
      wrapper.unmount()
    })

    it("trigger has base layout classes", () => {
      const wrapper = mountSelect()
      const trigger = wrapper.find('[role="combobox"]')
      const classes = trigger.classes()
      for (const cls of [
        "flex",
        "h-10",
        "w-full",
        "items-center",
        "justify-between",
        "rounded-md",
        "border",
        "border-input",
        "bg-background",
        "px-3",
        "py-2",
        "text-sm",
      ]) {
        expect(classes).toContain(cls)
      }
      wrapper.unmount()
    })

    it("trigger has focus styles", () => {
      const wrapper = mountSelect()
      const trigger = wrapper.find('[role="combobox"]')
      expect(trigger.classes()).toContain("focus:outline-none")
      expect(trigger.classes()).toContain("focus:ring-2")
      expect(trigger.classes()).toContain("focus:ring-ring")
      wrapper.unmount()
    })

    it("trigger has disabled styles", () => {
      const wrapper = mountSelect()
      const trigger = wrapper.find('[role="combobox"]')
      expect(trigger.classes()).toContain("disabled:cursor-not-allowed")
      expect(trigger.classes()).toContain("disabled:opacity-50")
      wrapper.unmount()
    })

    it("trigger renders chevron-down icon", () => {
      const wrapper = mountSelect()
      expect(wrapper.find("svg").exists()).toBe(true)
      wrapper.unmount()
    })

    it("trigger accepts custom class", () => {
      const wrapper = mountSelect({}, { class: "custom-trigger" })
      const trigger = wrapper.find('[role="combobox"]')
      expect(trigger.classes()).toContain("custom-trigger")
      expect(trigger.classes()).toContain("flex")
      wrapper.unmount()
    })
  })

  describe("disabled", () => {
    it("renders a disabled trigger when root.disabled=true", () => {
      const wrapper = mountSelect({ disabled: true })
      const trigger = wrapper.find('[role="combobox"]')
      expect(trigger.attributes("disabled")).toBeDefined()
      wrapper.unmount()
    })
  })

  describe("open content (portal)", () => {
    it("renders items in the portal when defaultOpen=true", async () => {
      const wrapper = mount(Select, {
        props: { defaultOpen: true },
        attachTo: document.body,
        slots: {
          default: () => [
            h(SelectTrigger, null, {
              default: () => h(SelectValue, { placeholder: "Select..." }),
            }),
            h(SelectContent, null, {
              default: () => [
                h(SelectItem, { value: "apple" }, { default: () => "Apple" }),
                h(SelectItem, { value: "banana" }, { default: () => "Banana" }),
                h(SelectItem, { value: "cherry" }, { default: () => "Cherry" }),
              ],
            }),
          ],
        },
      })
      await nextTick()
      const items = document.body.querySelectorAll('[role="option"]')
      expect(items.length).toBe(3)
      expect(items[0]?.textContent).toContain("Apple")
      expect(items[1]?.textContent).toContain("Banana")
      expect(items[2]?.textContent).toContain("Cherry")
      wrapper.unmount()
    })

    it("content has popover styling classes", async () => {
      const wrapper = mount(Select, {
        props: { defaultOpen: true },
        attachTo: document.body,
        slots: {
          default: () => [
            h(SelectTrigger, null, {
              default: () => h(SelectValue, { placeholder: "Select..." }),
            }),
            h(SelectContent, null, {
              default: () =>
                h(SelectItem, { value: "apple" }, { default: () => "Apple" }),
            }),
          ],
        },
      })
      await nextTick()
      const content = document.body.querySelector('[role="listbox"]')
      expect(content).toBeTruthy()
      expect(content?.className).toContain("rounded-md")
      expect(content?.className).toContain("border")
      expect(content?.className).toContain("bg-popover")
      expect(content?.className).toContain("shadow-md")
      wrapper.unmount()
    })

    it("renders SelectItem with base item classes", async () => {
      const wrapper = mount(Select, {
        props: { defaultOpen: true },
        attachTo: document.body,
        slots: {
          default: () => [
            h(SelectTrigger, null, {
              default: () => h(SelectValue, { placeholder: "Select..." }),
            }),
            h(SelectContent, null, {
              default: () =>
                h(SelectItem, { value: "apple" }, { default: () => "Apple" }),
            }),
          ],
        },
      })
      await nextTick()
      const item = document.body.querySelector('[role="option"]')
      expect(item).toBeTruthy()
      for (const cls of [
        "relative",
        "flex",
        "cursor-default",
        "select-none",
        "items-center",
        "rounded-sm",
        "py-1.5",
        "pl-8",
        "pr-2",
        "text-sm",
      ]) {
        expect(item?.className).toContain(cls)
      }
      wrapper.unmount()
    })

    it("renders grouped items with label", async () => {
      const wrapper = mount(Select, {
        props: { defaultOpen: true },
        attachTo: document.body,
        slots: {
          default: () => [
            h(SelectTrigger, null, {
              default: () => h(SelectValue, { placeholder: "Select..." }),
            }),
            h(SelectContent, null, {
              default: () =>
                h(SelectGroup, null, {
                  default: () => [
                    h(SelectLabel, null, { default: () => "Fruits" }),
                    h(SelectItem, { value: "apple" }, { default: () => "Apple" }),
                    h(SelectSeparator),
                    h(SelectItem, { value: "banana" }, { default: () => "Banana" }),
                  ],
                }),
            }),
          ],
        },
      })
      await nextTick()
      expect(document.body.textContent).toContain("Fruits")
      expect(document.body.textContent).toContain("Apple")
      expect(document.body.textContent).toContain("Banana")
      wrapper.unmount()
    })
  })

  describe("v-model", () => {
    it("emits update:modelValue when defaultValue is provided and item changes", () => {
      // We can't reliably synthesize keyboard/pointer to drive selection in
      // jsdom, so we just verify the prop is accepted and the trigger reflects
      // a selected value visually.
      const wrapper = mount(Select, {
        props: { modelValue: "apple" },
        slots: {
          default: () => [
            h(SelectTrigger, null, {
              default: () => h(SelectValue, { placeholder: "Pick..." }),
            }),
            h(SelectContent, null, {
              default: () => [
                h(SelectItem, { value: "apple" }, { default: () => "Apple" }),
              ],
            }),
          ],
        },
      })
      expect(wrapper.exists()).toBe(true)
      wrapper.unmount()
    })
  })

  describe("SelectSeparator", () => {
    it("renders content with items around the separator", async () => {
      const wrapper = mount(Select, {
        props: { defaultOpen: true },
        attachTo: document.body,
        slots: {
          default: () => [
            h(SelectTrigger, null, {
              default: () => h(SelectValue, { placeholder: "Select..." }),
            }),
            h(SelectContent, null, {
              default: () => [
                h(SelectItem, { value: "apple" }, { default: () => "Apple" }),
                h(SelectSeparator),
                h(SelectItem, { value: "banana" }, { default: () => "Banana" }),
              ],
            }),
          ],
        },
      })
      await nextTick()
      expect(document.body.textContent).toContain("Apple")
      expect(document.body.textContent).toContain("Banana")
      wrapper.unmount()
    })
  })
})
