// ---------------------------------------------------------------------------
// Cloud's app-only HTTP surface — the `extensions.routes` fed to
// `ExecutorApp.make`. None of these are seams the shared core names; they are
// cloud-specific routes mounted alongside the executor `/api/*` plane:
//
//   - the WorkOS session routes (login / callback / me / organizations /
//     switch-organization / invitations / MCP-approval) — `NonProtectedApi`.
//   - the cloud-only WorkOS domain-verification routes — `OrgHttpApi`.
//   - Swagger UI + the OpenAPI JSON for the full cloud spec.
//   - the Autumn billing proxy (`/api/billing/*`) — billing-as-extension (the
//     `extensions.routes` SEAM, but served under `/api` like everything else).
//   - the global request-failure logging middleware.
//
// They all serve UNDER the `/api` prefix (the same namespace the protected +
// account APIs use), so each HttpApi group is provided the shared
// `apiPrefixedRouter` view; the plain `HttpRouter.add` routes use literal
// `/api/...` paths. The per-request `DbService` / `UserStoreService` the session
// handlers read is supplied by `RequestScopedServicesLive` (rebuilt per request
// so the postgres.js socket lives in the request fiber's scope).
// ---------------------------------------------------------------------------

import { Effect, Layer } from "effect";
import { HttpRouter, HttpServerResponse } from "effect/unstable/http";
import { HttpApiBuilder } from "effect/unstable/httpapi";
import { HttpApiSwagger, OpenApi } from "effect/unstable/httpapi";

import { requestScopedMiddleware } from "@executor-js/api/server";

import { UserStoreService } from "../auth/context";
import {
  CloudAuthPublicHandlers,
  CloudSessionAuthHandlers,
  NonProtectedApi,
} from "../auth/handlers";
import { CloudAuthApi, CloudAuthPublicApi } from "../auth/api";
import { SessionAuthLive } from "../auth/middleware-live";
import { OrgApi, OrgHttpApi } from "../org/api";
import { orgAuthMiddleware } from "../org/auth-middleware";
import { OrgHandlers } from "../org/handlers";
import { AutumnService } from "../extensions/billing/service";
import { DbService } from "../db/db";
import { ProtectedCloudApi } from "../api/layers";
import { AutumnRoutesLive } from "./billing/route";
import { ApiErrorLoggingLive } from "../observability/error-logging";

// The `/api`-prefixed `HttpRouter` view every cloud HttpApi group registers on,
// so `/auth/me` serves at `/api/auth/me` (matching the protected + account
// plane). Derived from the ambient router, exactly as `ExecutorApp.make` builds
// its own internal prefixed view for the protected API.
const apiPrefixedRouter = Layer.effect(HttpRouter.HttpRouter)(
  Effect.map(HttpRouter.HttpRouter.asEffect(), (router) => router.prefixed("/api")),
);

// The full cloud OpenAPI spec, prefixed so the served paths match `/api/*`.
const CloudOpenApi = ProtectedCloudApi.add(CloudAuthPublicApi)
  .add(CloudAuthApi)
  .add(OrgApi)
  .prefix("/api");

const spec = OpenApi.fromApi(CloudOpenApi);

/**
 * Build cloud's app-only extension routes. `rsLive` is the per-request DB layer
 * the session handlers read; passed in so tests can swap a counting fake.
 *
 * `AutumnService.Default` is provided to the session + org groups because the
 * `createOrganization` free-limit gate and the domain-verification-link gate
 * read it — the few app-only billing touchpoints. It is NOT on the neutral boot
 * core.
 */
export const makeCloudExtensionRoutes = (rsLive: Layer.Layer<DbService | UserStoreService>) => {
  // Session routes (login / callback / me / switch-org / …). Handlers yield
  // `UserStoreService` directly; the per-request DB combine keeps the postgres
  // socket request-scoped.
  const SessionRoutes = HttpApiBuilder.layer(NonProtectedApi).pipe(
    Layer.provide(Layer.mergeAll(CloudAuthPublicHandlers, CloudSessionAuthHandlers)),
    Layer.provide(requestScopedMiddleware(rsLive).layer),
    Layer.provideMerge(SessionAuthLive),
    Layer.provideMerge(AutumnService.Default),
    Layer.provide(apiPrefixedRouter),
  );

  // Cloud-only WorkOS domain-verification routes; the auth middleware resolves
  // the URL org selector header before falling back to the session org, so slug
  // lookup needs the same request-scoped UserStoreService as other org-scoped
  // APIs.
  const OrgRoutes = HttpApiBuilder.layer(OrgHttpApi).pipe(
    Layer.provide(OrgHandlers),
    Layer.provide(orgAuthMiddleware(rsLive)),
    Layer.provideMerge(AutumnService.Default),
    Layer.provide(apiPrefixedRouter),
  );

  // Swagger UI at /api/docs + the OpenAPI JSON at /api/openapi.json, over the
  // `/api`-prefixed spec (so the served paths match).
  const DocsRoutes = Layer.mergeAll(
    HttpApiSwagger.layer(CloudOpenApi, { path: "/api/docs" }),
    HttpRouter.add("GET", "/api/openapi.json", Effect.succeed(HttpServerResponse.jsonUnsafe(spec))),
  );

  const BillingRoutes = AutumnRoutesLive.pipe(Layer.provide(requestScopedMiddleware(rsLive).layer));

  return [SessionRoutes, OrgRoutes, DocsRoutes, BillingRoutes, ApiErrorLoggingLive] as const;
};
