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

describe("Button", () => {
  describe("default rendering", () => {
    it("renders with default variant and size", () => {
      const wrapper = mount(Button, { slots: { default: "Click me" } })
      expect(wrapper.text()).toBe("Click me")
      expect(wrapper.classes()).toContain("bg-primary")
      expect(wrapper.classes()).toContain("text-primary-foreground")
      expect(wrapper.classes()).toContain("h-10")
      expect(wrapper.classes()).toContain("px-4")
    })

    it("renders as a button element by default", () => {
      const wrapper = mount(Button, { slots: { default: "Click me" } })
      expect(wrapper.element.tagName).toBe("BUTTON")
    })
  })

  describe("variants", () => {
    it.each([
      ["default", ["bg-primary", "text-primary-foreground"]],
      ["secondary", ["bg-secondary", "text-secondary-foreground"]],
      ["destructive", ["bg-destructive", "text-destructive-foreground"]],
      ["outline", ["border", "border-input", "bg-background"]],
      ["ghost", ["hover:bg-accent", "hover:text-accent-foreground"]],
      ["link", ["text-primary", "underline-offset-4", "hover:underline"]],
      ["success", ["bg-success", "text-success-foreground"]],
      ["warning", ["bg-warning", "text-warning-foreground"]],
      ["info", ["bg-info", "text-info-foreground"]],
    ] as const)("renders %s variant with expected classes", (variant, expected) => {
      const wrapper = mount(Button, {
        props: { variant },
        slots: { default: variant },
      })
      for (const cls of expected) {
        expect(wrapper.classes()).toContain(cls)
      }
    })

    it("does not apply primary classes to ghost variant", () => {
      const wrapper = mount(Button, { props: { variant: "ghost" } })
      expect(wrapper.classes()).not.toContain("bg-primary")
    })
  })

  describe("sizes", () => {
    it.each([
      ["default", ["h-10", "px-4", "py-2"]],
      ["sm", ["h-9", "px-3"]],
      ["lg", ["h-11", "px-8"]],
      ["xl", ["h-12", "px-10"]],
      ["icon", ["h-10", "w-10"]],
      ["icon-sm", ["size-9"]],
      ["icon-lg", ["size-11"]],
    ] as const)("renders %s size with expected classes", (size, expected) => {
      const wrapper = mount(Button, { props: { size } })
      for (const cls of expected) {
        expect(wrapper.classes()).toContain(cls)
      }
    })
  })

  describe("custom class", () => {
    it("merges custom class with default classes", () => {
      const wrapper = mount(Button, { props: { class: "my-custom-class" } })
      expect(wrapper.classes()).toContain("my-custom-class")
      expect(wrapper.classes()).toContain("bg-primary")
      expect(wrapper.classes()).toContain("inline-flex")
    })

    it("allows overriding default classes via tailwind-merge", () => {
      const wrapper = mount(Button, { props: { class: "bg-blue-500" } })
      expect(wrapper.classes()).toContain("bg-blue-500")
      expect(wrapper.classes()).not.toContain("bg-primary")
    })
  })

  describe("slot content", () => {
    it("renders text", () => {
      const wrapper = mount(Button, { slots: { default: "Hello" } })
      expect(wrapper.text()).toBe("Hello")
    })

    it("renders HTML", () => {
      const wrapper = mount(Button, {
        slots: { default: '<span class="icon">+</span> Add' },
      })
      expect(wrapper.find(".icon").exists()).toBe(true)
      expect(wrapper.text()).toContain("Add")
    })

    it("renders component", () => {
      const IconComponent = { template: '<svg class="test-icon"></svg>' }
      const wrapper = mount(Button, { slots: { default: h(IconComponent) } })
      expect(wrapper.find(".test-icon").exists()).toBe(true)
    })

    it("renders empty when no slot", () => {
      const wrapper = mount(Button)
      expect(wrapper.text()).toBe("")
    })
  })

  describe("asChild prop", () => {
    it("renders the child element when asChild is true", () => {
      const wrapper = mount(Button, {
        props: { asChild: true },
        slots: {
          default: h("a", { href: "/test", class: "child-link" }, "Link"),
        },
      })
      expect(wrapper.find("a").exists()).toBe(true)
      expect(wrapper.find("a").attributes("href")).toBe("/test")
    })

    it("passes button styles to the child element when asChild is true", () => {
      const wrapper = mount(Button, {
        props: { asChild: true, variant: "secondary" },
        slots: { default: h("a", { href: "/test" }, "Styled") },
      })
      expect(wrapper.find("a").classes()).toContain("bg-secondary")
    })
  })

  describe("as prop", () => {
    it("renders as an anchor when as='a'", () => {
      const wrapper = mount(Button, {
        props: { as: "a" },
        attrs: { href: "/test" },
        slots: { default: "Link" },
      })
      expect(wrapper.element.tagName).toBe("A")
      expect(wrapper.attributes("href")).toBe("/test")
    })

    it("renders as a div when as='div'", () => {
      const wrapper = mount(Button, {
        props: { as: "div" },
      })
      expect(wrapper.element.tagName).toBe("DIV")
    })
  })

  describe("base styles + a11y", () => {
    it("has base layout, transition, and focus-visible classes", () => {
      const wrapper = mount(Button)
      const classes = wrapper.classes()
      for (const cls of [
        "inline-flex",
        "items-center",
        "justify-center",
        "rounded-md",
        "text-sm",
        "font-medium",
        "transition-colors",
        "focus-visible:outline-none",
        "focus-visible:ring-2",
        "focus-visible:ring-ring",
        "disabled:pointer-events-none",
        "disabled:opacity-50",
      ]) {
        expect(classes).toContain(cls)
      }
    })
  })
})
