import * as NodeServices from "@effect/platform-node/NodeServices";
import { expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";

import type { ServerConfigShape } from "../../config.ts";
import { ServerConfig } from "../../config.ts";
import { BootstrapCredentialServiceLive } from "./BootstrapCredentialService.ts";
import { ServerSecretStoreLive } from "./ServerSecretStore.ts";
import { SessionCredentialServiceLive } from "./SessionCredentialService.ts";
import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts";
import { AuthControlPlane } from "../Services/AuthControlPlane.ts";
import { makeAuthControlPlane } from "./AuthControlPlane.ts";
import { SessionCredentialService } from "../Services/SessionCredentialService.ts";

const makeServerConfigLayer = (
  overrides?: Partial<Pick<ServerConfigShape, "desktopBootstrapToken">>,
) =>
  Layer.effect(
    ServerConfig,
    Effect.gen(function* () {
      const config = yield* ServerConfig;
      return {
        ...config,
        ...overrides,
      } satisfies ServerConfigShape;
    }),
  ).pipe(
    Layer.provide(
      ServerConfig.layerTest(process.cwd(), { prefix: "belweave-auth-control-plane-test-" }),
    ),
  );

const makeAuthControlPlaneLayer = (
  overrides?: Partial<Pick<ServerConfigShape, "desktopBootstrapToken">>,
) =>
  Layer.effect(AuthControlPlane, makeAuthControlPlane).pipe(
    Layer.provideMerge(BootstrapCredentialServiceLive),
    Layer.provideMerge(SessionCredentialServiceLive),
    Layer.provideMerge(ServerSecretStoreLive),
    Layer.provideMerge(SqlitePersistenceMemory),
    Layer.provide(makeServerConfigLayer(overrides)),
  );

it.layer(NodeServices.layer)("AuthControlPlane", (it) => {
  it.effect("creates, lists, and revokes client pairing links", () =>
    Effect.gen(function* () {
      const authControlPlane = yield* AuthControlPlane;

      const created = yield* authControlPlane.createPairingLink({
        role: "client",
        subject: "one-time-token",
        label: "CI phone",
      });
      const listedBeforeRevoke = yield* authControlPlane.listPairingLinks({ role: "client" });
      const revoked = yield* authControlPlane.revokePairingLink(created.id);
      const listedAfterRevoke = yield* authControlPlane.listPairingLinks({ role: "client" });

      expect(created.role).toBe("client");
      expect(created.credential.length).toBeGreaterThan(0);
      expect(listedBeforeRevoke).toHaveLength(1);
      expect(listedBeforeRevoke[0]?.id).toBe(created.id);
      expect(listedBeforeRevoke[0]?.label).toBe("CI phone");
      expect(listedBeforeRevoke[0]?.credential).toBe(created.credential);
      expect(revoked).toBe(true);
      expect(listedAfterRevoke).toHaveLength(0);
    }).pipe(Effect.provide(makeAuthControlPlaneLayer())),
  );

  it.effect("issues bearer sessions and lists them without exposing raw tokens", () =>
    Effect.gen(function* () {
      const authControlPlane = yield* AuthControlPlane;
      const sessionCredentials = yield* SessionCredentialService;

      const issued = yield* authControlPlane.issueSession({
        label: "deploy-bot",
      });
      const verified = yield* sessionCredentials.verify(issued.token);
      const listedBeforeRevoke = yield* authControlPlane.listSessions();
      const revoked = yield* authControlPlane.revokeSession(issued.sessionId);
      const listedAfterRevoke = yield* authControlPlane.listSessions();

      expect(issued.method).toBe("bearer-session-token");
      expect(issued.role).toBe("owner");
      expect(issued.client.deviceType).toBe("bot");
      expect(issued.client.label).toBe("deploy-bot");
      expect(verified.sessionId).toBe(issued.sessionId);
      expect(verified.role).toBe("owner");
      expect(verified.method).toBe("bearer-session-token");
      expect(listedBeforeRevoke).toHaveLength(1);
      expect(listedBeforeRevoke[0]?.sessionId).toBe(issued.sessionId);
      expect("token" in (listedBeforeRevoke[0] ?? {})).toBe(false);
      expect(revoked).toBe(true);
      expect(listedAfterRevoke).toHaveLength(0);
    }).pipe(Effect.provide(makeAuthControlPlaneLayer())),
  );

  it.effect("surfaces lastConnectedAt through the listed session view", () =>
    Effect.gen(function* () {
      const authControlPlane = yield* AuthControlPlane;
      const sessionCredentials = yield* SessionCredentialService;

      const issued = yield* authControlPlane.issueSession({
        label: "remote-ipad",
      });
      const beforeConnect = yield* authControlPlane.listSessions();
      yield* sessionCredentials.markConnected(issued.sessionId);
      const afterConnect = yield* authControlPlane.listSessions();

      expect(beforeConnect[0]?.lastConnectedAt).toBeNull();
      expect(afterConnect[0]?.lastConnectedAt).not.toBeNull();
    }).pipe(Effect.provide(makeAuthControlPlaneLayer())),
  );
});
