import { encryptCustomFieldValue, resolveTenantEncryptionService } from "@open-mercato/shared/lib/encryption/customFieldValues";
import {
  MAX_CUSTOM_FIELD_KEYS_PER_RECORD,
  TOO_MANY_CUSTOM_FIELDS_ERROR
} from "@open-mercato/shared/modules/entities/validation";
import { CustomFieldDef, CustomFieldValue } from "../data/entities.js";
function columnFromKind(kind) {
  switch (kind) {
    case "text":
    case "select":
    case "currency":
    case "dictionary":
      return "valueText";
    case "multiline":
      return "valueMultiline";
    case "integer":
      return "valueInt";
    case "float":
      return "valueFloat";
    case "boolean":
      return "valueBool";
    default:
      return "valueText";
  }
}
function columnFromJsValue(v) {
  if (v === null || v === void 0) return "valueText";
  if (typeof v === "boolean") return "valueBool";
  if (typeof v === "number") return Number.isInteger(v) ? "valueInt" : "valueFloat";
  return "valueText";
}
function clearValueColumns(cf) {
  cf.valueText = null;
  cf.valueMultiline = null;
  cf.valueInt = null;
  cf.valueFloat = null;
  cf.valueBool = null;
}
async function setRecordCustomFields(em, opts) {
  const { entityId, recordId, values } = opts;
  const organizationId = opts.organizationId ?? null;
  const tenantId = opts.tenantId ?? null;
  const preferDefs = opts.preferDefs !== false;
  let defsByKey;
  if (preferDefs) {
    const defs = await em.find(CustomFieldDef, {
      entityId,
      isActive: true,
      deletedAt: null,
      organizationId: { $in: [organizationId, null] },
      tenantId: { $in: [tenantId, null] }
    });
    const scopeScore = (def) => (def.tenantId ? 2 : 0) + (def.organizationId ? 1 : 0);
    defsByKey = {};
    for (const d of defs) {
      const existing = defsByKey[d.key];
      if (!existing) {
        defsByKey[d.key] = d;
        continue;
      }
      const nextScore = scopeScore(d);
      const existingScore = scopeScore(existing);
      if (nextScore > existingScore) {
        defsByKey[d.key] = d;
        continue;
      }
      if (nextScore < existingScore) continue;
      const nextUpdatedAt = d.updatedAt instanceof Date ? d.updatedAt.getTime() : new Date(d.updatedAt).getTime();
      const existingUpdatedAt = existing.updatedAt instanceof Date ? existing.updatedAt.getTime() : new Date(existing.updatedAt).getTime();
      if (nextUpdatedAt >= existingUpdatedAt) {
        defsByKey[d.key] = d;
      }
    }
  }
  const toPersist = [];
  let encryptionService;
  const encryptionCache = /* @__PURE__ */ new Map();
  const getEncryptionService = () => {
    if (encryptionService !== void 0) return encryptionService;
    encryptionService = resolveTenantEncryptionService(em, opts.encryptionService);
    return encryptionService;
  };
  const keys = Object.keys(values);
  const presentKeyCount = keys.filter((key) => values[key] !== void 0).length;
  if (preferDefs && presentKeyCount > MAX_CUSTOM_FIELD_KEYS_PER_RECORD) {
    throw new Error(TOO_MANY_CUSTOM_FIELDS_ERROR);
  }
  const txEm = em;
  const txCapable = typeof txEm.begin === "function" && typeof txEm.commit === "function" && typeof txEm.rollback === "function" && typeof txEm.isInTransaction === "function";
  const ownCustomFieldTransaction = txCapable && !txEm.isInTransaction();
  if (ownCustomFieldTransaction) await txEm.begin();
  try {
    for (const fieldKey of keys) {
      const raw = values[fieldKey];
      if (raw === void 0) continue;
      const def = defsByKey?.[fieldKey];
      const encrypted = Boolean(def?.configJson && def.configJson?.encrypted);
      const isArray = Array.isArray(raw);
      if (isArray) {
        const arr = raw;
        await em.nativeDelete(CustomFieldValue, { entityId, recordId, organizationId, tenantId, fieldKey });
        for (const val of arr) {
          const col = encrypted ? "valueText" : def ? columnFromKind(def.kind) : columnFromJsValue(val);
          const cf2 = em.create(CustomFieldValue, { entityId, recordId, organizationId, tenantId, fieldKey, createdAt: /* @__PURE__ */ new Date() });
          clearValueColumns(cf2);
          const stored = encrypted ? await encryptCustomFieldValue(val, tenantId, getEncryptionService(), encryptionCache) : val;
          switch (col) {
            case "valueText":
              cf2.valueText = stored == null ? null : String(stored);
              break;
            case "valueMultiline":
              cf2.valueMultiline = stored == null ? null : String(stored);
              break;
            case "valueInt":
              cf2.valueInt = stored == null ? null : Number(stored);
              break;
            case "valueFloat":
              cf2.valueFloat = stored == null ? null : Number(stored);
              break;
            case "valueBool":
              cf2.valueBool = stored == null ? null : Boolean(stored);
              break;
            default:
              cf2.valueText = stored == null ? null : String(stored);
              break;
          }
          toPersist.push(cf2);
        }
        continue;
      }
      const column = encrypted ? "valueText" : def ? columnFromKind(def.kind) : columnFromJsValue(raw);
      const storedValue = encrypted ? await encryptCustomFieldValue(raw, tenantId, getEncryptionService(), encryptionCache) : raw;
      let cf = await em.findOne(CustomFieldValue, { entityId, recordId, organizationId, tenantId, fieldKey });
      if (!cf) {
        cf = em.create(CustomFieldValue, { entityId, recordId, organizationId, tenantId, fieldKey, createdAt: /* @__PURE__ */ new Date() });
        toPersist.push(cf);
      }
      clearValueColumns(cf);
      switch (column) {
        case "valueText":
          cf.valueText = storedValue == null ? null : String(storedValue);
          break;
        case "valueMultiline":
          cf.valueMultiline = storedValue == null ? null : String(storedValue);
          break;
        case "valueInt":
          cf.valueInt = storedValue == null ? null : Number(storedValue);
          break;
        case "valueFloat":
          cf.valueFloat = storedValue == null ? null : Number(storedValue);
          break;
        case "valueBool":
          cf.valueBool = storedValue == null ? null : Boolean(storedValue);
          break;
        default:
          cf.valueText = storedValue == null ? null : String(storedValue);
          break;
      }
    }
    if (toPersist.length) em.persist(toPersist);
    await em.flush();
    if (ownCustomFieldTransaction) await txEm.commit();
  } catch (err) {
    if (ownCustomFieldTransaction) {
      try {
        await txEm.rollback();
      } catch {
      }
    }
    throw err;
  }
  try {
    if (typeof opts.onChanged === "function") {
      await opts.onChanged({ entityId, recordId, organizationId, tenantId });
    }
  } catch {
  }
}
export {
  setRecordCustomFields
};
//# sourceMappingURL=helpers.js.map
