// Stage plugin frontends into the Vite tree (ADR-013 Phase 4).
//
// Each plugin that owns UI keeps it self-contained under
// plugins/<name>/frontend/ (routes.js + views/). This script copies the CHOSEN
// plugins' frontend dirs into frontend/src/.plugins-staged/<name>/ and codegens
// frontend/src/router/routes.gen.js, which the router imports. A per-site build
// selects plugins via the SITE_PLUGINS env (comma-separated); with none set,
// every plugin that has a frontend/ is staged (the default full build). A
// frontend dir with no manifest.json is a CORE feature and is ALWAYS staged
// regardless of SITE_PLUGINS.
//
// Both outputs are generated (gitignored). Run by npm predev/prebuild.

import { readdirSync, existsSync, rmSync, cpSync, writeFileSync, mkdirSync } from 'fs'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'

const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
const pluginsDir = join(repoRoot, 'plugins')
const stagedDir = join(repoRoot, 'frontend', 'src', '.plugins-staged')
const genFile = join(repoRoot, 'frontend', 'src', 'router', 'routes.gen.js')

const requested = (process.env.SITE_PLUGINS || '')
  .split(',').map(s => s.trim()).filter(Boolean)

const withFrontend = readdirSync(pluginsDir, { withFileTypes: true })
  .filter(entry => entry.isDirectory()
    && existsSync(join(pluginsDir, entry.name, 'frontend', 'routes.js')))
  .map(entry => entry.name)

// A frontend dir with no manifest.json is a CORE feature (e.g. applications),
// not a per-site plugin - it MUST always ship, or a lean build loses a core
// page (its nav is advertised by the core, so the route would 404 to a blank
// screen). SITE_PLUGINS selection applies only to real plugins (those with a
// manifest).
const isPlugin = name => existsSync(join(pluginsDir, name, 'manifest.json'))
const coreFrontend = withFrontend.filter(name => !isPlugin(name))
const pluginFrontend = withFrontend.filter(isPlugin)
const chosenPlugins = requested.length
  ? pluginFrontend.filter(name => requested.includes(name))
  : pluginFrontend
const chosen = [...coreFrontend, ...chosenPlugins]

// Copy each chosen plugin's frontend dir into the staging area (clean first so
// a removed plugin does not linger).
rmSync(stagedDir, { recursive: true, force: true })
mkdirSync(stagedDir, { recursive: true })
for (const name of chosen) {
  cpSync(join(pluginsDir, name, 'frontend'), join(stagedDir, name),
    { recursive: true })
}

// Codegen the aggregation the router imports. `toplevel` is optional per plugin.
const lines = ['// AUTO-GENERATED by scripts/stage-frontend.mjs - do not edit.']
const childParts = []
const topParts = []
for (const name of chosen) {
  const id = 'p_' + name.replace(/[^a-zA-Z0-9]/g, '_')
  // Namespace import so a route file without a `toplevel` export is not a
  // (strict ESM) build error - the binding is just undefined on the namespace.
  lines.push(`import * as ${id} from '../.plugins-staged/${name}/routes.js'`)
  childParts.push(`...(${id}.default || [])`)
  topParts.push(`...(${id}.toplevel || [])`)
}
lines.push('')
lines.push(`export const stagedChildren = [${childParts.join(', ')}]`)
lines.push(`export const stagedToplevel = [${topParts.join(', ')}]`)
lines.push('')
writeFileSync(genFile, lines.join('\n'))

console.log(`staged ${chosen.length} plugin frontend(s): `
  + (chosen.join(', ') || '(none)'))
