// Plugin manager: everything beyond the generic engine is a plugin (see README →
// "Plugins"). A plugin is `api/src/plugins/<name>/index.js` exporting a manifest:
//   { name, routes:[…], onBoot, onPublish, onPreview, migrations:[…], stats, admin:{…} }
//
// A route entry is either a mounted sub-app or a single handler:
//   { path, app, public?, admin? }                       // app = Hono instance
//   { method, path, handler, public?, admin?, use?:[mw] } // method = get|post|put|delete
// `public: true` → mounted before requireAuth; `admin: true` → guarded with the
// supplied requireAdmin; `use` adds per-route middleware (e.g. a rate limit).
//
// The manager loads the manifests named in config.plugins and wires them in:
// mounts routes by auth tier, runs lifecycle hooks, merges stats, surfaces
// migrations. Pure wiring — plugins import what they need (supabase, files, …).

const PLUGINS_DIR = new URL('./plugins/', import.meta.url);

// Import the manifests for `names` (config.plugins) and return a manager.
export async function loadPlugins(names = [], dir = PLUGINS_DIR) {
  const manifests = [];
  for (const name of names) {
    const url = new URL(`${name}/index.js`, dir);
    const mod = await import(url.href);
    const manifest = mod.default || mod;
    if (!manifest?.name) throw new Error(`Plugin "${name}": ungültiges Manifest (name fehlt)`);
    manifest.__dir = new URL(`${name}/`, dir);
    manifests.push(manifest);
  }
  return createManager(manifests);
}

// Mount one route entry (sub-app or handler) at `full`, behind `guards` (+ its
// own `use` middleware).
function mountRoute(app, full, r, guards) {
  const mws = [...guards, ...(r.use || [])];
  if (r.app) {
    for (const mw of mws) { app.use(full, mw); app.use(full + '/*', mw); }
    app.route(full, r.app);
  } else if (r.method && r.handler) {
    app[r.method](full, ...mws, r.handler);
  } else {
    throw new Error(`Plugin-Route ${full}: braucht entweder app oder method+handler`);
  }
}

// Build a manager over already-loaded manifests (kept separate from import so it
// unit-tests without touching the filesystem).
export function createManager(manifests) {
  const plugins = (manifests || []).filter(Boolean);
  const routesWhere = (pred) =>
    plugins.flatMap((p) => (p.routes || []).filter(pred).map((r) => ({ plugin: p.name, ...r })));

  return {
    plugins,
    names: () => plugins.map((p) => p.name),
    has: (name) => plugins.some((p) => p.name === name),

    // Public routes (no auth) — mount BEFORE `requireAuth` in the pipeline.
    mountPublic(app, prefix = '/api') {
      for (const r of routesWhere((r) => r.public)) mountRoute(app, prefix + r.path, r, []);
    },

    // Authenticated routes — mount AFTER `requireAuth`. `admin: true` routes are
    // guarded with the supplied requireAdmin (sub-apps that self-guard can omit
    // the flag, as dialog's mod/adminForums do).
    mountPrivate(app, prefix = '/api', requireAdmin) {
      for (const r of routesWhere((r) => !r.public)) {
        const guards = r.admin && requireAdmin ? [requireAdmin] : [];
        mountRoute(app, prefix + r.path, r, guards);
      }
    },

    // Lifecycle hooks (awaited in plugin order).
    async runBoot(ctx) { for (const p of plugins) if (p.onBoot) await p.onBoot(ctx); },
    async runPublish(ctx, payload) { for (const p of plugins) if (p.onPublish) await p.onPublish(ctx, payload); },
    async runPreview(ctx, payload) { for (const p of plugins) if (p.onPreview) await p.onPreview(ctx, payload); },

    // Merge each plugin's stats() under its own name: { dialog: { forums, … } }.
    async collectStats(ctx) {
      const out = {};
      for (const p of plugins) if (p.stats) out[p.name] = await p.stats(ctx);
      return out;
    },

    // Declared migrations resolved to file URLs; EXECUTION is the caller's job
    // (needs the live Postgres — wired in stage 5/boot, not here).
    migrations() {
      return plugins.flatMap((p) =>
        (p.migrations || []).map((file) => ({
          plugin: p.name,
          file,
          url: p.__dir ? new URL(`migrations/${file}`, p.__dir).href : null,
        })));
    },
  };
}
