import { beforeEach, describe, expect, it } from "vitest"
import { flushPromises, mount } from "@vue/test-utils"
import { defineComponent, h, Suspense } from "vue"
import { createPinia, setActivePinia } from "pinia"
import type { InputElement } from "@/definitions/elements/InputElement"
import ElementRenderer from "@/engine/ElementRenderer.vue"
import { useFormDataStore } from "@/stores/formDataStore"
import { __clearElementCache, preloadElement } from "@/engine/useElementLoader"

/**
 * Helper: mount ElementRenderer inside a <Suspense> boundary so the
 * inner async component fully resolves before assertions. The
 * preload in beforeEach ensures the dynamic-import promise is settled
 * before the first render tick.
 */
function mountWithSuspense(props: Record<string, unknown>) {
  const Host = defineComponent({
    setup() {
      return () =>
        h(Suspense, null, {
          default: () =>
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            h(ElementRenderer as any, props),
        })
    },
  })
  return mount(Host)
}

describe("ElementRenderer", () => {
  beforeEach(async () => {
    setActivePinia(createPinia())
    __clearElementCache()
    await preloadElement("input.text")
  })

  it("lazy-loads a text input and renders it", async () => {
    const element: InputElement = {
      id: "first-name",
      type: "input.text",
      name: "first_name",
      label: "First name",
    }
    const wrapper = mountWithSuspense({ element })
    await flushPromises()

    expect(wrapper.text()).toContain("First name")
    expect(wrapper.find("input").exists()).toBe(true)
  })

  it("writes input edits into the formDataStore", async () => {
    const element: InputElement = {
      id: "first-name",
      type: "input.text",
      name: "first_name",
      label: "First name",
    }
    const wrapper = mountWithSuspense({ element })
    await flushPromises()

    await wrapper.find("input").setValue("Stephen")

    const store = useFormDataStore()
    expect(store.forms.default?.values.first_name).toBe("Stephen")
    expect(store.forms.default?.touched.first_name).toBe(true)
    expect(store.forms.default?.dirty).toBe(true)
  })

  it("hides the element when conditionParams fail", () => {
    const element: InputElement = {
      id: "admin-field",
      type: "input.text",
      name: "secret",
      label: "Secret",
      conditionParams: [{ name: "role", logic: "==", value: "admin" }],
    }
    const wrapper = mount(ElementRenderer, {
      props: { element, item: { role: "user" } },
    })
    expect(wrapper.html()).toBe("<!--v-if-->")
  })

  it("renders the missing-renderer fallback for an unregistered style", async () => {
    const element = {
      id: "bogus",
      type: "input.bogus-style",
      name: "x",
      label: "X",
    } as unknown as InputElement
    const wrapper = mountWithSuspense({ element })
    await flushPromises()
    expect(wrapper.find("[data-testid='missing-renderer']").exists()).toBe(true)
  })
})
