// The metadata header the launcher reads from the compiled bundle: name / icon / gl / 2d / ar /
// preload / fonts / icons. Single source of truth — the backend and CLI both call this (today they
// mirror it by hand).
//
// Detection keys on the *bundled* `_creator*` calls, which the SDK's thin wrappers still emit
// (new Scene() → _creator.createScene, new Scene2D() → _creator2d.createScene), so the re-skin keeps
// the exact same signals the old worker bundles produced.

export type HeaderOptions = {
  /** Generated-icon URL (backend supplies it; the CLI omits it). */
  icon?: string
  /** `// fonts:` payload from the font() macro (buildFontsHeaderValue) — the boot faces a host
   *  should prefetch and hold first paint for. CDN URLs carry a `{fmt}` placeholder (woff2/ttf). */
  fonts?: string
  /** `// icons:` payload from the assetIcon() macro (buildIconsHeaderValue) — the icon packs whose
   *  glyphs are inlined in the bundle. Purely informational (attribution record); hosts ignore it. */
  icons?: string
}

export const buildHeader = (name: string, code: string, opts: HeaderOptions = {}): string => {
  let header = `// name: ${name.slice(0, 20)}\n`
  if (opts.icon) header += `// icon: ${opts.icon}\n`
  header += `// last-updated: ${new Date().toISOString()}\n`

  // The short engine-detection lines come BEFORE the potentially long preload/fonts/icons lines:
  // launchers sniff `// gl:` in a fixed-size header slice, and a long `// fonts:` list used to push
  // `// gl: disable` past the slice — the legacy no-header check then booted the full GL engine for
  // a pure-UI app (main-thread freeze at launch). Early placement lets even already-deployed
  // launchers with small slices detect correctly.

  // GL: disabled unless the project touches the 3D engine; deferred if it lazily creates the engine.
  if (!code.includes("_creator.createScene") && !code.includes("_creator.createEntity")) header += `// gl: disable\n`
  else if (code.includes("_creator.createEngine")) header += `// gl: defer\n`

  // 2D (creator-2d) — non-exclusive: emitted alongside `gl: disable` so hosts skip Filament and a
  // 2D-aware host brings up the lightweight sokol engine instead.
  if (code.includes("_creator2d.")) header += `// 2d: enable\n`

  if (code.includes("_creator.createARController")) header += `// ar: enable\n`

  // Local assets to preload: explicit fetchLocal() calls + implicit engine archives.
  const localAssets = new Set<string>()
  for (const m of code.matchAll(/_creatorUtils\.fetchLocal\((.+?)\)/g)) {
    const arg = m[1].trim()
    // Only a plain string literal names a file. A computed argument (the local-shader lookup
    // builds `"<hash>_" + _creator.backend + ".filamat"` at runtime, since one bundle serves
    // every backend) would otherwise land in the header as a garbage "name".
    if (!/^(['"])[^'"]*\1$/.test(arg)) continue
    localAssets.add(arg.slice(1, -1).trim())
  }
  if (/_creator\.createGlb/.test(code)) localAssets.add("uberarchive")
  if (/_creator\.captureImage/.test(code)) localAssets.add("capture")
  if (localAssets.size > 0) header += `// preload: ${[ ...localAssets ].join(";")}\n`

  if (opts.fonts) header += `// fonts: ${opts.fonts}\n`
  if (opts.icons) header += `// icons: ${opts.icons}\n`
  header += "\n"
  return header
}

/** Same engine signals the header uses, for callers that need the project kind (e.g. icon type). */
export const detectProjectKind = (code: string): "ar" | "ui" | "2d" | "3d" => {
  if (code.includes("_creator.createARController")) return "ar"
  if (code.includes("_creator2d.")) return "2d"
  if (!code.includes("_creator.")) return "ui"
  return "3d"
}
