import { createServer } from "node:net";
import { once } from "node:events";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { DEFAULT_ORG_ID, prisma, resetDb, TEST_DATABASE_URL, TEST_SECRET_KEY, TEST_SECRET_KEY_ID, testSecretEnvelope } from "./helpers.js";
import { FeishuApplicationConnectionService } from "../../src/connections/feishuApplicationConnections.js";
import { ProviderConnectionService } from "../../src/connections/providerConnections.js";

const ENV_KEYS = [
  "DATABASE_URL",
  "ANTHROPIC_AUTH_TOKEN",
  "ANTHROPIC_API_KEY",
  "HUB_SILO_ORGANIZATION_ID",
  "HUB_PROJECT_WORKSPACE_ROOT",
  "HUB_SESSION_SECRET",
  "HUB_PUBLIC_BASE_URL",
  "HUB_FEISHU_LISTENER_ENABLED",
  "HUB_SECRET_KEYRING_FILE",
  "HOST",
  "PORT",
  "HUB_HTTP_BODY_LIMIT_BYTES",
  "HUB_MAX_FILES_PER_MESSAGE",
  "HUB_MAX_FILE_BYTES",
  "HUB_AGENT_MAX_CONCURRENT_RUNS",
  "HUB_AGENT_MAX_RUN_SECONDS",
  "HUB_HTTP_REQUESTS_PER_MINUTE",
  "HUB_FEISHU_EVENTS_PER_MINUTE",
] as const;

describe("Hub startup", () => {
  const previousEnv = new Map(ENV_KEYS.map((key) => [key, process.env[key]]));

  afterEach(() => {
    for (const [key, value] of previousEnv) {
      if (value === undefined) delete process.env[key];
      else process.env[key] = value;
    }
  });

  it("rejects startup before the Feishu listener when the HTTP bind fails", async () => {
    await resetDb();
    const owner = await prisma.user.create({
      data: {
        feishuOpenId: "legacy-server-start-owner",
        displayName: "Server Start Owner",
        organizationMemberships: { create: { organizationId: DEFAULT_ORG_ID, role: "OWNER" } },
      },
    });
    await new FeishuApplicationConnectionService(prisma, testSecretEnvelope, async () => {}).rotateCustomerApplication({
      organizationId: DEFAULT_ORG_ID,
      actorUserId: owner.id,
      appId: "test-app",
      appSecret: "test-secret",
      botOpenId: "ou_test_bot",
    });
    await new ProviderConnectionService(prisma, testSecretEnvelope, async () => {}).rotateByok({
      organizationId: DEFAULT_ORG_ID,
      actorUserId: owner.id,
      providerId: "openrouter",
      baseUrl: "https://openrouter.ai/api",
      authToken: "test-provider-token",
    });
    const blocker = createServer();
    blocker.listen(0, "127.0.0.1");
    await once(blocker, "listening");
    const address = blocker.address();
    if (address === null || typeof address === "string") throw new Error("expected an INET test listener");

    const keyringDirectory = await mkdtemp(join(tmpdir(), "cph-hub-start-keyring-"));
    const keyringFile = join(keyringDirectory, "keyring.json");
    await writeFile(keyringFile, JSON.stringify({
      version: 1,
      activeKeyId: TEST_SECRET_KEY_ID,
      keys: { [TEST_SECRET_KEY_ID]: TEST_SECRET_KEY.toString("base64") },
    }), { mode: 0o600 });

    Object.assign(process.env, {
      DATABASE_URL: TEST_DATABASE_URL,
      HUB_SILO_ORGANIZATION_ID: DEFAULT_ORG_ID,
      HUB_PROJECT_WORKSPACE_ROOT: "/tmp/cph-hub-server-start-test",
      HUB_SESSION_SECRET: "integration-session-secret-with-32-bytes",
      HUB_PUBLIC_BASE_URL: "https://hub.example.test",
      HUB_FEISHU_LISTENER_ENABLED: "false",
      HUB_SECRET_KEYRING_FILE: keyringFile,
      HOST: "127.0.0.1",
      PORT: String(address.port),
      HUB_HTTP_BODY_LIMIT_BYTES: "1048576",
      HUB_MAX_FILES_PER_MESSAGE: "8",
      HUB_MAX_FILE_BYTES: "26214400",
      HUB_AGENT_MAX_CONCURRENT_RUNS: "1",
      HUB_AGENT_MAX_RUN_SECONDS: "900",
      HUB_HTTP_REQUESTS_PER_MINUTE: "120",
      HUB_FEISHU_EVENTS_PER_MINUTE: "120",
    });

    try {
      const { startHub } = await import("../../src/hub.js");
      await expect(startHub()).rejects.toMatchObject({ code: "EADDRINUSE" });
    } finally {
      blocker.close();
      await once(blocker, "close");
      await rm(keyringDirectory, { recursive: true, force: true });
    }
  });
});
