import { defineStore } from "pinia"
import { computed, ref } from "vue"
import type { CardElement } from "@/definitions/elements"
import type { ElementCategory, ElementType } from "@/definitions/shared/base"
import { elementDefaultStyle } from "@/engine/registry"

/**
 * State for a single ElementListBuilder instance. Like FormBuilder but
 * without cards / tabs — the schema is just a flat list of
 * CardElements. Useful for forms-as-arrays and list-of-fields configs.
 */
export interface ElementListBuilderSchema {
  elements: CardElement[]
}

/** Identifies which element is currently selected in the inspector. */
export interface ElementListBuilderSelection {
  id: string | null
}

let idCounter = 0
function nextId(prefix: string): string {
  idCounter += 1
  return `${prefix}-${Date.now()}-${idCounter}`
}

/** Test helper to make ids deterministic. */
export function __resetElementListBuilderIdCounter(): void {
  idCounter = 0
}

function emptySchema(): ElementListBuilderSchema {
  return { elements: [] }
}

export const useElementListBuilderStore = defineStore(
  "gx-modui/elementListBuilder",
  () => {
    const builders = ref<Record<string, ElementListBuilderSchema>>({})
    const selections = ref<Record<string, ElementListBuilderSelection>>({})

    function ensureBuilder(builderId: string): ElementListBuilderSchema {
      if (!builders.value[builderId]) {
        builders.value[builderId] = emptySchema()
        selections.value[builderId] = { id: null }
      }
      return builders.value[builderId]!
    }

    function loadSchema(
      builderId: string,
      schema: ElementListBuilderSchema,
    ): void {
      builders.value[builderId] = {
        elements: schema.elements.map(
          (el) =>
            ({ ...(el as unknown as Record<string, unknown>) }) as unknown as CardElement,
        ),
      }
      selections.value[builderId] = { id: null }
    }

    function resetBuilder(builderId: string): void {
      delete builders.value[builderId]
      delete selections.value[builderId]
    }

    /**
     * Append a fresh element of `type` (dot-notation or category) to
     * the flat list. Returns the new element's id.
     */
    function addElement(
      builderId: string,
      type: ElementType | ElementCategory,
    ): string {
      const schema = ensureBuilder(builderId)
      const dotType: ElementType = type.includes(".")
        ? (type as ElementType)
        : (`${type}.${elementDefaultStyle[type as ElementCategory] ?? "text"}` as ElementType)
      const id = nextId(`el-${dotType.replace(".", "-")}`)
      const element = {
        id,
        type: dotType,
        label: `New ${dotType}`,
      } as unknown as CardElement
      schema.elements = [...schema.elements, element]
      return id
    }

    /** Remove an element by id. */
    function removeElement(builderId: string, elementId: string): void {
      const schema = ensureBuilder(builderId)
      schema.elements = schema.elements.filter(
        (el) => (el as unknown as { id: string }).id !== elementId,
      )
      const sel = selections.value[builderId]
      if (sel?.id === elementId) {
        selections.value[builderId] = { id: null }
      }
    }

    /** Move an element by index within the list. */
    function reorderElement(
      builderId: string,
      fromIndex: number,
      toIndex: number,
    ): void {
      const schema = ensureBuilder(builderId)
      const list = schema.elements
      if (
        fromIndex < 0 ||
        fromIndex >= list.length ||
        toIndex < 0 ||
        toIndex >= list.length ||
        fromIndex === toIndex
      ) {
        return
      }
      const next = [...list]
      const [moved] = next.splice(fromIndex, 1)
      if (moved !== undefined) {
        next.splice(toIndex, 0, moved)
        schema.elements = next
      }
    }

    /** Patch an element's properties (used by the Inspector). */
    function updateElement(
      builderId: string,
      elementId: string,
      patch: Partial<CardElement>,
    ): void {
      const schema = ensureBuilder(builderId)
      const idx = schema.elements.findIndex(
        (el) => (el as unknown as { id: string }).id === elementId,
      )
      if (idx < 0) {
        return
      }
      const current = schema.elements[idx]!
      const next = {
        ...(current as unknown as Record<string, unknown>),
        ...(patch as unknown as Record<string, unknown>),
      } as unknown as CardElement
      const list = [...schema.elements]
      list[idx] = next
      schema.elements = list
    }

    function select(builderId: string, id: string | null): void {
      selections.value[builderId] = { id }
    }

    function getSelection(builderId: string): ElementListBuilderSelection {
      return selections.value[builderId] ?? { id: null }
    }

    function getSchema(builderId: string): ElementListBuilderSchema {
      return ensureBuilder(builderId)
    }

    return {
      builders,
      selections,
      ensureBuilder,
      loadSchema,
      resetBuilder,
      addElement,
      removeElement,
      reorderElement,
      updateElement,
      select,
      getSelection,
      getSchema,
    }
  },
)

/**
 * Composable wrapper around `useElementListBuilderStore` for a single
 * builder instance.
 */
export function useElementListBuilder(builderId: string) {
  const store = useElementListBuilderStore()
  store.ensureBuilder(builderId)
  return {
    schema: computed<ElementListBuilderSchema>(() =>
      store.getSchema(builderId),
    ),
    selection: computed<ElementListBuilderSelection>(() =>
      store.getSelection(builderId),
    ),
    addElement: (type: ElementType | ElementCategory) =>
      store.addElement(builderId, type),
    removeElement: (id: string) => store.removeElement(builderId, id),
    reorderElement: (from: number, to: number) =>
      store.reorderElement(builderId, from, to),
    updateElement: (id: string, patch: Partial<CardElement>) =>
      store.updateElement(builderId, id, patch),
    select: (id: string | null) => store.select(builderId, id),
    loadSchema: (schema: ElementListBuilderSchema) =>
      store.loadSchema(builderId, schema),
    reset: () => store.resetBuilder(builderId),
  }
}
