import { beforeEach, describe, expect, it, vi } from "vitest"
import { defineComponent, h, Suspense } from "vue"
import { flushPromises, mount } from "@vue/test-utils"
import { createPinia, setActivePinia } from "pinia"
import type { DisplayCard } from "@/definitions/cards"
import type { CardElement } from "@/definitions/elements"
import type { ElementListCard } from "@/definitions/cards/ElementListCard"
import type { InputElement } from "@/definitions/elements/InputElement"
import ShowPage from "./ShowPage.vue"
import type { ShowPageConfig } from "./ShowPage.vue"
import {
  __clearElementCache,
  preloadElement,
} from "@/engine/useElementLoader"
import {
  __clearCardCache,
  preloadCard,
} from "@/engine/useCardLoader"
import { useFormDataStore } from "@/stores/formDataStore"

function makeConfig(overrides: Partial<ShowPageConfig> = {}): ShowPageConfig {
  return {
    title: "Edit user",
    ...overrides,
  }
}

function makeInput(
  id: string,
  overrides: Partial<InputElement> = {},
): InputElement {
  return {
    id,
    type: "input",
    style: "text",
    name: id,
    label: `Label for ${id}`,
    ...overrides,
  } as InputElement
}

function makeElementListCard(
  id: string,
  elementList: string[],
  overrides: Partial<ElementListCard> = {},
): ElementListCard {
  return {
    id,
    cardType: "element-list",
    elementList,
    ...overrides,
  } as ElementListCard
}

function mountWithSuspense(props: Record<string, unknown>) {
  const Host = defineComponent({
    setup() {
      return () =>
        h(Suspense, null, {
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          default: () => h(ShowPage as any, props),
        })
    },
  })
  return mount(Host)
}

describe("ShowPage", () => {
  beforeEach(async () => {
    setActivePinia(createPinia())
    __clearElementCache()
    __clearCardCache()
    await preloadElement("input.text")
    await preloadCard("element-list")
  })

  it("mounts without error", () => {
    const wrapper = mount(ShowPage, {
      props: { config: makeConfig() },
    })
    expect(wrapper.exists()).toBe(true)
  })

  it("renders the title", () => {
    const wrapper = mount(ShowPage, {
      props: { config: makeConfig({ title: "User profile" }) },
    })
    expect(
      wrapper.find("[data-testid='show-page-title']").text(),
    ).toBe("User profile")
  })

  it("renders breadcrumbs when provided", () => {
    const wrapper = mount(ShowPage, {
      props: {
        config: makeConfig({
          breadCrumbs: [{ title: "Dashboard" }, { title: "Users" }],
        }),
      },
    })
    const crumbs = wrapper.find("[data-testid='show-page-breadcrumbs']")
    expect(crumbs.exists()).toBe(true)
    expect(crumbs.text()).toContain("Dashboard")
    expect(crumbs.text()).toContain("Users")
  })

  it("renders the optional section header", () => {
    const wrapper = mount(ShowPage, {
      props: { config: makeConfig({ section: "Accounts" }) },
    })
    expect(
      wrapper.find("[data-testid='show-page-section']").text(),
    ).toBe("Accounts")
  })

  it("renders tabs when tabsList is provided and activates the first tab", async () => {
    const elements: Record<string, CardElement> = {
      "first-name": makeInput("first-name", { label: "First name" }),
    }
    const wrapper = mountWithSuspense({
      config: makeConfig({
        tabsList: [
          {
            title: "General",
            tabId: "general",
            cards: [makeElementListCard("card-1", ["first-name"])],
          },
          {
            title: "Advanced",
            tabId: "advanced",
            cards: [makeElementListCard("card-2", [])],
          },
        ],
        elements,
      }),
    })
    await flushPromises()
    expect(wrapper.find("[data-testid='show-page-tabs']").exists()).toBe(true)
    expect(
      wrapper.find("[data-testid='show-page-tab-general']").exists(),
    ).toBe(true)
    expect(
      wrapper.find("[data-testid='show-page-tab-advanced']").exists(),
    ).toBe(true)
    expect(wrapper.text()).toContain("First name")
  })

  it("renders flat cards when no tabsList is provided", async () => {
    const elements: Record<string, CardElement> = {
      "first-name": makeInput("first-name", { label: "First name" }),
    }
    const cards: Record<string, DisplayCard> = {
      "card-1": makeElementListCard("card-1", ["first-name"]),
    }
    const wrapper = mountWithSuspense({
      config: makeConfig({ cards, elements }),
    })
    await flushPromises()
    expect(
      wrapper.find("[data-testid='show-page-flat-cards']").exists(),
    ).toBe(true)
    expect(wrapper.find("[data-testid='show-page-tabs']").exists()).toBe(false)
    expect(wrapper.text()).toContain("First name")
  })

  it("shows the save button when updateConfig is provided", () => {
    const wrapper = mount(ShowPage, {
      props: {
        config: makeConfig({
          updateConfig: {
            update: { route: "users.update" },
          },
        }),
      },
    })
    expect(
      wrapper.find("[data-testid='show-page-save-button']").exists(),
    ).toBe(true)
  })

  it("hides the save button when no updateConfig is provided", () => {
    const wrapper = mount(ShowPage, {
      props: { config: makeConfig() },
    })
    expect(
      wrapper.find("[data-testid='show-page-save-button']").exists(),
    ).toBe(false)
  })

  it("hides the save button when updateConfig.showSaveButton is false", () => {
    const wrapper = mount(ShowPage, {
      props: {
        config: makeConfig({
          updateConfig: {
            update: { route: "users.update" },
            showSaveButton: false,
          },
        }),
      },
    })
    expect(
      wrapper.find("[data-testid='show-page-save-button']").exists(),
    ).toBe(false)
  })

  it("invokes eventHandlers.save and emits save on save-button click", async () => {
    const onSave = vi.fn()
    const wrapper = mount(ShowPage, {
      props: {
        config: makeConfig({
          updateConfig: { update: { route: "users.update" } },
          eventHandlers: { save: onSave },
        }),
      },
    })
    await wrapper
      .find("[data-testid='show-page-save-button']")
      .trigger("click")
    expect(onSave).toHaveBeenCalledTimes(1)
    expect(wrapper.emitted("save")).toBeTruthy()
  })

  it("shows the delete button when deleteConfig is provided", () => {
    const wrapper = mount(ShowPage, {
      props: {
        config: makeConfig({
          deleteConfig: {
            delete: { route: "users.destroy" },
          },
        }),
      },
    })
    expect(
      wrapper.find("[data-testid='show-page-delete-button']").exists(),
    ).toBe(true)
  })

  it("populates the form-data store when a nested input changes", async () => {
    const elements: Record<string, CardElement> = {
      "first-name": makeInput("first-name", { label: "First name" }),
    }
    const cards: Record<string, DisplayCard> = {
      "card-1": makeElementListCard("card-1", ["first-name"]),
    }
    const wrapper = mountWithSuspense({
      config: makeConfig({ cards, elements }),
      formId: "test-form",
    })
    await flushPromises()
    const input = wrapper.find("input")
    expect(input.exists()).toBe(true)
    await input.setValue("Alice")
    const store = useFormDataStore()
    expect(store.forms["test-form"]?.values["first-name"]).toBe("Alice")
    expect(store.forms["test-form"]?.dirty).toBe(true)
  })
})
