import { describe, it, expect } from "vitest"
import { mount } from "@vue/test-utils"
import { defineComponent, h, nextTick, ref } from "vue"
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogOverlay,
  DialogTitle,
  DialogTrigger,
} from "."

describe("Dialog", () => {
  describe("Dialog root", () => {
    it("renders without errors", () => {
      const wrapper = mount(Dialog, { slots: { default: "content" } })
      expect(wrapper.exists()).toBe(true)
    })

    it("renders trigger slot content in the unopened state", () => {
      const wrapper = mount(Dialog, {
        slots: {
          default: () =>
            h(DialogTrigger, { asChild: true }, {
              default: () => h("button", { class: "trigger-btn" }, "Open"),
            }),
        },
      })
      expect(wrapper.find(".trigger-btn").exists()).toBe(true)
      expect(wrapper.find(".trigger-btn").text()).toBe("Open")
    })
  })

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

    it("has correct base classes", () => {
      const wrapper = mount(DialogHeader)
      const classes = wrapper.classes()
      expect(classes).toContain("flex")
      expect(classes).toContain("flex-col")
      expect(classes).toContain("gap-y-1.5")
      expect(classes).toContain("text-center")
      expect(classes).toContain("sm:text-left")
    })

    it("renders as a div element", () => {
      const wrapper = mount(DialogHeader)
      expect(wrapper.element.tagName).toBe("DIV")
    })

    it("merges custom class with default classes", () => {
      const wrapper = mount(DialogHeader, {
        props: { class: "custom-header" },
      })
      expect(wrapper.classes()).toContain("custom-header")
      expect(wrapper.classes()).toContain("flex")
    })
  })

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

    it("has correct base classes", () => {
      const wrapper = mount(DialogFooter)
      const classes = wrapper.classes()
      expect(classes).toContain("flex")
      expect(classes).toContain("flex-col-reverse")
      expect(classes).toContain("sm:flex-row")
      expect(classes).toContain("sm:justify-end")
      expect(classes).toContain("sm:gap-x-2")
    })

    it("renders as a div element", () => {
      const wrapper = mount(DialogFooter)
      expect(wrapper.element.tagName).toBe("DIV")
    })

    it("merges custom class with default classes", () => {
      const wrapper = mount(DialogFooter, {
        props: { class: "custom-footer" },
      })
      expect(wrapper.classes()).toContain("custom-footer")
      expect(wrapper.classes()).toContain("flex")
    })

    it("renders multiple slot children", () => {
      const wrapper = mount(DialogFooter, {
        slots: { default: "<button>Cancel</button><button>Save</button>" },
      })
      expect(wrapper.text()).toContain("Cancel")
      expect(wrapper.text()).toContain("Save")
    })
  })

  describe("Dialog with open content", () => {
    it("renders DialogContent with portal when defaultOpen is true", async () => {
      mount(Dialog, {
        props: { defaultOpen: true },
        slots: {
          default: () =>
            h(DialogContent, null, {
              default: () => [
                h(DialogTitle, null, { default: () => "Title" }),
                h("p", "Body"),
              ],
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      const content = document.body.querySelector("[role=\"dialog\"]")
      expect(content).toBeTruthy()
      expect(document.body.textContent).toContain("Title")
      expect(document.body.textContent).toContain("Body")
    })

    it("renders DialogTitle with correct classes", async () => {
      mount(Dialog, {
        props: { defaultOpen: true },
        slots: {
          default: () =>
            h(DialogContent, null, {
              default: () =>
                h(DialogHeader, null, {
                  default: () =>
                    h(DialogTitle, null, { default: () => "Test Title" }),
                }),
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      const title = document.body.querySelector("h2")
      expect(title).toBeTruthy()
      expect(title?.textContent).toContain("Test Title")
      expect(title?.className).toContain("font-semibold")
      expect(title?.className).toContain("leading-none")
      expect(title?.className).toContain("tracking-tight")
    })

    it("renders DialogDescription with correct classes", async () => {
      mount(Dialog, {
        props: { defaultOpen: true },
        slots: {
          default: () =>
            h(DialogContent, null, {
              default: () => [
                h(DialogTitle, null, { default: () => "T" }),
                h(DialogDescription, null, {
                  default: () => "Test Description",
                }),
              ],
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      const description = document.body.querySelector("p")
      expect(description).toBeTruthy()
      expect(description?.textContent).toContain("Test Description")
      expect(description?.className).toContain("text-sm")
      expect(description?.className).toContain("text-muted-foreground")
    })

    it("renders the close X button with sr-only label", async () => {
      mount(Dialog, {
        props: { defaultOpen: true },
        slots: {
          default: () =>
            h(DialogContent, null, {
              default: () => h(DialogTitle, null, { default: () => "T" }),
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      const srOnly = document.body.querySelector(".sr-only")
      expect(srOnly).toBeTruthy()
      expect(srOnly?.textContent).toBe("Close")
    })

    it("renders the overlay element when open", async () => {
      mount(Dialog, {
        props: { defaultOpen: true },
        slots: {
          default: () =>
            h(DialogContent, null, {
              default: () => h(DialogTitle, null, { default: () => "T" }),
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      const overlay = document.body.querySelector("[data-state=\"open\"]")
      expect(overlay).toBeTruthy()
    })

    it("does not render content when defaultOpen is false", async () => {
      mount(Dialog, {
        slots: {
          default: () =>
            h(DialogContent, null, {
              default: () => h(DialogTitle, null, { default: () => "Hidden" }),
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      expect(document.body.textContent).not.toContain("Hidden")
    })

    it("closes when DialogClose is clicked (v-model open syncs to false)", async () => {
      const Harness = defineComponent({
        components: { Dialog, DialogContent, DialogTitle, DialogClose },
        setup() {
          const open = ref(true)
          return { open }
        },
        template: `
          <Dialog v-model:open="open">
            <DialogContent>
              <DialogTitle>ClickClose</DialogTitle>
              <DialogClose as-child>
                <button class="close-btn">Close</button>
              </DialogClose>
            </DialogContent>
          </Dialog>
          <span class="state">{{ open ? 'OPEN' : 'CLOSED' }}</span>
        `,
      })
      const wrapper = mount(Harness, { attachTo: document.body })
      await nextTick()
      const btn = document.body.querySelector(".close-btn") as HTMLButtonElement | null
      expect(btn).toBeTruthy()
      btn?.click()
      await nextTick()
      await nextTick()
      expect(wrapper.find(".state").text()).toBe("CLOSED")
    })

    it("merges custom class on DialogContent", async () => {
      mount(Dialog, {
        props: { defaultOpen: true },
        slots: {
          default: () =>
            h(DialogContent, { class: "my-dialog-class" }, {
              default: () => h(DialogTitle, null, { default: () => "Custom" }),
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      const content = document.body.querySelector("[role=\"dialog\"]")
      expect(content?.className).toContain("my-dialog-class")
    })

    it("renders full dialog structure with header, body, and footer", async () => {
      mount(Dialog, {
        props: { defaultOpen: true },
        slots: {
          default: () =>
            h(DialogContent, null, {
              default: () => [
                h(DialogHeader, null, {
                  default: () => [
                    h(DialogTitle, null, { default: () => "Edit Profile" }),
                    h(DialogDescription, null, {
                      default: () => "Make changes here.",
                    }),
                  ],
                }),
                h("div", { class: "dialog-body" }, "Body content"),
                h(DialogFooter, null, {
                  default: () => h("button", "Save changes"),
                }),
              ],
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      const title = document.body.querySelector("h2")
      expect(title?.textContent).toContain("Edit Profile")
      const description = document.body.querySelector("p")
      expect(description?.textContent).toContain("Make changes here.")
      expect(document.body.querySelector(".dialog-body")?.textContent).toBe(
        "Body content",
      )
      const footer = document.body.querySelector("[class*=\"flex-col-reverse\"]")
      expect(footer?.textContent).toContain("Save changes")
    })
  })

  describe("DialogClose", () => {
    it("renders close child inside open dialog", async () => {
      mount(Dialog, {
        props: { defaultOpen: true },
        slots: {
          default: () =>
            h(DialogContent, null, {
              default: () => [
                h(DialogTitle, null, { default: () => "T" }),
                h(DialogClose, { asChild: true }, {
                  default: () =>
                    h("button", { class: "close-btn" }, "Close Me"),
                }),
              ],
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      const btn = document.body.querySelector(".close-btn")
      expect(btn).toBeTruthy()
      expect(btn?.textContent).toBe("Close Me")
    })
  })

  describe("DialogOverlay", () => {
    it("renders the overlay inside open dialog", async () => {
      mount(Dialog, {
        props: { defaultOpen: true },
        slots: {
          default: () =>
            h(DialogContent, null, {
              default: () => h(DialogTitle, null, { default: () => "O" }),
            }),
        },
        attachTo: document.body,
      })
      await nextTick()
      const overlay = document.body.querySelector("[class*=\"bg-black/80\"]")
      expect(overlay).toBeTruthy()
    })

    it("exports the DialogOverlay component for standalone use", () => {
      expect(DialogOverlay).toBeTruthy()
    })
  })
})
