/**
 * {{titleCase name}} — Platform-authenticated API (authz mode "policy-engine").
 *
 * authn: Zitadel JWTs verified in-process (@mesh-tech/authn) against the app's
 * OWN Zitadel project. authz: schema-driven permission checks in the app's OWN
 * SpiceDB (@mesh-tech/authz). Everything the Hub needs to administer people,
 * roles and API keys for this app is published from here — nothing is created
 * by hand in Zitadel and nothing lives in a runbook (the Mesh app contract,
 * gate H: `@mesh-tech/app-kit#apps` → references/app-contract.md).
 *
 * Wires, IN ORDER:
 *   1. ZitadelConnector   — authed provider for the platform Zitadel.
 *   2. ZitadelAppIdentity — this app's OWN project; roles = schema.coarseRoles.
 *   3. AppEnvironment     — namespace + providers, bound to the app's project.
 *   4. SpiceDB            — the app's OWN SpiceDB instance (shared-DB mode).
 *   5. SpiceDBSchema      — compiles api/src/schema.ts to .zed + ops-hub
 *                           metadata, applies it, exports the metadata to SSM.
 *   6. api Service        — Hono app: authn + authz middleware, linked to SpiceDB.
 *   7. AppAuthzPointer    — LAST. Tells the Hub where this app's authz lives.
 *
 * Prerequisite: the target platform must have Zitadel enabled — ZitadelConnector
 * eager-reads it at preview time.
 *
 * Local:   mesh start && mesh dev   (the local platform's Zitadel + SpiceDB)
 * Deploy:  mesh deploy up
 */

import * as pulumi from "@pulumi/pulumi";
import { mesh } from "@mesh-tech/app-kit/infra";
import { compileOpsHubMetadata, compileSpiceDBSchema } from "@mesh-tech/authz";
// The authz schema is the single source of truth, shared by the api (enforces
// it) and this Pulumi program (compiles it to SpiceDB + ops-hub metadata + roles).
import { schema as schemaDef } from "./api/src/schema.js";

const { tenant, platform, stack, deploy } = mesh.apps.getConfig(pulumi);
const appName = "{{name}}";

// --- 1. Connector — authed provider for the platform Zitadel. ---
const connector = new mesh.auth.ZitadelConnector("idp", { tenant, env: platform.env });

// --- 2. App identity — this app's OWN Zitadel project + roles. ---
// The schema's coarse roles become Zitadel PROJECT ROLES: the vocabulary the
// Hub grants people and API keys against. An `api`-type application is the
// resource server tokens are minted for (its clientId is the JWT audience).
const identity = new mesh.auth.ZitadelAppIdentity("identity", {
  connector,
  tenant,
  env: platform.env,
  project: { name: appName },
  roles: [...schemaDef.coarseRoles],
  applications: {
    api: { type: "api" },
  },
});

// --- 3. Environment — bound to the app's own project via zitadelAppProjectId. ---
const env = new mesh.apps.AppEnvironment("env", {
  tenant,
  platform,
  stack,
  deploy,
  appName,
  namespace: true,
  database: false,
  zitadelAppProjectId: identity.projectId,
});

// --- 4. SpiceDB — the app's OWN instance (omit `rds` → the platform's shared RDS). ---
// `httpEnabled` serves the REST gateway the starter spicedb-http-provider talks to.
const spicedb = new mesh.auth.SpiceDB("spicedb", { env, httpEnabled: true });

// --- 5. Schema — compile, apply, and publish the ops-hub metadata. ---
// `opsHubMetadataRef` is the SSM path the pointer below hands to the Hub; without
// it the Hub's create-key and role catalog answer 409 METADATA_UNPUBLISHED.
const schema = new mesh.auth.SpiceDBSchema("schema", {
  env,
  spicedb,
  compiled: {
    zed: compileSpiceDBSchema(schemaDef).zed,
    metadata: compileOpsHubMetadata(schemaDef),
  },
});

// --- 6. api Service — in-process authn + authz; SpiceDB reached via link(). ---
const api = new mesh.apps.Service("api", {
  env,
  title: "{{titleCase name}} API",
  description: "Platform-authenticated API for {{titleCase name}}",
  runtime: "node",
  src: "./api",
  port: 3000,
  // spicedb.link() injects SPICEDB_ENDPOINT (gRPC) + SPICEDB_TOKEN and the IAM
  // to reach the instance; the HTTP gateway address rides alongside for the
  // starter provider.
  link: [spicedb.link()],
  environment: {
    ZITADEL_ISSUER: pulumi.output(env.zitadel).apply((z) => z?.issuer ?? ""),
    ZITADEL_PROJECT_ID: identity.projectId,
    SPICEDB_HTTP_ENDPOINT: spicedb.httpEndpoint,
  },
  ingress: env.buildIngress({
    subdomain: "{{name}}-api",
    healthCheckPath: "/health",
  }),
  replicas: 1,
  resources: { cpu: "100m", memory: "128Mi" },
});

// --- 7. AppAuthzPointer — LAST. The Hub's App → Access tab reads this. It
// publishes env.zitadel, i.e. the project bound above through zitadelAppProjectId;
// never pass a `zitadel:` override here (that is the Hub's own escape hatch). ---
new mesh.auth.AppAuthzPointer("authz", { env, mode: "policy-engine", opsHubMetadataRef: schema.opsHubMetadataRef });

// --- Outputs ---

export const app = env.register({
  description: "{{titleCase name}} — platform-authenticated API (authn + authz)",
});

/** The app's own Zitadel project id (grant people and API keys against this). */
export const zitadelProjectId = identity.projectId;
