/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { camelize, ImplementationError, isObject } from "#general";
import { DataModelPath, Metatype } from "#model";
import { SchemaImplementationError, WriteError } from "../../../errors.js";
function ValuePatcher(schema, owner) {
  switch (schema.effectiveMetatype) {
    // "any" means the schema defines no type.  Assume it's an object since ValuePatcher is only invoked where
    // an object is expected naturally
    case Metatype.any:
    case Metatype.object:
      return StructPatcher(schema, owner);
    case Metatype.array:
      return ListPatcher(schema, owner);
    default:
      return PrimitivePatcher();
  }
}
const defaultsCache = /* @__PURE__ */ new WeakMap();
function getDefaults(schema) {
  if (defaultsCache.has(schema)) {
    return defaultsCache.get(schema);
  }
  const defaults = {};
  for (const member of schema.activeMembers) {
    if (member.default !== void 0) {
      defaults[camelize(member.name)] = member.default;
      continue;
    }
    if (member.mandatory && member.nullable) {
      defaults[camelize(member.name)] = null;
      continue;
    }
  }
  defaultsCache.set(schema, defaults);
  return defaults;
}
function StructPatcher(schema, owner) {
  const memberPatchers = {};
  const memberDefaults = {};
  const memberArrays = {};
  for (const member of schema.activeMembers) {
    const metatype = member.effectiveMetatype;
    let handler;
    if (metatype === Metatype.object || metatype === Metatype.array) {
      handler = owner.get(member).patch;
    }
    const key = camelize(member.name);
    memberPatchers[key] = handler;
    if (metatype === Metatype.object) {
      memberDefaults[key] = getDefaults(member);
    }
    if (metatype === Metatype.array) {
      memberArrays[key] = true;
    }
  }
  return (changes, target, path) => {
    if (typeof changes !== "object" || changes === null || Array.isArray(changes)) {
      throw new WriteError(path, `patch definition ${changes} is not an object`);
    }
    if (typeof target !== "object" || target === null || Array.isArray(target)) {
      throw new WriteError(path, `cannot patch ${target} because it is not an object`);
    }
    for (const key in changes) {
      if (!(key in memberPatchers)) {
        throw new WriteError(path, `${key} is not a property of ${schema.name}`);
      }
      let newValue = changes[key];
      const subpatch = memberPatchers[key];
      if (!subpatch || newValue === null || typeof newValue !== "object") {
        target[key] = newValue;
        continue;
      }
      if (memberArrays[key]) {
        if (target[key] === void 0 || target[key] === null) {
          newValue = subpatch(newValue, [], path.at(key));
          target[key] = newValue;
          continue;
        }
      }
      if (target[key] === void 0 || target[key] === null) {
        newValue = subpatch(newValue, { ...memberDefaults[key] }, path.at(key));
        target[key] = newValue;
        continue;
      }
      subpatch(newValue, target[key], path.at(key));
    }
    return target;
  };
}
function ListPatcher(schema, owner) {
  const entry = schema.listEntry;
  if (entry === void 0) {
    throw new SchemaImplementationError(DataModelPath(schema.path), "List schema has no entry definition");
  }
  const entryMetatype = entry?.effectiveMetatype;
  let patchEntry;
  if (entryMetatype === Metatype.object || entryMetatype === Metatype.array) {
    patchEntry = owner.get(entry).patch;
  }
  let entryDefaults;
  if (entryMetatype === Metatype.object) {
    entryDefaults = getDefaults(entry);
  }
  return (changes, target, path) => {
    if (typeof changes !== "object" || changes === null) {
      throw new WriteError(path, `patch definition ${changes} is not an object`);
    }
    if (!Array.isArray(target)) {
      throw new WriteError(path, `patch definition ${changes} is not an object`);
    }
    for (const indexStr in changes) {
      const index = Number.parseInt(indexStr);
      if (index < 0 || Number.isNaN(index)) {
        throw new WriteError(path, `key ${index} is not a valid array index`);
      }
      let newValue = changes[indexStr];
      if (patchEntry) {
        const oldValue = index < target.length ? target[index] : void 0;
        if (newValue === void 0 || newValue === null || oldValue === void 0 || oldValue === null) {
          if (entryDefaults && isObject(newValue)) {
            newValue = patchEntry(newValue, { ...entryDefaults }, path.at(index));
          }
          target[index] = newValue;
        } else {
          patchEntry(newValue, target[index], path.at(index));
        }
      } else {
        target[index] = newValue;
      }
    }
    return target;
  };
}
function PrimitivePatcher() {
  return (_changes, _target, path) => {
    throw new ImplementationError(`Cannot generate patch ${path} because it does not define not a collection`);
  };
}
export {
  ValuePatcher
};
//# sourceMappingURL=ValuePatcher.js.map
