"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PropertyMapper = void 0;
exports.mapEntityToStore = mapEntityToStore;
exports.mapStoreToEntity = mapStoreToEntity;
const constants_1 = require("../../discussions/constants");
const utils_1 = require("../../discussions/utils");
const deep_set_1 = require("../../objects/deep-set");
const get_prop_1 = require("../../objects/get-prop");
const set_prop_1 = require("../../objects/set-prop");
const util_1 = require("../../util");
/**
 * @private
 * Manage forward and backward property mappings to
 * streamline conversion between the Hub Entities, and
 * the backing Store objects.
 */
class PropertyMapper {
    /**
     * Pass in the mappings between the Entity and
     * it's backing structure (model or otherwise)
     * @param mappings
     */
    constructor(mappings) {
        this.mappings = mappings;
    }
    /**
     * Map properties from a Store object, on to an Entity object.
     *
     * Used when constructing an Entity from a fetched Store object,
     * in which case the Entity should be an empty object (`{}`).
     *
     * Can also be used to apply changes to an Entity from a Store,
     * in which case an existing Entity can be passed in.
     * @param store
     * @param entity
     * @returns
     */
    storeToEntity(store, entity) {
        const obj = mapStoreToEntity(store, entity, this.mappings);
        // ------------------------------
        // Additional Read-Only Model Level Property Mappings
        // ------------------------------
        // Since they are not in the property map, setting these values on
        // the entity does not attempt to send that back upstream to the store
        if ((0, get_prop_1.getProp)(store, "item")) {
            const itm = (0, get_prop_1.getProp)(store, "item");
            // use setProp to side-step typechecking
            (0, set_prop_1.setProp)("canEdit", ["admin", "update"].includes(itm.itemControl), obj);
            (0, set_prop_1.setProp)("canDelete", itm.itemControl === "admin", obj);
            (0, set_prop_1.setProp)("canRecycle", itm.canRecycle || false, obj);
            (0, set_prop_1.setProp)("protected", itm.protected, obj);
        }
        return obj;
    }
    /**
     * Map properties from an entity object onto a model.
     *
     * Typically the model will already exist, and this
     * method is used to transfer changes to the model
     * prior to storage.
     * @param entity
     * @param store
     * @returns
     */
    entityToStore(entity, store) {
        return mapEntityToStore(entity, store, this.mappings);
    }
}
exports.PropertyMapper = PropertyMapper;
/**
 * Generic function to apply properties from an Object
 * (i.e. IHubProject) onto a Model that can be persisted to an Item
 * @param entity
 * @param store
 * @param mappings
 * @returns
 */
function mapEntityToStore(entity, store, mappings) {
    if ((0, get_prop_1.getProp)(store, "item")) {
        const item = (0, get_prop_1.getProp)(store, "item");
        if (item.typeKeywords) {
            item.typeKeywords = (0, get_prop_1.getProp)(entity, "isDiscussable")
                ? item.typeKeywords.filter((typeKeyword) => typeKeyword !== constants_1.CANNOT_DISCUSS)
                : [...item.typeKeywords, constants_1.CANNOT_DISCUSS];
            (0, set_prop_1.setProp)("item.typeKeywords", item.typeKeywords, store);
        }
    }
    return mapProps(entity, "entityKey", store, "storeKey", mappings);
}
/**
 * Generic function to apply properties from a Model
 * onto an Object (i.e. IHubProject etc)
 * @param store
 * @param entity
 * @param mappings
 * @returns
 */
function mapStoreToEntity(store, entity, mappings) {
    // Item specific logic...
    if ((0, get_prop_1.getProp)(store, "item")) {
        const item = (0, get_prop_1.getProp)(store, "item");
        (0, set_prop_1.setProp)("isDiscussable", (0, utils_1.isDiscussable)(item), entity);
    }
    return mapProps(store, "storeKey", entity, "entityKey", mappings);
}
/**
 * Internal function to map between objects
 * @param source
 * @param sourceKey
 * @param target
 * @param targetKey
 * @param mappings
 * @returns
 */
function mapProps(source, sourceKey, target, targetKey, mappings) {
    // clone the target
    const clone = (0, util_1.cloneObject)(target);
    // walk the property map array
    mappings.forEach((entry) => {
        // Verbose b/c typescript hates the use of property indexing with generics
        // i.e. entry<T>[sourceKey] makes typescript angry
        const sourcePath = (0, get_prop_1.getProp)(entry, sourceKey);
        const targetPath = (0, get_prop_1.getProp)(entry, targetKey);
        // get the value from the source
        const sourceVal = (0, get_prop_1.getProp)(source, sourcePath);
        // if it's not null or undefined
        if (sourceVal !== null && sourceVal !== undefined) {
            // set it
            (0, deep_set_1.deepSet)(clone, targetPath, sourceVal, true);
        }
    });
    return clone;
}
//# sourceMappingURL=PropertyMapper.js.map