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

import { SystemError, SystemHttpApi } from "./api";
import { BetterAuth, countOrgMembers, type BetterAuthHandle } from "../auth/better-auth";
import { SelfHostDb, type SelfHostDbHandle } from "../db/self-host-db";
import { findRedeemableCode } from "../auth/invites";

// ---------------------------------------------------------------------------
// Handlers for the public system API. Unauthenticated; every DB touch is an
// Effect.tryPromise. `health` fails soft (a DB hiccup reports "degraded", it
// never throws); `setup-status` reports whether the one org has zero members.
// ---------------------------------------------------------------------------

export const SystemHandlers = HttpApiBuilder.group(SystemHttpApi, "system", (handlers) =>
  handlers
    .handle("health", () =>
      Effect.gen(function* () {
        const { client } = yield* SelfHostDb;
        const status = yield* Effect.tryPromise({
          try: () => client.execute("SELECT 1"),
          catch: () => new SystemError({ message: "database unreachable" }),
        }).pipe(
          Effect.as("ok"),
          Effect.orElseSucceed(() => "degraded"),
        );
        return { status };
      }),
    )
    .handle("setupStatus", () =>
      Effect.gen(function* () {
        const { auth, organizationId } = yield* BetterAuth;
        // Count via Better Auth's adapter (see countOrgMembers) so this read is
        // consistent with how memberships are written.
        const count = yield* Effect.tryPromise({
          try: () => countOrgMembers(auth, organizationId),
          catch: () => new SystemError({ message: "failed to read setup status" }),
        });
        return { needsSetup: count === 0 };
      }),
    )
    .handle("inviteStatus", ({ params }) =>
      Effect.gen(function* () {
        const { client } = yield* SelfHostDb;
        const code = yield* Effect.tryPromise({
          try: () => findRedeemableCode(client, params.code),
          catch: () => new SystemError({ message: "failed to read invite status" }),
        });
        return { valid: code !== null };
      }),
    ),
);

export interface SelfHostSystemApiDeps {
  readonly betterAuth: BetterAuthHandle;
  readonly db: SelfHostDbHandle;
  readonly mountPrefix: `/${string}`;
}

/** Mountable extension route layer (see makeSelfHostAdminApiLayer). */
export const makeSelfHostSystemApiLayer = ({
  betterAuth,
  db,
  mountPrefix,
}: SelfHostSystemApiDeps) => {
  const prefixedRouter = Layer.effect(HttpRouter.HttpRouter)(
    Effect.map(HttpRouter.HttpRouter.asEffect(), (router) => router.prefixed(mountPrefix)),
  );
  return HttpApiBuilder.layer(SystemHttpApi).pipe(
    Layer.provide(SystemHandlers),
    Layer.provide(prefixedRouter),
    HttpRouter.provideRequest(
      Layer.mergeAll(Layer.succeed(BetterAuth)(betterAuth), Layer.succeed(SelfHostDb)(db)),
    ),
  );
};
