import { describe, expect, it } from "vitest";
import {
  dropDatabaseWithForceOrDrain,
  waitForDatabaseConnectionsToDrain,
} from "./index.mjs";

describe("database lifecycle helpers", () => {
  it("uses DROP DATABASE ... WITH (FORCE) when supported", async () => {
    const calls = [];

    await dropDatabaseWithForceOrDrain(
      { containerName: "pg", password: "pw", user: "user" },
      "demo",
      {
        async runAdminQuery(_infra, args) {
          calls.push(args);
          return "";
        },
      }
    );

    expect(calls).toEqual([["-c", 'DROP DATABASE IF EXISTS "demo" WITH (FORCE)']]);
  });

  it("falls back to draining connections when forced drop is unsupported", async () => {
    const calls = [];
    const counts = ["2", "0"];

    await dropDatabaseWithForceOrDrain(
      { containerName: "pg", password: "pw", user: "user" },
      "demo",
      {
        async runAdminQuery(_infra, args) {
          calls.push(args);
          if (args[1] === 'DROP DATABASE IF EXISTS "demo" WITH (FORCE)') {
            const error = new Error('syntax error at or near "WITH"');
            error.stderr = 'ERROR: syntax error at or near "WITH"';
            throw error;
          }
          if (args[0] === "-tAc") {
            return counts.shift() || "0";
          }
          return "";
        },
        async databaseExists() {
          return true;
        },
        async sleep() {},
      }
    );

    expect(calls).toEqual([
      ["-c", 'DROP DATABASE IF EXISTS "demo" WITH (FORCE)'],
      ["-c", 'ALTER DATABASE "demo" WITH ALLOW_CONNECTIONS false'],
      [
        "-c",
        "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'demo' AND pid <> pg_backend_pid();",
      ],
      [
        "-tAc",
        "SELECT COUNT(*) FROM pg_stat_activity WHERE datname = 'demo' AND pid <> pg_backend_pid();",
      ],
      [
        "-c",
        "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'demo' AND pid <> pg_backend_pid();",
      ],
      [
        "-tAc",
        "SELECT COUNT(*) FROM pg_stat_activity WHERE datname = 'demo' AND pid <> pg_backend_pid();",
      ],
      ["-c", 'DROP DATABASE IF EXISTS "demo"'],
    ]);
  });

  it("times out clearly while waiting for lingering connections to drain", async () => {
    let now = 0;
    await expect(() =>
      waitForDatabaseConnectionsToDrain(
        { containerName: "pg", password: "pw", user: "user" },
        "demo",
        {
          async runAdminQuery(_infra, args) {
            if (args[0] === "-tAc") return "1";
            return "";
          },
          async sleep() {
            now += 10;
          },
          now: () => now,
          timeoutMs: 20,
        }
      )
    ).rejects.toThrow(/Timed out waiting for database "demo" connections to close/);
  });
});
