import { beforeEach, describe, expect, it } from "vitest"
import { mount } from "@vue/test-utils"
import { createPinia, setActivePinia } from "pinia"
import ElementListBuilder from "./ElementListBuilder.vue"
import {
  __resetElementListBuilderIdCounter,
  useElementListBuilderStore,
  type ElementListBuilderSchema,
} from "@/stores/elementListBuilderStore"

const BUILDER_ID = "elb-test"

describe("ElementListBuilder", () => {
  beforeEach(() => {
    setActivePinia(createPinia())
    __resetElementListBuilderIdCounter()
  })

  it("renders the three-pane layout", () => {
    const wrapper = mount(ElementListBuilder, {
      props: { builderId: BUILDER_ID },
    })
    expect(
      wrapper.find("[data-testid='element-list-builder-palette']").exists(),
    ).toBe(true)
    expect(
      wrapper.find("[data-testid='element-list-builder-canvas']").exists(),
    ).toBe(true)
    expect(
      wrapper.find("[data-testid='element-list-builder-inspector']").exists(),
    ).toBe(true)
  })

  it("shows an empty-state hint on the canvas when there are no elements", () => {
    const wrapper = mount(ElementListBuilder, {
      props: { builderId: BUILDER_ID },
    })
    expect(
      wrapper
        .find("[data-testid='element-list-builder-canvas-empty']")
        .exists(),
    ).toBe(true)
  })

  it("lists every registered element type in the palette", () => {
    const wrapper = mount(ElementListBuilder, {
      props: { builderId: BUILDER_ID },
    })
    const palette = wrapper.find(
      "[data-testid='element-list-builder-palette']",
    )
    for (const type of [
      "action",
      "boolean",
      "builder",
      "code",
      "configuration-block",
      "display",
      "editor",
      "file",
      "input",
      "option-select",
      "picker",
    ]) {
      expect(
        palette
          .find(`[data-testid='element-list-builder-palette-${type}']`)
          .exists(),
      ).toBe(true)
    }
  })

  it("clicking a palette button appends an element and selects it", async () => {
    const wrapper = mount(ElementListBuilder, {
      props: { builderId: BUILDER_ID },
    })
    await wrapper
      .find("[data-testid='element-list-builder-palette-input']")
      .trigger("click")

    const store = useElementListBuilderStore()
    const schema = store.getSchema(BUILDER_ID)
    expect(schema.elements).toHaveLength(1)
    expect(
      (schema.elements[0] as unknown as { type: string }).type,
    ).toBe("input.text")
    expect(
      wrapper
        .find("[data-testid='element-list-builder-inspector-fields']")
        .exists(),
    ).toBe(true)
  })

  it("editing the label patches the schema", async () => {
    const wrapper = mount(ElementListBuilder, {
      props: { builderId: BUILDER_ID },
    })
    await wrapper
      .find("[data-testid='element-list-builder-palette-input']")
      .trigger("click")
    await wrapper
      .find("[data-testid='element-list-builder-inspector-label']")
      .setValue("First name")

    const store = useElementListBuilderStore()
    const schema = store.getSchema(BUILDER_ID)
    expect(
      (schema.elements[0] as unknown as { label?: string }).label,
    ).toBe("First name")
  })

  it("removes the selected element when Remove is clicked", async () => {
    const wrapper = mount(ElementListBuilder, {
      props: { builderId: BUILDER_ID },
    })
    await wrapper
      .find("[data-testid='element-list-builder-palette-input']")
      .trigger("click")
    await wrapper
      .find("[data-testid='element-list-builder-remove']")
      .trigger("click")

    const store = useElementListBuilderStore()
    expect(store.getSchema(BUILDER_ID).elements).toHaveLength(0)
  })

  it("reorders an element when move-up is clicked", async () => {
    const wrapper = mount(ElementListBuilder, {
      props: { builderId: BUILDER_ID },
    })
    await wrapper
      .find("[data-testid='element-list-builder-palette-input']")
      .trigger("click")
    await wrapper
      .find("[data-testid='element-list-builder-palette-display']")
      .trigger("click")

    const store = useElementListBuilderStore()
    const initial = store
      .getSchema(BUILDER_ID)
      .elements.map((el) => (el as unknown as { id: string }).id)
    expect(initial).toHaveLength(2)

    // The second click selected the display element. Move it up.
    await wrapper
      .find("[data-testid='element-list-builder-move-up']")
      .trigger("click")
    const reordered = store
      .getSchema(BUILDER_ID)
      .elements.map((el) => (el as unknown as { id: string }).id)
    expect(reordered[0]).toBe(initial[1])
    expect(reordered[1]).toBe(initial[0])
  })

  it("loads an existing schema from modelValue on mount", () => {
    const existingSchema = {
      elements: [
        {
          id: "preloaded-el",
          type: "input.text",
          name: "preloaded",
          label: "Preloaded element",
        },
      ],
    } as unknown as ElementListBuilderSchema
    mount(ElementListBuilder, {
      props: {
        builderId: BUILDER_ID,
        modelValue: existingSchema,
      },
    })

    const store = useElementListBuilderStore()
    const schema = store.getSchema(BUILDER_ID)
    expect(schema.elements).toHaveLength(1)
    expect(
      (schema.elements[0] as unknown as { label?: string }).label,
    ).toBe("Preloaded element")
  })

  it("emits update:modelValue on schema mutation", async () => {
    const wrapper = mount(ElementListBuilder, {
      props: { builderId: BUILDER_ID },
    })
    await wrapper
      .find("[data-testid='element-list-builder-palette-input']")
      .trigger("click")
    await wrapper.vm.$nextTick()

    const emitted = wrapper.emitted("update:modelValue")
    expect(emitted).toBeTruthy()
    const last = emitted![emitted!.length - 1] as unknown[]
    const schema = last[0] as { elements: unknown[] }
    expect(schema.elements.length).toBe(1)
  })

  it("patches required / readonly / disabled flags from the inspector", () => {
    setActivePinia(createPinia())
    __resetElementListBuilderIdCounter()
    const store = useElementListBuilderStore()
    const id = store.addElement(BUILDER_ID, "input")
    store.updateElement(BUILDER_ID, id, {
      required: true,
      readonly: true,
      disabled: true,
    })
    const el = store.getSchema(BUILDER_ID).elements[0] as unknown as Record<
      string,
      unknown
    >
    expect(el.required).toBe(true)
    expect(el.readonly).toBe(true)
    expect(el.disabled).toBe(true)
  })
})
