/**
 * Static registry mapping the JSON discriminators (`type` + `style`,
 * or `cardType`) to a relative path under `src/elements/` or
 * `src/cards/`. The path is what `import.meta.glob` uses to resolve a
 * lazy component loader at build time.
 *
 * Adding a new element style:
 *   1. Add it to the element's `Style` union in `definitions/elements/`.
 *   2. Add the matching path here.
 *   3. Create the Vue file at `src/elements/<path>.vue`.
 *
 * The registry is dense and exhaustive on purpose: TypeScript will fail
 * to compile if a style enum value is missing from the map, which keeps
 * the JSON contract and the runtime renderers in lock-step.
 */
import type { CardType, ElementCategory } from "@/definitions/shared/base"
import type { ActionStyle } from "@/definitions/elements/ActionElement"
import type { BooleanStyle } from "@/definitions/elements/BooleanElement"
import type { BuilderStyle } from "@/definitions/elements/BuilderElement"
import type { CardStyle } from "@/definitions/elements/CardLayoutElement"
import type { CodeStyle } from "@/definitions/elements/CodeElement"
import type { ConfigurationBlockStyle } from "@/definitions/elements/ConfigurationBlockElement"
import type { DisplayStyle } from "@/definitions/elements/DisplayElement"
import type { EditorStyle } from "@/definitions/elements/EditorElement"
import type { FileStyle } from "@/definitions/elements/FileElement"
import type { InputStyle } from "@/definitions/elements/InputElement"
import type { OptionSelectStyle } from "@/definitions/elements/OptionSelectElement"
import type { PickerStyle } from "@/definitions/elements/PickerElement"

/**
 * A path under `src/elements/`, without the `./` prefix or the `.vue`
 * extension. e.g. `"input/TextInput"` resolves to
 * `src/elements/input/TextInput.vue`.
 */
export type ElementPath = string

/**
 * A path under `src/cards/`. e.g. `"ElementListCard"` resolves to
 * `src/cards/ElementListCard.vue`.
 */
export type CardPath = string

/**
 * Per-type map of style → path. Each top-level key is an ElementType;
 * the inner object covers every style variant for that type.
 *
 * Using `Record<Style, ElementPath>` here means TS errors if a new
 * style is added to the definitions but not given a renderer path.
 */
export interface ElementRegistry {
  action: Record<ActionStyle, ElementPath>
  boolean: Record<BooleanStyle, ElementPath>
  builder: Record<BuilderStyle, ElementPath>
  card: Record<CardStyle, ElementPath>
  code: Record<CodeStyle, ElementPath>
  "configuration-block": Record<ConfigurationBlockStyle, ElementPath>
  display: Record<DisplayStyle, ElementPath>
  editor: Record<EditorStyle, ElementPath>
  file: Record<FileStyle, ElementPath>
  input: Record<InputStyle, ElementPath>
  "option-select": Record<OptionSelectStyle, ElementPath>
  picker: Record<PickerStyle, ElementPath>
}

export const elementRegistry: ElementRegistry = {
  card: {
    list: "card/ListCard",
    tab: "card/TabCard",
    datatable: "card/DatatableCard",
    grid: "card/GridCard",
    info: "card/InfoCard",
  },
  action: {
    button: "action/ButtonAction",
    "button-group": "action/ButtonGroupAction",
    // v2 alias — same file, accepted for backwards compat.
    buttongroup: "action/ButtonGroupAction",
    link: "action/LinkAction",
  },
  boolean: {
    checkbox: "boolean/CheckboxBoolean",
    switch: "boolean/SwitchBoolean",
    radio: "boolean/RadioBoolean",
    "icon-toggle": "boolean/IconToggleBoolean",
  },
  builder: {
    "form-builder": "builder/FormBuilder",
    "object-builder": "builder/ObjectBuilder",
    "preset-builder": "builder/PresetBuilder",
    "reactive-canvas-builder": "builder/ReactiveCanvasBuilder",
    "element-list-builder": "builder/ElementListBuilder",
    "quiz-builder": "builder/QuizBuilder",
    "dependency-list-builder": "builder/DependencyListBuilder",
    "grid-builder": "builder/GridBuilder",
  },
  code: {
    "git-editor": "code/GitCodeEditor",
    "file-tree": "code/FileTree",
    "pull-requests-list": "code/PullRequestsList",
    "build-status": "code/BuildStatus",
    "chat-message": "code/ChatMessage",
  },
  "configuration-block": {
    "elements-block": "configuration-block/ElementsBlock",
    repeatable: "configuration-block/RepeatableBlock",
    dialog: "configuration-block/DialogBlock",
    modal: "configuration-block/ModalBlock",
  },
  display: {
    type: "display/TypeDisplay",
    markdown: "display/MarkdownDisplay",
    html: "display/HtmlDisplay",
    iframe: "display/IframeDisplay",
    image: "display/ImageDisplay",
    video: "display/VideoPlayerDisplay",
    banner: "display/BannerDisplay",
    chart: "display/ChartDisplay",
    carousel: "display/CarouselDisplay",
    barcode: "display/BarcodeDisplay",
    qrcode: "display/QrCodeDisplay",
    icon: "display/IconDisplay",
    "progress-bar": "display/ProgressBarDisplay",
    sprite: "display/SpriteDisplay",
    "reactive-canvas": "display/ReactiveCanvasDisplay",
  },
  editor: {
    "code-editor": "editor/CodeEditor",
    "markdown-editor": "editor/MarkdownEditor",
    "html-editor": "editor/HtmlEditor",
    "image-editor": "editor/ImageEditor",
    "rich-text-editor": "editor/RichTextEditor",
  },
  file: {
    upload: "file/UploadFile",
    dropzone: "file/DropzoneFile",
    "asset-picker": "file/AssetPickerFile",
  },
  input: {
    text: "input/TextInput",
    number: "input/NumberInput",
    email: "input/EmailInput",
    password: "input/PasswordInput",
    url: "input/UrlInput",
    tel: "input/TelInput",
    search: "input/SearchInput",
    textarea: "input/TextareaInput",
    "range-slider": "input/RangeSliderInput",
    "barcode-scanner": "input/BarcodeScannerInput",
  },
  "option-select": {
    select: "option-select/SelectOption",
    "select-multiple": "option-select/SelectMultipleOption",
    radio: "option-select/RadioOption",
    checkbox: "option-select/CheckboxOption",
    autocomplete: "option-select/AutoCompleteOption",
    list: "option-select/ListOption",
    "pill-select": "option-select/PillSelectOption",
  },
  picker: {
    "date-picker": "picker/DatePicker",
    "datetime-picker": "picker/DateTimePicker",
    "time-picker": "picker/TimePicker",
    "timezone-picker": "picker/TimezonePicker",
    "color-picker": "picker/ColorPicker",
    "icon-picker": "picker/IconPicker",
    "icon-picker-inline": "picker/IconPickerInline",
    icon: "picker/IconPicker",
    "map-picker": "picker/MapPicker",
    "async-select": "picker/AsyncSelectPicker",
    "data-model": "picker/DataModelPicker",
    "integration-connector": "picker/IntegrationConnectorPicker",
  },
}

/**
 * Per-cardType map. Cards have no style discriminator — `cardType`
 * alone resolves the renderer.
 */
export const cardRegistry: Record<CardType, CardPath> = {
  "card-list": "CardListCard",
  "tab-list": "TabListCard",
  "element-list": "ElementListCard",
  "element-display": "ElementDisplayCard",
  datatable: "DataTableCard",
  "info-card": "InfoCard",
  "grid-view": "GridViewCard",
}

/**
 * Default style picked when an element JSON omits the dot suffix
 * (e.g. `type: "input"` is normalised to `"input.text"`). Each
 * ElementCategory must have a default registered.
 */
export const elementDefaultStyle: Record<ElementCategory, string> = {
  action: "button",
  boolean: "checkbox",
  builder: "form-builder",
  card: "list",
  code: "git-editor",
  "configuration-block": "repeatable",
  display: "type",
  editor: "code-editor",
  file: "upload",
  input: "text",
  "option-select": "select",
  picker: "date-picker",
}

/**
 * Returns the relative element path for a given dot-notation `type`
 * (e.g. `"input.text"`), or `null` when the type isn't registered.
 *
 * A bare category (e.g. `"input"`) is auto-completed using the
 * category's default style. The loader uses the returned path to look
 * up a Vue component module from `import.meta.glob`.
 */
export function resolveElementPath(type: string): ElementPath | null {
  const dot = type.indexOf(".")
  const category = (dot < 0 ? type : type.slice(0, dot)) as ElementCategory
  const style =
    dot < 0
      ? elementDefaultStyle[category] ?? ""
      : type.slice(dot + 1)
  const typeBucket = (elementRegistry as unknown as Record<string, Record<string, string>>)[category]
  if (!typeBucket) {
    return null
  }
  if (!style) {
    const fallback = Object.keys(typeBucket)[0]
    return fallback ? typeBucket[fallback]! : null
  }
  return typeBucket[style] ?? null
}

/**
 * Returns the relative card path for a given `cardType`, or `null` if
 * unregistered.
 */
export function resolveCardPath(cardType: string): CardPath | null {
  return (cardRegistry as Record<string, string>)[cardType] ?? null
}
