import { describe, expect, it } from "vitest";
import { unsafeOpaqueId, type PrincipalId, type ProjectId, type SessionId } from "@awp/contracts";
import type {
  ApplicationTransaction,
  I1LifecycleService,
  SessionRecord,
  UnitOfWork,
  WorkspaceExecutionDispatcher,
} from "@awp/application";
import { createControlPlaneApp } from "../../../apps/control-plane/src/app.js";
import { OperatorSessionManager } from "../../../apps/control-plane/src/operator-session.js";

function sessionManager() {
  const rows: SessionRecord[] = [];
  const repository = {
    async getById(id: SessionId) {
      return rows.find((row) => row.id === id);
    },
    async getByTokenHash(tokenHash: string) {
      return rows.find((row) => row.tokenHash === tokenHash);
    },
    async insert(session: SessionRecord) {
      rows.push(session);
    },
    async updateLastSeen(id: SessionId, lastSeenAt: string) {
      const index = rows.findIndex((row) => row.id === id);
      rows[index] = { ...rows[index]!, lastSeenAt };
    },
    async revoke(id: SessionId, revokedAt: string) {
      const index = rows.findIndex((row) => row.id === id);
      rows[index] = { ...rows[index]!, revokedAt };
    },
    async revokeAllByPrincipal(principalId: PrincipalId, revokedAt: string) {
      let count = 0;
      for (let index = 0; index < rows.length; index += 1) {
        if (rows[index]!.principalId !== principalId) continue;
        rows[index] = { ...rows[index]!, revokedAt };
        count += 1;
      }
      return count;
    },
  };
  const tx = { sessions: repository } as unknown as ApplicationTransaction;
  const uow: UnitOfWork = { transaction: async (work) => work(tx) };
  return new OperatorSessionManager(uow, {
    passwordHash: "$argon2id$v=19$m=65536,t=3,p=1$test$test",
    verifyPassword: async (_hash, password) => password === "correct",
  });
}

describe("control-plane authentication boundary", () => {
  it("fails closed without a session and attributes owner mutations to the resolved Human Principal", async () => {
    const sessions = sessionManager();
    const created = await sessions.login("correct", "test-agent");
    let mutationPrincipal: { id: string; kind: string } | undefined;
    const lifecycle = {
      async listProjects() {
        return [];
      },
      async createProject(input: {
        name: string;
        repositoryUrl: string;
        context: { authority: { principal: { id: string; kind: string } } };
      }) {
        mutationPrincipal = input.context.authority.principal;
        return {
          id: unsafeOpaqueId<ProjectId>("project-auth-test"),
          name: input.name,
          repositoryUrl: input.repositoryUrl,
          requiredChecks: [],
          status: "active" as const,
          revision: 1,
        };
      },
    } as unknown as I1LifecycleService;
    const app = createControlPlaneApp({
      lifecycle,
      sessionManager: sessions,
      health: async () => ({
        status: "ok",
        database: "postgresql",
        migrations: { applied: 1, expected: 1, pending: 0, drift: [] },
      }),
    });

    expect((await app.request("/internal/projects")).status).toBe(401);
    expect(
      (
        await app.request("/internal/projects", {
          headers: { "x-awp-principal": "principal:forged" },
        })
      ).status,
    ).toBe(401);

    const list = await app.request("/internal/projects", {
      headers: { "x-awp-session": created!.token },
    });
    expect(list.status).toBe(200);

    const mutation = await app.request("/internal/projects", {
      method: "POST",
      headers: {
        "content-type": "application/json",
        "x-awp-session": created!.token,
      },
      body: JSON.stringify({ name: "Auth project", repositoryUrl: "https://github.com/a/b.git" }),
    });
    expect(mutation.status).toBe(201);
    expect(mutationPrincipal).toEqual({
      id: "principal:owner",
      kind: "human",
      capabilities: [],
    });
  });

  it("allows Attempt-token callbacks without a browser session but attributes them to System", async () => {
    let callbackPrincipal: { id: string; kind: string } | undefined;
    const lifecycle = {} as I1LifecycleService;
    const execution = {
      async failAttempt(
        _input: unknown,
        context: { authority: { principal: { id: string; kind: string } } },
      ) {
        callbackPrincipal = context.authority.principal;
        return { id: "attempt-callback", status: "failed" };
      },
    } as unknown as WorkspaceExecutionDispatcher;
    const app = createControlPlaneApp({
      lifecycle,
      execution,
      sessionManager: sessionManager(),
      health: async () => ({
        status: "ok",
        database: "postgresql",
        migrations: { applied: 1, expected: 1, pending: 0, drift: [] },
      }),
    });

    const response = await app.request("/internal/execution/fail", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        attemptId: "attempt-callback",
        token: "attempt-scoped-token",
        reason: "test",
        workspaceCheckpointDigest: "checkpoint",
        workspaceCheckpointedAt: "2026-08-23T00:00:00.000Z",
      }),
    });
    expect(response.status).toBe(201);
    expect(callbackPrincipal).toEqual({
      id: "principal:system:control-plane",
      kind: "system",
      capabilities: [],
    });
  });
});
