import Fastify from "fastify";
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
import { mintSessionToken, sessionCookieHeader } from "../../src/admin/routes/authRoutes.js";
import { registerAdminPlugin } from "../../src/admin/plugin.js";
import { FeishuReadinessError } from "../../src/connections/feishuReadiness.js";
import { DEFAULT_ORG_ID, prisma, resetDb, testSecretEnvelope } from "./helpers.js";

const SESSION_SECRET = "integration-test-session-secret";

async function buildApp(readinessProbe: (input: {
  readonly appId: string;
  readonly appSecret: string;
  readonly botOpenId: string;
}) => Promise<void>) {
  const app = Fastify({ logger: false });
  await registerAdminPlugin(app, {
    prisma,
    sessionSecret: SESSION_SECRET,
    publicBaseUrl: "http://127.0.0.1:8788",
    feishuAppId: "cli_legacy_auth_only",
    feishuAppSecret: "legacy-auth-secret",
    projectWorkspaceRoot: "/tmp/cph-test-workspaces",
    secretEnvelope: testSecretEnvelope,
    feishuConnectionReadinessProbe: readinessProbe,
    cookieSecure: false,
  });
  await app.ready();
  return app;
}

async function seedUser(role: "OWNER" | "ADMIN" | "MEMBER"): Promise<string> {
  const id = `feishu-${role.toLowerCase()}`;
  const openId = `ou_feishu_${role.toLowerCase()}`;
  await prisma.user.create({ data: { id, feishuOpenId: openId, displayName: id } });
  await prisma.organizationMembership.create({
    data: { organizationId: DEFAULT_ORG_ID, userId: id, role },
  });
  return mintSessionToken({ userId: id, feishuOpenId: openId }, SESSION_SECRET);
}

describe("Organization Feishu Application Connection API", () => {
  beforeEach(async () => {
    await resetDb();
  });

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

  it("accepts write-only credentials and returns only redacted metadata", async () => {
    const token = await seedUser("OWNER");
    const readiness = vi.fn(async () => {});
    const app = await buildApp(readiness);
    const credential = {
      appId: "cli_customer",
      appSecret: "api-secret-never-return",
      botOpenId: "ou_customer_bot",
      verificationToken: "verification-never-return",
      encryptKey: "encrypt-key-never-return",
    };
    try {
      const created = await app.inject({
        method: "PUT",
        url: "/api/org/test-default/feishu-application-connection",
        headers: {
          cookie: sessionCookieHeader(token),
          "content-type": "application/json",
        },
        payload: credential,
      });

      expect(created.statusCode).toBe(201);
      expect(created.json()).toMatchObject({
        status: "ACTIVE",
        activeVersion: 1,
        keyId: "test-active",
      });
      for (const plaintext of Object.values(credential)) {
        expect(created.body).not.toContain(plaintext);
      }
      expect(readiness).toHaveBeenCalledWith({
        appId: credential.appId,
        appSecret: credential.appSecret,
        botOpenId: credential.botOpenId,
        verificationToken: credential.verificationToken,
        encryptKey: credential.encryptKey,
        schemaVersion: 1,
      });

      const read = await app.inject({
        method: "GET",
        url: "/api/org/test-default/feishu-application-connection",
        headers: { cookie: sessionCookieHeader(token) },
      });
      expect(read.statusCode).toBe(200);
      expect(read.json()).toEqual({ connection: expect.objectContaining({ activeVersion: 1 }) });
      for (const plaintext of Object.values(credential)) {
        expect(read.body).not.toContain(plaintext);
      }

      const disabled = await app.inject({
        method: "DELETE",
        url: "/api/org/test-default/feishu-application-connection",
        headers: { cookie: sessionCookieHeader(token) },
      });
      expect(disabled.statusCode).toBe(200);
      expect(disabled.json()).toMatchObject({ status: "DISABLED", activeVersion: 1 });
      for (const plaintext of Object.values(credential)) {
        expect(disabled.body).not.toContain(plaintext);
      }
    } finally {
      await app.close();
    }
  });

  it("rejects members and does not persist credentials rejected by Feishu", async () => {
    const memberToken = await seedUser("MEMBER");
    const app = await buildApp(async () => {
      throw new FeishuReadinessError(
        "feishu_readiness_rejected",
        "Feishu bot identity does not match the configured botOpenId",
        "provider_rejection",
      );
    });
    const payload = {
      appId: "cli_rejected",
      appSecret: "rejected-secret",
      botOpenId: "ou_rejected",
    };
    try {
      const denied = await app.inject({
        method: "PUT",
        url: "/api/org/test-default/feishu-application-connection",
        headers: {
          cookie: sessionCookieHeader(memberToken),
          "content-type": "application/json",
        },
        payload,
      });
      expect(denied.statusCode).toBe(403);

      const adminToken = await seedUser("ADMIN");
      const rejected = await app.inject({
        method: "PUT",
        url: "/api/org/test-default/feishu-application-connection",
        headers: {
          cookie: sessionCookieHeader(adminToken),
          "content-type": "application/json",
        },
        payload,
      });
      expect(rejected.statusCode).toBe(400);
      expect(rejected.json()).toMatchObject({ error: { code: "feishu_credential_rejected" } });
      await expect(prisma.organizationFeishuApplicationConnection.count()).resolves.toBe(0);
      await expect(prisma.feishuApplicationCredentialVersion.count()).resolves.toBe(0);
    } finally {
      await app.close();
    }
  });
});
