import { asFunction, createContainer, asValue, InjectionMode } from "awilix";
import { RequestContext } from "@mikro-orm/core";
import { getOrm } from "@open-mercato/shared/lib/db/mikro";
import { BasicQueryEngine } from "@open-mercato/shared/lib/query/engine";
import { DefaultDataEngine } from "@open-mercato/shared/lib/data/engine";
import { commandRegistry, CommandBus } from "@open-mercato/shared/lib/commands";
import { applyDiOverridesToContainer } from "@open-mercato/shared/modules/overrides";
import { createOptimisticLockGuardService } from "@open-mercato/shared/lib/crud/optimistic-lock";
import { getAllOptimisticLockReaders } from "@open-mercato/shared/lib/crud/optimistic-lock-store";
import { createCommandOptimisticLockGuardService } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
import { createLogger } from "../logger/index.js";
const logger = createLogger("shared").child({ component: "di" });
const GLOBAL_KEY = "__openMercatoDiRegistrars__";
const APP_DI_REGISTRAR_KEY = "__openMercatoAppDiRegistrar__";
const APP_DI_LOAD_WARNING_KEY = "__openMercatoAppDiLoadWarningEmitted__";
const APP_DI_REGISTER_WARNING_KEY = "__openMercatoAppDiRegisterWarningEmitted__";
const APP_DI_MODULE_UNRESOLVABLE_KEY = "__openMercatoAppDiModuleUnresolvable__";
const BOOTSTRAP_CACHE_KEY = "__openMercatoBootstrapCache__";
const ENCRYPTION_ENABLED_KEY = "__openMercatoEncryptionEnabledCache__";
const BOOTSTRAP_CACHE_KEYS = [
  "cache",
  "eventBus",
  "kmsService",
  "tenantEncryptionService",
  "rateLimiterService",
  "searchModuleConfigs",
  "searchIndexer"
];
function isBootstrapCacheEnabled() {
  const raw = process.env.OM_BOOTSTRAP_CACHE;
  if (raw === void 0) return false;
  const normalized = raw.trim().toLowerCase();
  if (!normalized.length) return false;
  if (normalized === "0" || normalized === "off" || normalized === "false" || normalized === "no") return false;
  return true;
}
function getBootstrapCache() {
  if (!isBootstrapCacheEnabled()) return null;
  const existing = globalThis[BOOTSTRAP_CACHE_KEY];
  return existing && typeof existing === "object" ? existing : null;
}
function setBootstrapCache(entry) {
  if (!isBootstrapCacheEnabled()) return;
  globalThis[BOOTSTRAP_CACHE_KEY] = entry;
}
function harvestBootstrapCache(container) {
  const entry = {};
  for (const key of BOOTSTRAP_CACHE_KEYS) {
    try {
      const value = container.resolve(key);
      if (value !== void 0 && value !== null) entry[key] = value;
    } catch {
    }
  }
  return entry;
}
function getCachedEncryptionEnabled(service) {
  if (!service || typeof service.isEnabled !== "function") return false;
  const cached = globalThis[ENCRYPTION_ENABLED_KEY];
  if (typeof cached === "boolean") return cached;
  try {
    const result = !!service.isEnabled();
    globalThis[ENCRYPTION_ENABLED_KEY] = result;
    return result;
  } catch {
    return null;
  }
}
function getGlobalRegistrars() {
  return globalThis[GLOBAL_KEY] ?? null;
}
function setGlobalRegistrars(registrars) {
  globalThis[GLOBAL_KEY] = registrars;
}
function registerDiRegistrars(registrars) {
  const existing = getGlobalRegistrars();
  if (existing !== null && process.env.NODE_ENV === "development") {
    logger.debug("DI registrars re-registered (this may occur during HMR)");
  }
  setGlobalRegistrars(registrars);
  globalThis[BOOTSTRAP_CACHE_KEY] = null;
  globalThis[ENCRYPTION_ENABLED_KEY] = void 0;
  globalThis[APP_DI_MODULE_UNRESOLVABLE_KEY] = void 0;
}
function getDiRegistrars() {
  const registrars = getGlobalRegistrars();
  if (!registrars) {
    throw new Error("[Bootstrap] DI registrars not registered. Call registerDiRegistrars() at bootstrap.");
  }
  return registrars;
}
function getAppDiRegistrar() {
  const registrar = globalThis[APP_DI_REGISTRAR_KEY];
  return typeof registrar === "function" ? registrar : null;
}
function registerAppDiRegistrar(registrar) {
  ;
  globalThis[APP_DI_REGISTRAR_KEY] = registrar;
}
function resetBootstrapCache() {
  globalThis[BOOTSTRAP_CACHE_KEY] = null;
  globalThis[ENCRYPTION_ENABLED_KEY] = void 0;
  globalThis[APP_DI_LOAD_WARNING_KEY] = void 0;
  globalThis[APP_DI_REGISTER_WARNING_KEY] = void 0;
  globalThis[APP_DI_MODULE_UNRESOLVABLE_KEY] = void 0;
}
function isAppDiModuleUnresolvable() {
  return globalThis[APP_DI_MODULE_UNRESOLVABLE_KEY] === true;
}
function markAppDiModuleUnresolvable() {
  ;
  globalThis[APP_DI_MODULE_UNRESOLVABLE_KEY] = true;
}
function isAppDiModuleNotFound(error) {
  if (!error || typeof error !== "object") return false;
  const { code, message } = error;
  const text = typeof message === "string" ? message : "";
  const moduleNotFound = code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND" || text.startsWith("Cannot find module") || text.startsWith("Cannot find package");
  return moduleNotFound && /(?:module|package) ['"]@\/di['"]/.test(text);
}
function warnAppDiFailureOnce(key, message, error) {
  const globalScope = globalThis;
  if (globalScope[key] === true) return;
  globalScope[key] = true;
  logger.warn(message, { err: error });
}
function isAwilixResolver(value) {
  return Boolean(value && typeof value === "object" && typeof value.resolve === "function");
}
function toAwilixRegistrations(registrations) {
  return Object.fromEntries(
    Object.entries(registrations).map(([key, value]) => [
      key,
      isAwilixResolver(value) ? value : asValue(value)
    ])
  );
}
async function createRequestContainer() {
  const diRegistrars = getDiRegistrars();
  const orm = await getOrm();
  const baseEm = RequestContext.getEntityManager() ?? orm.em;
  const em = baseEm.fork({ clear: true, freshEventManager: true, useContext: true });
  const container = createContainer({ injectionMode: InjectionMode.CLASSIC });
  container.register({
    em: asValue(em),
    queryEngine: asValue(new BasicQueryEngine(em, void 0, () => {
      try {
        return container.resolve("tenantEncryptionService");
      } catch {
        return null;
      }
    })),
    dataEngine: asValue(new DefaultDataEngine(em, container)),
    commandRegistry: asValue(commandRegistry),
    commandBus: asValue(new CommandBus()),
    // Default OSS optimistic-lock guard. Reads from the global reader store
    // (populated by `makeCrudRoute` auto-registration + any module-DI
    // hand-wired calls to `registerOptimisticLockReaders`). Service is
    // strictly additive: when `OM_OPTIMISTIC_LOCK=off` (or no header is
    // sent) it short-circuits at validateMutation. Module-level di.ts
    // registrations override this default via Awilix replace semantics —
    // see the enterprise `record_locks` module for the canonical override.
    // Spec: .ai/specs/implemented/2026-05-25-oss-optimistic-locking.md
    crudMutationGuardService: asFunction(
      (em2) => createOptimisticLockGuardService({
        getEm: () => em2,
        readers: getAllOptimisticLockReaders()
      })
    ).scoped(),
    // Default OSS command-level optimistic-lock guard, awaited by
    // `enforceCommandOptimisticLockWithGuards` for Command-pattern writes.
    // Header/explicit-token compare only (no `resolveExpected`), so it is
    // behaviourally identical to calling `enforceCommandOptimisticLock`
    // directly. The enterprise `record_locks` module overrides this DI key
    // with a lock-backed `resolveExpected` via Awilix replace semantics.
    // Spec: .ai/specs/enterprise/2026-06-09-record-locks-unified-coverage.md (Phase 0)
    commandOptimisticLockGuardService: asFunction(
      () => createCommandOptimisticLockGuardService()
    ).scoped()
  });
  for (const [registrarIndex, reg] of diRegistrars.entries()) {
    try {
      reg?.(container);
    } catch (error) {
      logger.error("Module DI registrar failed", { registrarIndex, err: error });
    }
  }
  const alreadyBootstrappedOnThisContainer = !!container.registrations?.eventBus;
  if (!alreadyBootstrappedOnThisContainer) {
    const cached = getBootstrapCache();
    if (cached) {
      const replay = {};
      for (const [key, value] of Object.entries(cached)) {
        if (value !== void 0 && value !== null) replay[key] = asValue(value);
      }
      if (Object.keys(replay).length > 0) container.register(replay);
    } else {
      try {
        const { bootstrap } = await import("@open-mercato/core/bootstrap");
        if (bootstrap && typeof bootstrap === "function") {
          await bootstrap(container);
          setBootstrapCache(harvestBootstrapCache(container));
        }
      } catch {
      }
    }
  }
  const appDiRegistrar = getAppDiRegistrar();
  if (appDiRegistrar) {
    try {
      await appDiRegistrar(container);
    } catch (error) {
      logger.error("App-level DI registrar failed", { err: error });
    }
  } else if (!isAppDiModuleUnresolvable()) {
    try {
      const appDi = await import("@/di");
      if (appDi?.register) {
        try {
          const maybe = appDi.register(container);
          if (maybe && typeof maybe.then === "function") await maybe;
        } catch (err) {
          warnAppDiFailureOnce(
            APP_DI_REGISTER_WARNING_KEY,
            "App-level DI override (src/di.ts register()) threw; its registrations are skipped",
            err
          );
        }
      }
    } catch (err) {
      if (isAppDiModuleNotFound(err)) {
        markAppDiModuleUnresolvable();
        logger.debug("App-level DI override module (@/di) not resolvable; skipping", { err });
      } else {
        warnAppDiFailureOnce(
          APP_DI_LOAD_WARNING_KEY,
          "App-level DI override module (@/di) failed to load; its registrations are skipped",
          err
        );
      }
    }
  }
  applyDiOverridesToContainer({
    register: (registrations) => container.register(toAwilixRegistrations(registrations)),
    unregister: (key) => container.register({ [key]: asValue(void 0) })
  });
  try {
    const emForEnc = container.resolve("em");
    const tenantEncryptionService = container.hasRegistration("tenantEncryptionService") ? container.resolve("tenantEncryptionService") : null;
    if (emForEnc && tenantEncryptionService && getCachedEncryptionEnabled(tenantEncryptionService) === true) {
      const { registerTenantEncryptionSubscriber } = await import("@open-mercato/shared/lib/encryption/subscriber");
      registerTenantEncryptionSubscriber(emForEnc, tenantEncryptionService);
    }
  } catch {
  }
  return container;
}
try {
  require("server-only");
} catch {
}
export {
  createRequestContainer,
  getDiRegistrars,
  registerAppDiRegistrar,
  registerDiRegistrars,
  resetBootstrapCache
};
//# sourceMappingURL=container.js.map
