"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getValidationErrors = void 0;
var ramda_1 = require("ramda");
var ui_i18n_1 = __importDefault(require("ui-i18n"));
var mdm_sdk_1 = require("@reltio/mdm-sdk");
var ALLOWED_CHANGE_TYPES = [
    mdm_sdk_1.DCRTypes.UPDATE_ATTRIBUTE,
    mdm_sdk_1.DCRTypes.INSERT_ATTRIBUTE,
    mdm_sdk_1.DCRTypes.DELETE_ATTRIBUTE,
    mdm_sdk_1.DCRTypes.IGNORE_ATTRIBUTE
];
var getValidationErrors = function (_a) {
    var changes = _a.changes, userChanges = _a.userChanges, entitiesOrRelations = _a.entitiesOrRelations, metadata = _a.metadata;
    var _b = validateTopLevelAttributes({
        changes: changes,
        userChanges: userChanges,
        entitiesOrRelations: entitiesOrRelations,
        metadata: metadata
    }), topLevelErrors = _b.errors, topRejectedChangeIds = _b.topRejectedChangeIds;
    var complexErrors = validateComplexAttributes({ changes: changes, userChanges: userChanges, metadata: metadata, topRejectedChangeIds: topRejectedChangeIds });
    return __spreadArray(__spreadArray([], topLevelErrors, true), complexErrors, true);
};
exports.getValidationErrors = getValidationErrors;
var validateTopLevelAttributes = function (_a) {
    var changes = _a.changes, userChanges = _a.userChanges, entitiesOrRelations = _a.entitiesOrRelations, metadata = _a.metadata;
    var errors = [];
    var topRejectedChangeIds = [];
    var _loop_1 = function (entityUri) {
        var entityOrRelation = entitiesOrRelations[entityUri];
        var objectChanges = changes[entityUri] || [];
        var requiredAttributeUris = getRequiredAttributeUris(metadata, entityOrRelation.type);
        var changesByAttributePath = {};
        objectChanges.forEach(function (change) {
            var _a;
            var attributeTypeUri = change.refAttributeType || change.attributeType;
            if (!change.type ||
                !ALLOWED_CHANGE_TYPES.includes(change.type) ||
                !requiredAttributeUris.includes(change.attributeType || '')) {
                return;
            }
            var foundUserChanges = getTopLevelUserChangesByChangeId(userChanges, change.id);
            foundUserChanges.forEach(function (userChange) {
                if (userChange.data.isRejected) {
                    topRejectedChangeIds.push(change.id);
                }
            });
            var hasUserChanges = foundUserChanges.length > 0;
            var adjustedChange = adjustChangeWithUserChanges(change, foundUserChanges);
            var currentAttributePath = getCurrentAttributePath(change.attributePath);
            if (!changesByAttributePath[currentAttributePath]) {
                var objectValues = (0, mdm_sdk_1.findAttributeValuesByTypeUri)(metadata, entityOrRelation, attributeTypeUri);
                changesByAttributePath[currentAttributePath] = {
                    changes: [],
                    objectValues: objectValues,
                    attributeTypeUri: attributeTypeUri,
                    attributeType: (0, mdm_sdk_1.findAttributeTypeByUri)(metadata, attributeTypeUri),
                    parentUri: (0, mdm_sdk_1.getParentUri)(change.attributePath),
                    lineIds: []
                };
            }
            if (adjustedChange) {
                changesByAttributePath[currentAttributePath].changes.push(adjustedChange);
            }
            if (hasUserChanges) {
                changesByAttributePath[currentAttributePath].hasUserChanges = true;
            }
            (_a = changesByAttributePath[currentAttributePath].lineIds).push.apply(_a, foundUserChanges.map(function (userChange) { return userChange.lineId; }));
        });
        Object.values(changesByAttributePath).forEach(function (_a) {
            var _b;
            var hasUserChanges = _a.hasUserChanges, objectValues = _a.objectValues, attributeChanges = _a.changes, attributeTypeUri = _a.attributeTypeUri, attributeType = _a.attributeType, parentUri = _a.parentUri, lineIds = _a.lineIds;
            if (hasUserChanges && !hasAttributeValue(objectValues, attributeChanges, entityUri)) {
                var valueUri = (_b = objectValues === null || objectValues === void 0 ? void 0 : objectValues[0]) === null || _b === void 0 ? void 0 : _b.uri;
                errors.push(createRequiredError({
                    attributeType: attributeType,
                    attributeTypeUri: attributeTypeUri,
                    parentUri: parentUri,
                    lineIds: lineIds,
                    metadata: metadata,
                    entityUri: entityUri,
                    entitiesMap: entitiesOrRelations,
                    valueUri: valueUri
                }));
            }
        });
    };
    for (var entityUri in entitiesOrRelations) {
        _loop_1(entityUri);
    }
    return { errors: errors, topRejectedChangeIds: topRejectedChangeIds };
};
var validateComplexAttributes = function (_a) {
    var changes = _a.changes, userChanges = _a.userChanges, metadata = _a.metadata, _b = _a.topRejectedChangeIds, topRejectedChangeIds = _b === void 0 ? [] : _b;
    var errors = [];
    var _loop_2 = function (entityUri) {
        var objectChanges = changes[entityUri] || [];
        objectChanges.forEach(function (change) {
            var _a, _b, _c, _d;
            if (topRejectedChangeIds.includes(change.id)) {
                return;
            }
            var hasComplexValue = (typeof ((_a = change.newValue) === null || _a === void 0 ? void 0 : _a.value) === 'object' &&
                !Array.isArray((_b = change.newValue) === null || _b === void 0 ? void 0 : _b.value)) ||
                (typeof ((_c = change.oldValue) === null || _c === void 0 ? void 0 : _c.value) === 'object' &&
                    !Array.isArray((_d = change.oldValue) === null || _d === void 0 ? void 0 : _d.value));
            var isCreateType = change.type === mdm_sdk_1.DCRTypes.CREATE_ENTITY || change.type === mdm_sdk_1.DCRTypes.CREATE_RELATIONSHIP;
            if (!hasComplexValue && !isCreateType) {
                return;
            }
            var foundUserChanges = getUserChangesByChangeId(userChanges, change.id);
            var complexErrors = validateRequiredComplexAttributes({
                metadata: metadata,
                change: change,
                userChanges: foundUserChanges,
                entityUri: entityUri
            });
            errors.push.apply(errors, complexErrors);
        });
    };
    for (var entityUri in changes) {
        _loop_2(entityUri);
    }
    return errors;
};
var getTopLevelUserChangesByChangeId = function (userChanges, changeId) {
    return getUserChangesByChangeId(userChanges, changeId).filter(function (userChange) { return userChange.lineId.split('/').length === 3; });
};
var getUserChangesByChangeId = function (userChanges, changeId) {
    return userChanges.filter(function (userChange) { return userChange.lineId.split('/')[1] === changeId; });
};
var getIsChangeRejected = function (userChanges, changeType) {
    if (userChanges.length === 0)
        return false;
    var isInsertType = changeType === mdm_sdk_1.DCRTypes.INSERT_ATTRIBUTE;
    return userChanges.some(function (userChange) { var _a; return userChange.data.isRejected || (isInsertType && (0, mdm_sdk_1.isEmptyValue)((_a = userChange.data.value) === null || _a === void 0 ? void 0 : _a.value)); });
};
var adjustChangeWithUserChanges = function (change, userChanges) {
    var _a;
    if (getIsChangeRejected(userChanges, change.type)) {
        return null;
    }
    if (change.type === mdm_sdk_1.DCRTypes.UPDATE_ATTRIBUTE && userChanges.length > 0) {
        if ((0, mdm_sdk_1.isEmptyValue)((_a = userChanges[0].data.value) === null || _a === void 0 ? void 0 : _a.value)) {
            return __assign(__assign({}, change), { type: mdm_sdk_1.DCRTypes.DELETE_ATTRIBUTE });
        }
    }
    return change;
};
var getCurrentAttributePath = function (attributePath) {
    var parts = attributePath.split('/');
    return parts.slice(0, parts.length % 2 === 0 ? parts.length - 1 : parts.length).join('/');
};
var getEntityValueUri = function (entityOrRelationTypeUri, change) {
    if (change.refObjectURI) {
        var relationId = change.refObjectURI.split('/').pop();
        var refAttributeTypeParts = change.refAttributeType.split('/attributes/');
        return "".concat(entityOrRelationTypeUri, "/attributes/").concat(refAttributeTypeParts[1], "/").concat(relationId, "/").concat(change.attributePath);
    }
    return "".concat(entityOrRelationTypeUri, "/attributes/").concat(change.attributePath);
};
var hasAttributeValue = function (objectValues, changes, entityUri) {
    var values = (0, ramda_1.clone)(objectValues);
    var _loop_3 = function (change) {
        if (change.type === mdm_sdk_1.DCRTypes.UPDATE_ATTRIBUTE || change.type === mdm_sdk_1.DCRTypes.INSERT_ATTRIBUTE) {
            return { value: true };
        }
        var valueUri = getEntityValueUri(entityUri, change);
        var valueIndex = values.findIndex(function (value) { return value.uri === valueUri; });
        if (change.type === mdm_sdk_1.DCRTypes.DELETE_ATTRIBUTE) {
            if (valueIndex !== -1) {
                values.splice(valueIndex, 1);
            }
        }
        else if (change.type === mdm_sdk_1.DCRTypes.IGNORE_ATTRIBUTE && valueIndex !== -1) {
            values[valueIndex].ignored = change.newPinOrIgnoreValue;
        }
    };
    for (var _i = 0, changes_1 = changes; _i < changes_1.length; _i++) {
        var change = changes_1[_i];
        var state_1 = _loop_3(change);
        if (typeof state_1 === "object")
            return state_1.value;
    }
    return values.filter(mdm_sdk_1.isOv).filter(function (value) { return !(0, mdm_sdk_1.isIgnored)(value); }).length > 0;
};
var getEntityTypeUriFromAttributeUri = function (attributeUri) {
    return attributeUri.split('/').slice(0, 3).join('/');
};
var getAttributeTypeUriFromLineId = function (attributeTypeUri, lineId) {
    var EXCLUDED_PARTS = new Set(['value', 'newValue', 'oldValue', 'attributes']);
    var lineParts = lineId
        .split('/')
        .slice(2)
        .filter(function (part) {
        if (!isNaN(Number(part)))
            return false;
        return !EXCLUDED_PARTS.has(part);
    });
    return __spreadArray([attributeTypeUri], lineParts, true).join('/attributes/');
};
var getEntityTypeUriFromChange = function (change) {
    if (change.type === mdm_sdk_1.DCRTypes.CREATE_ENTITY || change.type === mdm_sdk_1.DCRTypes.CREATE_RELATIONSHIP) {
        return change.objectType || '';
    }
    return getEntityTypeUriFromAttributeUri(change.attributeType || change.objectType || '');
};
var getRequiredAttributeUris = function (metadata, typeUri) {
    var requiredAttrTypes = (0, mdm_sdk_1.getRequiredAttributeTypesByTypeUri)(metadata, typeUri, true) || [];
    return requiredAttrTypes.map(function (attr) { return attr.uri; });
};
var createRequiredError = function (_a) {
    var attributeType = _a.attributeType, attributeTypeUri = _a.attributeTypeUri, parentUri = _a.parentUri, lineIds = _a.lineIds, metadata = _a.metadata, entityUri = _a.entityUri, entitiesMap = _a.entitiesMap, valueUri = _a.valueUri;
    var error = (0, mdm_sdk_1.buildError)({
        type: mdm_sdk_1.ErrorType.missed,
        attributeType: attributeType,
        attributeTypeUri: attributeTypeUri,
        parentUri: parentUri,
        message: ui_i18n_1.default.text('Attribute is required'),
        source: mdm_sdk_1.ErrorSource.local,
        uri: lineIds[0]
    });
    var path = [];
    if ((entitiesMap === null || entitiesMap === void 0 ? void 0 : entitiesMap[entityUri]) && valueUri) {
        path = (0, mdm_sdk_1.evaluateErrorPath)({
            entityUri: entityUri,
            metadata: metadata,
            entitiesMap: entitiesMap
        }, valueUri);
    }
    return {
        error: __assign(__assign({}, error), { path: path }),
        lineIds: lineIds
    };
};
var validateRequiredComplexAttributes = function (_a) {
    var _b;
    var metadata = _a.metadata, change = _a.change, userChanges = _a.userChanges, entityUri = _a.entityUri;
    if (userChanges.length === 0 || change.type === mdm_sdk_1.DCRTypes.DELETE_ATTRIBUTE) {
        return [];
    }
    var valueHolder = change.type === mdm_sdk_1.DCRTypes.CREATE_ENTITY || change.type === mdm_sdk_1.DCRTypes.CREATE_RELATIONSHIP ? 'attributes' : 'value';
    var rootAttributeTypeUri = change.refAttributeType || change.attributeType || change.objectType;
    var attributeType = (0, mdm_sdk_1.findAttributeTypeByUri)(metadata, rootAttributeTypeUri);
    var isReferenceType = (0, mdm_sdk_1.isReference)(attributeType);
    var entityTypeUri = isReferenceType ? attributeType.relationshipTypeURI : getEntityTypeUriFromChange(change);
    var requiredAttributeUris = getRequiredAttributeUris(metadata, entityTypeUri);
    var calculateLineIdFromPath = function (path) {
        if (path === void 0) { path = ''; }
        var entityId = entityUri.split('/').pop() || '';
        return "".concat(entityId, "/").concat(change.id, "/newValue").concat(path ? "/".concat(path) : '');
    };
    var checkIsChangeRejected = function (path) {
        var _a, _b, _c;
        var searchLineId = calculateLineIdFromPath(path);
        var lineUserChanges = userChanges.find(function (userChange) { return userChange.lineId === searchLineId; });
        return (((_a = lineUserChanges === null || lineUserChanges === void 0 ? void 0 : lineUserChanges.data) === null || _a === void 0 ? void 0 : _a.isRejected) || (lineUserChanges && (0, mdm_sdk_1.isEmptyValue)((_c = (_b = lineUserChanges === null || lineUserChanges === void 0 ? void 0 : lineUserChanges.data) === null || _b === void 0 ? void 0 : _b.value) === null || _c === void 0 ? void 0 : _c.value)));
    };
    var collectErrors = function (attributes) {
        var errors = [];
        var visitValue = function (value, path) {
            var isRejected = checkIsChangeRejected(path.slice(0, -1).join('/'));
            if (typeof value === 'string') {
                return !isRejected;
            }
            if (isRejected) {
                return false;
            }
            var anyFieldHasValue = false;
            Object.entries(value).forEach(function (_a) {
                var fieldName = _a[0], subAttributes = _a[1];
                var anyChildHasValue = false;
                var attributePath = __spreadArray(__spreadArray([], path, true), [fieldName], false);
                var rejectedLineIds = [];
                subAttributes.forEach(function (subAttribute, index) {
                    var childPath = __spreadArray(__spreadArray([], attributePath, true), [index, 'value'], false);
                    var childHasValue = visitValue(subAttribute.value, childPath);
                    if (!childHasValue) {
                        rejectedLineIds.push(calculateLineIdFromPath(childPath.slice(0, -1).join('/')));
                    }
                    anyChildHasValue = anyChildHasValue || childHasValue;
                });
                anyFieldHasValue = anyFieldHasValue || anyChildHasValue;
                if (!anyChildHasValue) {
                    var attributeTypeUri = getAttributeTypeUriFromLineId(isReferenceType ? entityTypeUri : rootAttributeTypeUri, rejectedLineIds[0]);
                    var isRequired = requiredAttributeUris.includes(attributeTypeUri);
                    if (isRequired) {
                        errors.push(createRequiredError({
                            attributeType: (0, mdm_sdk_1.findAttributeTypeByUri)(metadata, attributeTypeUri),
                            attributeTypeUri: attributeTypeUri,
                            lineIds: rejectedLineIds
                        }));
                    }
                }
            });
            return anyFieldHasValue;
        };
        var hasValue = visitValue(attributes, [valueHolder]);
        if (!hasValue && attributeType.required) {
            errors.push(createRequiredError({
                attributeType: attributeType,
                attributeTypeUri: attributeType.uri,
                lineIds: [calculateLineIdFromPath()]
            }));
        }
        return errors;
    };
    return collectErrors((_b = change.newValue) === null || _b === void 0 ? void 0 : _b[valueHolder]);
};
