import "dotenv/config";
import "reflect-metadata";
import { MikroORM } from "@mikro-orm/core";
import { ReflectMetadataProvider } from "@mikro-orm/decorators/legacy";
import { PostgreSqlDriver } from "@mikro-orm/postgresql";
import { getSslConfig } from "./ssl.js";
import { createLogger } from "../logger/index.js";
const logger = createLogger("shared").child({ component: "orm" });
let ormInstance = null;
const GLOBAL_ENTITIES_KEY = "__openMercatoOrmEntities__";
function getRegisteredEntities() {
  return globalThis[GLOBAL_ENTITIES_KEY] ?? null;
}
function setRegisteredEntities(entities) {
  globalThis[GLOBAL_ENTITIES_KEY] = entities;
}
function registerOrmEntities(entities) {
  if (getRegisteredEntities() !== null && process.env.NODE_ENV === "development") {
    logger.debug("ORM entities re-registered (this may occur during HMR)");
  }
  setRegisteredEntities(entities);
}
function getOrmEntities() {
  const entities = getRegisteredEntities();
  if (!entities) {
    throw new Error("[Bootstrap] ORM entities not registered. Call registerOrmEntities() at bootstrap.");
  }
  return entities;
}
function parsePositiveIntEnv(raw) {
  const parsed = parseInt(raw || "");
  return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
}
function resolvePoolConfig(env = process.env) {
  const idleSessionTimeoutEnv = parseInt(env.DB_IDLE_SESSION_TIMEOUT_MS || "");
  const idleInTxTimeoutEnv = parseInt(env.DB_IDLE_IN_TRANSACTION_TIMEOUT_MS || "");
  return {
    poolMin: parseInt(env.DB_POOL_MIN || "2"),
    poolMax: parseInt(env.DB_POOL_MAX || "20"),
    poolIdleTimeout: parseInt(env.DB_POOL_IDLE_TIMEOUT || "3000"),
    poolAcquireTimeout: parseInt(env.DB_POOL_ACQUIRE_TIMEOUT || "6000"),
    idleSessionTimeoutMs: Number.isFinite(idleSessionTimeoutEnv) ? idleSessionTimeoutEnv : env.NODE_ENV === "production" ? void 0 : 6e5,
    // Finite default in every environment (including production) so a leaked or idle
    // open transaction cannot pin a pool connection indefinitely and exhaust the pool.
    // Mirrors the long-standing dev value; override (incl. 0 to disable) via env.
    idleInTransactionTimeoutMs: Number.isFinite(idleInTxTimeoutEnv) ? idleInTxTimeoutEnv : 12e4,
    // Opt-in guards against runaway statements and lock waits. No timeout when unset.
    statementTimeoutMs: parsePositiveIntEnv(env.DB_STATEMENT_TIMEOUT_MS),
    lockTimeoutMs: parsePositiveIntEnv(env.DB_LOCK_TIMEOUT_MS)
  };
}
function attachPoolErrorHandlers(pool) {
  pool.on("error", (err) => {
    logger.warn("Idle pg pool client error (connection reaped/terminated)", { err });
  });
  pool.on("connect", (client) => {
    client.on("error", (err) => {
      logger.warn("pg client error (connection reaped/terminated)", { err });
    });
  });
}
async function getOrm() {
  if (ormInstance) {
    return ormInstance;
  }
  const entities = getOrmEntities();
  const clientUrl = process.env.DATABASE_URL;
  if (!clientUrl) {
    throw new Error("DATABASE_URL is not set");
  }
  const {
    poolMin,
    poolMax,
    poolIdleTimeout,
    poolAcquireTimeout,
    idleSessionTimeoutMs,
    idleInTransactionTimeoutMs,
    statementTimeoutMs,
    lockTimeoutMs
  } = resolvePoolConfig();
  const connectionOptions = idleSessionTimeoutMs && idleSessionTimeoutMs > 0 ? `-c idle_session_timeout=${idleSessionTimeoutMs}` : void 0;
  const sslConfig = getSslConfig();
  if (process.env.OM_DB_POOL_DEBUG === "1" || process.env.OM_INTEGRATION_TEST === "true") {
    logger.info("Pool config", {
      poolMin,
      poolMax,
      poolIdleTimeout,
      poolAcquireTimeout,
      idleSessionTimeoutMs,
      idleInTransactionTimeoutMs,
      statementTimeoutMs,
      lockTimeoutMs,
      nodeEnv: process.env.NODE_ENV
    });
  }
  ormInstance = await MikroORM.init({
    driver: PostgreSqlDriver,
    clientUrl,
    entities,
    debug: false,
    // v7 no longer defaults to ReflectMetadataProvider. Entities in this repo use
    // `@mikro-orm/decorators/legacy`, which relies on TypeScript `emitDecoratorMetadata`
    // + reflect-metadata for type inference (nullability, column types). Without this,
    // inferred types are silently wrong at runtime.
    metadataProvider: ReflectMetadataProvider,
    // MikroORM v7 pool shape (min/max/idleTimeoutMillis). Knex-era `acquireTimeoutMillis` /
    // `destroyTimeoutMillis` were removed; acquire wait maps to pg `connectionTimeoutMillis`
    // below under `driverOptions`. Mirror `connectionTimeoutMillis` here too — older Mikro
    // versions read it from `pool`; v7 reads from `driverOptions` but accepting both
    // costs nothing and protects us from upstream config-merge regressions.
    pool: {
      min: poolMin,
      max: poolMax,
      idleTimeoutMillis: poolIdleTimeout,
      acquireTimeoutMillis: poolAcquireTimeout
    },
    // Driver options are merged into pg.PoolConfig (ClientConfig + pg-pool).
    driverOptions: {
      connectionTimeoutMillis: poolAcquireTimeout,
      idle_in_transaction_session_timeout: idleInTransactionTimeoutMs,
      statement_timeout: statementTimeoutMs,
      lock_timeout: lockTimeoutMs,
      options: connectionOptions,
      ssl: sslConfig,
      onPoolCreated: (pool) => {
        attachPoolErrorHandlers(pool);
        if (process.env.OM_DB_POOL_DEBUG === "1" || process.env.OM_INTEGRATION_TEST === "true") {
          logger.info("pg pool created with options", {
            max: pool.options?.max,
            min: pool.options?.min,
            idleTimeoutMillis: pool.options?.idleTimeoutMillis,
            connectionTimeoutMillis: pool.options?.connectionTimeoutMillis
          });
        }
      }
    }
  });
  return ormInstance;
}
async function closeOrmIfLoaded() {
  if (ormInstance) {
    await ormInstance.close(true);
    ormInstance = null;
  }
}
if (process.env.NODE_ENV !== "production") {
  void closeOrmIfLoaded();
}
export {
  attachPoolErrorHandlers,
  getOrm,
  getOrmEntities,
  registerOrmEntities,
  resolvePoolConfig
};
//# sourceMappingURL=mikro.js.map
