import { createRequire } from "node:module";
import { join } from "node:path";
import { PostgreSqlContainer, type StartedPostgreSqlContainer } from "@testcontainers/postgresql";
import { RedisContainer, type StartedRedisContainer } from "@testcontainers/redis";

// pg and drizzle-orm live in the api workspace's node_modules. Global-setup
// files run outside Vite's transform pipeline, so vitest resolve.alias does
// not apply. Use createRequire pointed at the api workspace instead.
const apiRequire = createRequire(join(process.cwd(), "apps/api/package.json"));
const pg = apiRequire("pg") as typeof import("pg");
const { drizzle } = apiRequire(
  "drizzle-orm/node-postgres",
) as typeof import("drizzle-orm/node-postgres");
const { migrate } = apiRequire(
  "drizzle-orm/node-postgres/migrator",
) as typeof import("drizzle-orm/node-postgres/migrator");

let container: StartedPostgreSqlContainer | undefined;
let redisContainer: StartedRedisContainer | undefined;

export async function setup(): Promise<void> {
  // Base server: testcontainer by default, or an existing server via
  // TEST_DATABASE_URL (must allow CREATE DATABASE, e.g. postgres://...:5432/postgres).
  let baseUrl: string;
  if (process.env.TEST_DATABASE_URL) {
    baseUrl = process.env.TEST_DATABASE_URL;
  } else {
    container = await new PostgreSqlContainer("postgres:17-alpine").start();
    baseUrl = container.getConnectionUri();
  }
  process.env.TEST_PG_BASE_URL = baseUrl; // forks inherit this

  // Build the migrated template database once; forks clone it.
  const admin = new pg.Client({ connectionString: baseUrl });
  await admin.connect();
  await admin.query("DROP DATABASE IF EXISTS snapotter_template");
  await admin.query("CREATE DATABASE snapotter_template");
  await admin.end();

  const templateUrl = new URL(baseUrl);
  templateUrl.pathname = "/snapotter_template";
  const pool = new pg.Pool({ connectionString: templateUrl.toString(), max: 1 });
  try {
    await migrate(drizzle(pool), { migrationsFolder: join(process.cwd(), "apps/api/drizzle") });
  } finally {
    await pool.end();
  }

  // Redis server: testcontainer by default, or an existing server via
  // TEST_REDIS_URL (e.g. inside Docker where testcontainers cannot spawn).
  if (process.env.TEST_REDIS_URL) {
    process.env.TEST_REDIS_BASE_URL = process.env.TEST_REDIS_URL;
  } else {
    redisContainer = await new RedisContainer("redis:8-alpine").start();
    process.env.TEST_REDIS_BASE_URL = redisContainer.getConnectionUrl();
  }
}

export async function teardown(): Promise<void> {
  await redisContainer?.stop();
  await container?.stop();
}
