import type { V1PageConfig, V2PageConfig } from "./v1ToV2"
import { v1ToV2 } from "./v1ToV2"

/**
 * Detect the schema version of a page config and normalise it to the
 * v2 shape. ShowPage / IndexPage call this at the boundary so a
 * consumer can pass either shape transparently.
 *
 * Detection heuristics (any one of these indicates v1):
 *   - `cards` is an Array (v2 uses a Record<string, Card>).
 *   - any tab's `cards[]` contains an object with a `type` string
 *     instead of `cardType`.
 *   - top-level `headers` / `options` exist without `cards` / `elements`
 *     (the IndexPage shorthand).
 *
 * When `schemaVersion` is explicitly set on the config, the heuristic
 * is bypassed — useful for force-routing legacy or new shapes.
 */
export function normalisePageConfig(
  config: V1PageConfig | V2PageConfig | (V1PageConfig & { schemaVersion?: "v1" | "v2" }),
): V2PageConfig {
  const explicit = (config as { schemaVersion?: "v1" | "v2" }).schemaVersion
  if (explicit === "v2") {
    return ensureMaps(config as V2PageConfig)
  }
  if (explicit === "v1" || isLikelyV1(config)) {
    return v1ToV2(config as V1PageConfig)
  }
  return ensureMaps(config as V2PageConfig)
}

function ensureMaps(config: V2PageConfig): V2PageConfig {
  return {
    ...config,
    cards: config.cards ?? {},
    elements: config.elements ?? {},
  }
}

function isLikelyV1(config: V1PageConfig | V2PageConfig): boolean {
  const c = config as Record<string, unknown>
  // v2 always carries `cards` as a Record. An Array means v1.
  if (Array.isArray(c.cards)) {
    return true
  }
  // v1 IndexPage shorthand: top-level `headers` / `options` / `listKey`
  // without a `cards`/`elements` map.
  if (
    (c.headers !== undefined || c.options !== undefined || c.listKey !== undefined) &&
    !c.cards &&
    !c.elements
  ) {
    return true
  }
  // v1 tabs: each tab's `cards` is an array of objects with `type`
  // (string) rather than `cardType`.
  const tabsList = c.tabsList as Array<Record<string, unknown>> | undefined
  if (Array.isArray(tabsList)) {
    for (const tab of tabsList) {
      const tabCards = tab.cards as Array<Record<string, unknown>> | undefined
      if (Array.isArray(tabCards)) {
        for (const card of tabCards) {
          if (typeof card.type === "string" && card.cardType === undefined) {
            return true
          }
        }
      }
    }
  }
  return false
}
