import { CrudHttpError, isCrudHttpError } from "./errors.js";
import {
  OPTIMISTIC_LOCK_CONFLICT_CODE,
  OPTIMISTIC_LOCK_CONFLICT_ERROR,
  OPTIMISTIC_LOCK_ENV_VAR,
  OPTIMISTIC_LOCK_HEADER_NAME
} from "./optimistic-lock-headers.js";
import {
  normalizeIsoToken,
  parseOptimisticLockEnv
} from "./optimistic-lock.js";
function toIsoOrNull(value) {
  if (value == null) return null;
  if (value instanceof Date) {
    const ms = value.getTime();
    return Number.isFinite(ms) ? new Date(ms).toISOString() : null;
  }
  const trimmed = String(value).trim();
  if (!trimmed) return null;
  return normalizeIsoToken(trimmed);
}
function resolveConfig(envValue) {
  return parseOptimisticLockEnv(envValue !== void 0 ? envValue : process.env[OPTIMISTIC_LOCK_ENV_VAR]);
}
function isResourceLockEnabled(config, resourceKind) {
  if (config.mode === "off") return false;
  if (config.mode === "all") return true;
  return config.entities.has(resourceKind.toLowerCase());
}
function readOptimisticLockExpected(source) {
  if (!source) return null;
  const headers = source instanceof Headers ? source : source.headers instanceof Headers ? source.headers : null;
  if (!headers) return null;
  const direct = headers.get(OPTIMISTIC_LOCK_HEADER_NAME);
  if (typeof direct === "string" && direct.trim().length > 0) return direct.trim();
  return null;
}
function buildOptimisticLockConflictBody(currentIso, expectedIso) {
  return {
    error: OPTIMISTIC_LOCK_CONFLICT_ERROR,
    code: OPTIMISTIC_LOCK_CONFLICT_CODE,
    currentUpdatedAt: currentIso,
    expectedUpdatedAt: expectedIso
  };
}
function assertOptimisticLock(input) {
  const config = resolveConfig(input.envValue);
  if (!isResourceLockEnabled(config, input.resourceKind)) return;
  const expectedIso = toIsoOrNull(input.expected);
  if (expectedIso == null) return;
  const currentIso = toIsoOrNull(input.current);
  if (currentIso == null) return;
  if (currentIso === expectedIso) return;
  throw new CrudHttpError(409, buildOptimisticLockConflictBody(currentIso, expectedIso));
}
function enforceCommandOptimisticLock(input) {
  const expected = input.expected !== void 0 && input.expected !== null ? input.expected : readOptimisticLockExpected(input.request ?? null);
  assertOptimisticLock({
    resourceKind: input.resourceKind,
    resourceId: input.resourceId,
    expected,
    current: input.current,
    envValue: input.envValue
  });
}
function enforceRecordGoneIsConflict(input) {
  const config = resolveConfig(input.envValue);
  if (!isResourceLockEnabled(config, input.resourceKind)) return;
  const clientSupplied = input.expected !== void 0 && input.expected !== null ? input.expected : readOptimisticLockExpected(input.request ?? null);
  const expectedIso = toIsoOrNull(clientSupplied);
  if (expectedIso == null) return;
  throw new CrudHttpError(409, buildOptimisticLockConflictBody(expectedIso, expectedIso));
}
function createCommandOptimisticLockGuardService(options = {}) {
  const resolveExpected = options.resolveExpected ?? null;
  return {
    async enforce(input) {
      const config = resolveConfig(input.envValue);
      if (!isResourceLockEnabled(config, input.resourceKind)) return;
      const clientSupplied = input.expected !== void 0 && input.expected !== null ? input.expected : readOptimisticLockExpected(input.request ?? null);
      const expectedFromHeader = toIsoOrNull(clientSupplied);
      let expected = expectedFromHeader;
      if (resolveExpected) {
        const resolverInput = {
          expectedFromHeader,
          resourceKind: input.resourceKind,
          resourceId: input.resourceId
        };
        expected = await resolveExpected(resolverInput);
      }
      assertOptimisticLock({
        resourceKind: input.resourceKind,
        resourceId: input.resourceId,
        expected,
        current: input.current,
        envValue: input.envValue
      });
    }
  };
}
const COMMAND_OPTIMISTIC_LOCK_GUARD_SERVICE_KEY = "commandOptimisticLockGuardService";
function resolveCommandOptimisticLockGuardService(container) {
  try {
    const service = container.resolve(
      COMMAND_OPTIMISTIC_LOCK_GUARD_SERVICE_KEY
    );
    if (!service || typeof service.enforce !== "function") return null;
    return service;
  } catch {
    return null;
  }
}
async function enforceCommandOptimisticLockWithGuards(container, input) {
  enforceCommandOptimisticLock(input);
  const guard = resolveCommandOptimisticLockGuardService(container);
  if (!guard) return;
  try {
    await guard.enforce(input);
  } catch (error) {
    if (isCrudHttpError(error) && error.status === 409) throw error;
  }
}
export {
  assertOptimisticLock,
  buildOptimisticLockConflictBody,
  createCommandOptimisticLockGuardService,
  enforceCommandOptimisticLock,
  enforceCommandOptimisticLockWithGuards,
  enforceRecordGoneIsConflict,
  readOptimisticLockExpected
};
//# sourceMappingURL=optimistic-lock-command.js.map
