/**
 * Translates to a readable values
 * @param {Object} instance - The model or context instance
 * @param {Object} changes - Object containing changes
 * @param {Object} options - Object containing transaction
 */
exports.translateValues = async(instance, changes, options = {}) => {
    const models = instance.app.models;
    function getRelation(instance, property) {
        const relations = instance.definition.settings.relations;
        for (let relationName in relations) {
            const relation = relations[relationName];
            if (relation.foreignKey == property)
                return relation;
        }

        return;
    }

    function getValue(rawData) {
        const row = JSON.parse(JSON.stringify(rawData));
        for (column in row)
            return row[column];
    }

    function formatDate(date) {
        return new Intl.DateTimeFormat('es', {
            year: 'numeric',
            month: 'numeric',
            day: 'numeric',
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit'
        }).format(date);
    }

    if (changes instanceof instance)
        changes = changes.__data;

    const properties = Object.assign({}, changes);
    for (let property in properties) {
        const firstChar = property.substring(0, 1);
        const isPrivate = firstChar == '$';
        if (isPrivate) {
            delete properties[property];
            continue;
        }

        const relation = getRelation(instance, property);
        const value = properties[property];
        const hasValue = value != null && value != undefined;

        let finalValue = value;
        if (relation && hasValue) {
            let fieldsToShow = ['nickname', 'name', 'code', 'description'];
            const modelName = relation.model;
            const model = models[modelName];
            const log = model.definition.settings.log;

            if (log && log.showField)
                fieldsToShow = [log.showField];

            const row = await model.findById(value, {
                fields: fieldsToShow
            }, options);
            const newValue = getValue(row);
            if (newValue) finalValue = newValue;
        }

        if (finalValue instanceof Date)
            finalValue = formatDate(finalValue);

        properties[property] = finalValue;
    }

    return properties;
};

/**
 * Returns the changes between two objects
 * @param {Object} original - Original object
 * @param {Object} changes - New changes
 * @return {Object} Old and new values
 */
exports.getChanges = (original, changes) => {
    const oldChanges = {};
    const newChanges = {};
    const dateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;

    for (let property in changes) {
        if (dateRegex.test(original[property]))
            original[property] = new Date(Date.parse(original[property]));

        const firstChar = property.substring(0, 1);
        const isPrivate = firstChar == '$';
        if (isPrivate) return;

        const hasChanges = original[property] instanceof Date ?
            changes[property]?.getTime() != original[property]?.getTime() :
            changes[property] != original[property];

        if (hasChanges) {
            newChanges[property] = changes[property];

            if (original[property] != undefined)
                oldChanges[property] = original[property];
        }
    }

    return {
        old: oldChanges,
        new: newChanges
    };
};