/**
 * {{titleCase name}} api — role-gating reference (authz mode "role-gating").
 *   L1 authn: Zitadel JWT (audience = this app's project).
 *   L2 authz: `requireRole` against the roles that rode in on the token.
 *
 * `deps.testAuth` swaps L1 for a fixed principal in unit tests; production
 * always runs the real scheme (fail-closed 401 when unconfigured).
 */
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { createAuthnMiddleware, type AuthnVariables } from "@mesh-tech/authn/hono";
import { createAuthzMiddleware, type AuthzVariables } from "@mesh-tech/authz/hono";
import { createLogger, requestLogger } from "@mesh-tech/logger";
import { createAuthnFromEnv } from "./authn.js";
import { createAppAuthz } from "./authz.js";

type TestAuth = { subjectSub: string; coarseRole?: string | readonly string[] };
type AppEnv = { Variables: AuthnVariables & AuthzVariables };

// Every service logs through @mesh-tech/logger (the Mesh app contract, gate 0.4).
const log = createLogger({ service: "{{name}}-api" });

/** The role-gated route paths (kept in one place so authn + authz agree). */
const GATED = ["/admin", "/reports"] as const;

export const createApp = (deps: { testAuth?: TestAuth } = {}) => {
  const app = new Hono<AppEnv>();
  app.use("*", requestLogger(log));
  const m = createAuthzMiddleware(createAppAuthz());

  // Open: no token needed.
  app.get("/health", (c) => c.json({ ok: true, app: "{{name}}", mode: "role-gating" }));

  if (deps.testAuth) {
    const testAuth = deps.testAuth;
    app.use("*", async (c, next) => {
      c.set("auth", { scheme: "test", authenticatedAt: Date.now(), ...testAuth });
      await next();
    });
  } else {
    const authn = createAuthnFromEnv();
    if (authn) {
      const { middleware } = createAuthnMiddleware(authn);
      for (const path of GATED) app.use(path, middleware());
    } else {
      // Fail closed: no authn configured → every gated route is 401, never open.
      for (const path of GATED) app.use(path, (c) => c.json({ error: "authn unconfigured" }, 401));
    }
  }
  app.use("*", m.authContextMiddleware());

  // Role-gated: the caller's Zitadel project roles must include the named role.
  app.get("/admin", m.requireRole("admin"), (c) => c.json({ ok: true, role: "admin" }));
  app.get("/reports", m.requireRole("viewer"), (c) => c.json({ reports: [], role: "viewer" }));

  return app;
};

if (process.env.NODE_ENV !== "test" && process.argv[1]?.endsWith("main.js")) {
  const port = Number(process.env.PORT ?? 3000);
  serve({ fetch: createApp().fetch, port });
  log.info({ port, mode: "role-gating" }, "{{name}} api listening");
}
