import { afterAll, beforeEach, describe, expect, it } from "vitest";
import { ProviderConnectionService } from "../../src/connections/providerConnections.js";
import { DatabaseRuntimeSettings } from "../../src/settings/runtime.js";
import type { ProviderUpstreamCredential } from "../../src/connections/providerProxy.js";
import { DEFAULT_ORG_ID, prisma, resetDb, seedTestOrganization, testSecretEnvelope } from "./helpers.js";

const secrets = testSecretEnvelope;

describe("project-scoped provider runtime settings", () => {
  beforeEach(async () => {
    await resetDb();
  });

  afterAll(async () => {
    await prisma.$disconnect();
  });

  it("resolves two projects only through their own Organization connection", async () => {
    await seedTestOrganization("org_other", "other");
    await Promise.all([
      prisma.user.create({ data: { id: "user-a", feishuOpenId: "ou_a", displayName: "A" } }),
      prisma.user.create({ data: { id: "user-b", feishuOpenId: "ou_b", displayName: "B" } }),
      prisma.project.create({
        data: {
          id: "project-a",
          organizationId: DEFAULT_ORG_ID,
          name: "A",
          workspaceDir: "/tmp/project-a",
        },
      }),
      prisma.project.create({
        data: {
          id: "project-b",
          organizationId: "org_other",
          name: "B",
          workspaceDir: "/tmp/project-b",
        },
      }),
    ]);
    await Promise.all([
      prisma.organizationMembership.create({
        data: { organizationId: DEFAULT_ORG_ID, userId: "user-a", role: "ADMIN" },
      }),
      prisma.organizationMembership.create({
        data: { organizationId: "org_other", userId: "user-b", role: "OWNER" },
      }),
    ]);
    const service = new ProviderConnectionService(prisma, secrets, async () => {});
    await service.rotateByok({
      organizationId: DEFAULT_ORG_ID,
      providerId: "openrouter",
      actorUserId: "user-a",
      baseUrl: "https://provider-a.example/api",
      authToken: "provider-a-token",
      anthropicApiKey: "provider-a-api-key",
    });
    await service.rotateByok({
      organizationId: "org_other",
      providerId: "openrouter",
      actorUserId: "user-b",
      baseUrl: "https://provider-b.example/api",
      authToken: "provider-b-token",
    });
    const leasedCredentials: ProviderUpstreamCredential[] = [];
    const settings = new DatabaseRuntimeSettings(prisma, secrets, {
      ANTHROPIC_AUTH_TOKEN: "forbidden-global-token",
      ANTHROPIC_BASE_URL: "https://forbidden-global.example",
    }, async (credential) => {
      leasedCredentials.push(credential);
      return {
        sdkEnv: {
          ANTHROPIC_BASE_URL: "http://127.0.0.1:12345",
          ANTHROPIC_AUTH_TOKEN: `run-capability-${leasedCredentials.length}`,
          ANTHROPIC_API_KEY: "",
        },
        sensitiveValues: [`run-capability-${leasedCredentials.length}`],
        async close() {},
      };
    });

    const providerA = await settings.provider("openrouter", { projectId: "project-a" });
    const providerB = await settings.provider("openrouter", { projectId: "project-b" });
    expect(JSON.stringify([providerA, providerB])).not.toContain("provider-a-token");
    expect(JSON.stringify([providerA, providerB])).not.toContain("provider-b-token");

    const leaseA = await providerA.openAgentLease({ runId: "run-a" });
    const leaseB = await providerB.openAgentLease({ runId: "run-b" });
    expect(leaseA.sdkEnv).toMatchObject({
      ANTHROPIC_BASE_URL: "http://127.0.0.1:12345",
      ANTHROPIC_AUTH_TOKEN: "run-capability-1",
      ANTHROPIC_API_KEY: "",
    });
    expect(leaseB.sdkEnv.ANTHROPIC_AUTH_TOKEN).toBe("run-capability-2");
    expect(leasedCredentials).toEqual([
      {
        baseUrl: "https://provider-a.example/api",
        authToken: "provider-a-token",
        anthropicApiKey: "provider-a-api-key",
      },
      {
        baseUrl: "https://provider-b.example/api",
        authToken: "provider-b-token",
        anthropicApiKey: "",
      },
    ]);
  });

  it("fails closed without project scope or for missing, inactive, and corrupt connections", async () => {
    await prisma.user.create({ data: { id: "user-a", feishuOpenId: "ou_a", displayName: "A" } });
    await prisma.organizationMembership.create({
      data: { organizationId: DEFAULT_ORG_ID, userId: "user-a", role: "ADMIN" },
    });
    await prisma.project.create({
      data: {
        id: "project-a",
        organizationId: DEFAULT_ORG_ID,
        name: "A",
        workspaceDir: "/tmp/project-a",
      },
    });
    const settings = new DatabaseRuntimeSettings(prisma, secrets, {
      ANTHROPIC_AUTH_TOKEN: "must-not-be-a-fallback",
    });

    await expect(settings.provider("openrouter")).rejects.toThrow("projectId is required");
    await expect(settings.provider("openrouter", { projectId: "project-a" })).rejects.toThrow(
      "active provider connection not found",
    );

    const service = new ProviderConnectionService(prisma, secrets, async () => {});
    await service.rotateByok({
      organizationId: DEFAULT_ORG_ID,
      providerId: "openrouter",
      actorUserId: "user-a",
      baseUrl: "https://provider-a.example/api",
      authToken: "provider-a-token",
    });
    await prisma.organization.update({
      where: { id: DEFAULT_ORG_ID },
      data: { status: "SUSPENDED" },
    });
    await expect(settings.provider("openrouter", { projectId: "project-a" })).rejects.toThrow(
      `organization ${DEFAULT_ORG_ID} is SUSPENDED`,
    );

    await prisma.organization.update({ where: { id: DEFAULT_ORG_ID }, data: { status: "ACTIVE" } });
    const connection = await prisma.organizationProviderConnection.findUniqueOrThrow({
      where: {
        organizationId_providerId: { organizationId: DEFAULT_ORG_ID, providerId: "openrouter" },
      },
    });
    const version = await prisma.providerCredentialVersion.findUniqueOrThrow({
      where: { id: connection.activeSecretVersionId ?? "missing" },
    });
    const envelope = structuredClone(version.envelope) as {
      payload: { ciphertext: string };
    };
    const bytes = Buffer.from(envelope.payload.ciphertext, "base64");
    bytes[0] = (bytes[0] ?? 0) ^ 1;
    envelope.payload.ciphertext = bytes.toString("base64");
    await prisma.providerCredentialVersion.update({
      where: { id: version.id },
      data: { envelope },
    });
    const corruptProvider = await settings.provider("openrouter", { projectId: "project-a" });
    await expect(corruptProvider.openAgentLease({ runId: "run-corrupt" })).rejects.toThrow(
      "secret envelope authentication failed",
    );
  });
});
