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);
};
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import i18n from 'ui-i18n';
import classnames from 'classnames';
import { always, assoc, concat, differenceWith, equals, forEach, map, path, pick, pipe, prop, propEq, when, uniq, curry, values, flatten, any } from 'ramda';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import { createTemporaryEntity, Directions, findRelatedTemporaryEntities, getActivenessAttributes, getCreatableAttributeTypes, getEntityType, getErrorMessage, getErrorId, getRelationType, getSuitableEntityTypeUrisForRelationTypes, isAvailableRelationBetweenEntities, omitMarks, relationEditorAttributeTypesSelectionStrategy, validateConnection, updateConnectionEntityLabel, isTempUri, isEmptyValue, getRelationAttributesList, getDefaultRelationTypeObject, getInOutRelations, isSuggestMode, isAttributeTypeError, getRelationUriFromConnection, getRelationEditorActiveError, getRelationAttributesFromConnection, getEntityUriFromConnection, evaluateErrorPath, AttributeValuePathItemType, getAllRelationEditorActiveErrors, isAttributeHasMaskedValue } from '@reltio/mdm-sdk';
import { ConnectionRelationTypeSelector } from '../ConnectionRelationTypeSelector';
import { ErrorWrapper } from '../ErrorWrapper';
import { EntitySelector } from '../EntitySelector';
import { EditModeAttributesList } from '../EditModeAttributesList';
import { withContext } from '../HOCs/withContext';
import { ConfigPermissionsContextProvider } from '../contexts/ConfigPermissionsContext';
import { ScrollToElementContext, isHighlightedErrorType } from '../contexts/ScrollToElementContext';
import { useMdmAction, useMdmAuthoringItemsByEntityType, useMdmEntity, useMdmGlobalSearchRequestOptions, useMdmInitialConnection, useMdmInitialConnectionTempEntity, useMdmModifiedEntities, useMdmModifiedEntity, useMdmProfileErrors, useMdmEntityUri, useMdmShowMasking } from '../contexts/MdmModuleContext';
import { HiddenAttributesContext } from '../contexts/HiddenAttributesContext';
import { AddressAutoCompleteProvider } from '../contexts/AutoCompleteContext';
import { useScrollToAttributeError } from '../hooks/useScrollToAttributeError';
import { useStyles } from './styles';
var areRelatedEntitiesChanged = function (initial, actual) {
    return initial.length !== actual.length || initial.some(function (_, i) { return initial[i] !== actual[i]; });
};
var emptyHiddenAttributes = {};
var RelationEditor = function (_a) {
    var _b;
    var _c, _d;
    var connection = _a.connection, config = _a.config, mode = _a.mode, metadata = _a.metadata, highlightedError = _a.highlightedError;
    var styles = useStyles();
    var _e = connection.relation, relationType = _e.relationType, direction = _e.direction;
    var isNew = connection.isNew;
    var entityUri = getEntityUriFromConnection(connection);
    var relationUri = getRelationUriFromConnection(connection);
    var isTempEntity = isTempUri(entityUri);
    var mdmSetInitialInfo = useMdmAction('setInitialInfo');
    var setRelationType = useMdmAction('setRelationType');
    var closeRelationEditor = useMdmAction('closeRelationEditor');
    var entityCreated = useMdmAction('entityCreated');
    var entityDeleted = useMdmAction('entityDeleted');
    var setRelationEntity = useMdmAction('setRelationEntity');
    var errorDeactivated = useMdmAction('errorDeactivated');
    var errorsSet = useMdmAction('errorsSet');
    var addAttributes = useMdmAction('addAttributes');
    var modifyAttribute = useMdmAction('modifyAttribute');
    var removeAttribute = useMdmAction('removeAttribute');
    var addRelation = useMdmAction('addRelation');
    var editRelation = useMdmAction('editRelation');
    var mainEntity = useMdmEntity();
    var initialConnection = useMdmInitialConnection(config.id, relationUri);
    var initialModifiedEntity = useMdmInitialConnectionTempEntity(config.id, relationUri);
    var mdmModifiedEntity = useMdmModifiedEntity(entityUri);
    var mdmAllModifiedEntities = useMdmModifiedEntities();
    var globalSearchRequestOptions = useMdmGlobalSearchRequestOptions(['ovOnly']);
    var profileErrors = useMdmProfileErrors();
    var authoringItems = useMdmAuthoringItemsByEntityType(mainEntity === null || mainEntity === void 0 ? void 0 : mainEntity.type);
    var mainEntityUri = useMdmEntityUri();
    var showMasking = useMdmShowMasking();
    var error = getRelationEditorActiveError(connection, profileErrors);
    var allRelationEditorErrors = getAllRelationEditorActiveErrors(connection, profileErrors);
    var modifiedEntity = isTempEntity ? mdmModifiedEntity : null;
    var allModifiedEntities = isTempEntity ? mdmAllModifiedEntities : null;
    var excludeUris = useMemo(function () { return config.excludeUris || []; }, [config.excludeUris]);
    var _f = useScrollToAttributeError({ highlightedError: highlightedError, isSimple: true }), ref = _f.ref, errorClassName = _f.errorClassName;
    var _g = useMemo(function () {
        return getInOutRelations({
            content: connection.parentEntityUri
                ? config.contentSecondLevel
                : config.content,
            direction: direction,
            relationType: relationType,
            mode: mode,
            metadata: metadata
        });
    }, [config, direction, relationType, mode, metadata, connection.parentEntityUri]), _h = _g.inRelations, inRelationTypes = _h === void 0 ? [] : _h, _j = _g.outRelations, outRelationTypes = _j === void 0 ? [] : _j;
    var firstLevelRelationAttrTypes = useMemo(function () { return getRelationAttributesList(metadata, relationType).filter(function (_a) {
        var uri = _a.uri;
        return !excludeUris.includes(uri);
    }); }, [relationType, metadata, excludeUris]);
    var attributeListEntity = useMemo(function () { return (__assign({ attributes: __assign({}, getRelationAttributesFromConnection(connection)) }, getActivenessAttributes(connection.relation))); }, [connection]);
    var relatedTempEntities = useMemo(function () { return findRelatedTemporaryEntities(allModifiedEntities, modifiedEntity); }, [allModifiedEntities, modifiedEntity]);
    var relatedTempEntitiesUrisHistory = useRef([]);
    var setInitialInfo = useCallback(function (entity) {
        mdmSetInitialInfo(config.id, relationUri, entity);
    }, [mdmSetInitialInfo]);
    useEffect(function () {
        if (modifiedEntity && !initialModifiedEntity) {
            setInitialInfo(modifiedEntity);
        }
    }, []);
    useEffect(function () {
        relatedTempEntitiesUrisHistory.current = pipe(concat(relatedTempEntities.map(prop('uri'))), uniq)(relatedTempEntitiesUrisHistory.current);
    }, [relatedTempEntities]);
    var initialRelatedTempEntities = useRef(relatedTempEntities);
    var defaultRelationType = getDefaultRelationTypeObject(inRelationTypes, outRelationTypes);
    var initialConnectionChangeablePart = pick(['entity', 'relation'], initialConnection || {});
    var connectionChangeablePart = pick(['entity', 'relation'], connection);
    var hasChanges = !isEmptyValue(path(['entity'], connection)) &&
        (!equals(initialConnectionChangeablePart, connectionChangeablePart) ||
            !equals(initialModifiedEntity, modifiedEntity) ||
            areRelatedEntitiesChanged(initialRelatedTempEntities.current, relatedTempEntities));
    var applyAction = useMemo(function () { return curry(isNew ? addRelation : editRelation); }, [isNew, addRelation, editRelation]);
    var canCreateNewEntity = !isSuggestMode(mode) || !isTempUri(mainEntity === null || mainEntity === void 0 ? void 0 : mainEntity.uri) || !isEmptyValue(authoringItems);
    var handleClose = useCallback(function () { return closeRelationEditor(config.id, relationUri); }, [config, relationUri, closeRelationEditor]);
    var updateModifiedEntityLabel = useCallback(function (connection) {
        if (modifiedEntity) {
            var entityType = getEntityType(metadata, modifiedEntity.type);
            return updateConnectionEntityLabel(entityType, modifiedEntity, connection);
        }
        return connection;
    }, [modifiedEntity, metadata]);
    var setModifiedEntity = useCallback(function (entity) { return entityCreated(entity); }, [entityCreated]);
    var deleteModifiedEntity = useCallback(function (entityUri) { return entityDeleted(entityUri); }, [entityDeleted]);
    var onRelationTypeChange = useCallback(function (newRelationType) {
        var connectionEntity = connection.entity;
        var hasConnectionEntity = connectionEntity && connectionEntity.entityUri;
        var isEntitySuitableForNewRelationType = mainEntity &&
            newRelationType &&
            hasConnectionEntity &&
            isAvailableRelationBetweenEntities(newRelationType.direction === Directions.OUT, __assign(__assign({}, connectionEntity), { type: connectionEntity.entityType }), mainEntity, metadata, newRelationType.type);
        setRelationType(config.id, relationUri, newRelationType);
        if (hasConnectionEntity && !isEntitySuitableForNewRelationType) {
            if (modifiedEntity) {
                deleteModifiedEntity(modifiedEntity.uri);
            }
            setRelationEntity(config.id, relationUri, {});
        }
    }, [
        metadata,
        connection.entity,
        mainEntity,
        setRelationType,
        config.id,
        relationUri,
        modifiedEntity,
        setRelationEntity,
        deleteModifiedEntity
    ]);
    var onChangeEntity = useCallback(function (entity) {
        if (modifiedEntity) {
            deleteModifiedEntity(modifiedEntity.uri);
        }
        setRelationEntity(config.id, relationUri, entity);
        if (error) {
            errorDeactivated(getErrorId(error));
        }
    }, [config.id, relationUri, setRelationEntity, errorDeactivated, modifiedEntity, deleteModifiedEntity, error]);
    var validate = useCallback(function () {
        var errors = validateConnection(metadata, allModifiedEntities, connection).map(function (error) { return (__assign(__assign({}, error), { path: pipe(evaluateErrorPath, map(when(propEq('type', AttributeValuePathItemType.relationConnection), assoc('viewId', config.id))))({
                entityUri: mainEntityUri,
                metadata: metadata,
                connections: [connection],
                entitiesMap: allModifiedEntities || {}
            }, error.uri || error.parentUri) })); });
        errorsSet(errors);
        return errors.length === 0;
    }, [connection, metadata, allModifiedEntities, errorsSet, mainEntityUri, config]);
    var onApply = useCallback(pipe(always(connection), when(validate, pipe(omitMarks, updateModifiedEntityLabel, applyAction(config.id), always(differenceWith(equals, relatedTempEntitiesUrisHistory.current, relatedTempEntities.map(prop('uri')))), forEach(deleteModifiedEntity), handleClose))), [connection, config, updateModifiedEntityLabel, validate, relatedTempEntities]);
    var onCancel = useCallback(function () {
        if (modifiedEntity !== initialModifiedEntity) {
            if (initialModifiedEntity) {
                setModifiedEntity(initialModifiedEntity);
                if (modifiedEntity && modifiedEntity.uri !== initialModifiedEntity.uri) {
                    deleteModifiedEntity(modifiedEntity.uri);
                }
            }
            else {
                deleteModifiedEntity(modifiedEntity.uri);
            }
        }
        var initialTempEntities = initialRelatedTempEntities.current;
        var entitiesUrisToDelete = differenceWith(equals, relatedTempEntitiesUrisHistory.current, initialTempEntities.map(prop('uri')));
        entitiesUrisToDelete.forEach(deleteModifiedEntity);
        initialTempEntities.forEach(setModifiedEntity);
        var filteredErrors = profileErrors.filter(function (profileError) { return !allRelationEditorErrors.some(function (error) { return getErrorId(error) === getErrorId(profileError); }); });
        errorsSet(filteredErrors);
        handleClose();
    }, [
        handleClose,
        setModifiedEntity,
        deleteModifiedEntity,
        modifiedEntity,
        initialModifiedEntity,
        allRelationEditorErrors,
        errorsSet,
        profileErrors
    ]);
    var onAddAttributes = useCallback(function (params) { return addAttributes(params.map(function (param) { return (__assign(__assign({}, param), { viewId: config.id })); })); }, [config.id, addAttributes]);
    var onChangeAttribute = useCallback(function (params) { return modifyAttribute(__assign(__assign({}, params), { viewId: config.id })); }, [config.id, modifyAttribute]);
    var onDeleteAttribute = useCallback(function (params) { return removeAttribute(__assign(__assign({}, params), { viewId: config.id })); }, [config.id, removeAttribute]);
    var onCreateEntity = useCallback(function (entityTypeUri, initValue) {
        var entityType = getEntityType(metadata, entityTypeUri);
        var newEntityAttrTypes = pipe(relationEditorAttributeTypesSelectionStrategy, getCreatableAttributeTypes(mode))(entityType);
        var newEntity = createTemporaryEntity({
            entityTypeUri: entityTypeUri,
            initValue: initValue,
            attributeTypes: newEntityAttrTypes,
            metadata: metadata,
            mode: mode
        });
        onChangeEntity(newEntity);
        setModifiedEntity(newEntity);
    }, [setModifiedEntity, onChangeEntity, mode, metadata]);
    var errorMessage = getErrorMessage(error);
    var entityTypesUris = useMemo(function () {
        var _a;
        var _b = config, content = _b.content, contentSecondLevel = _b.contentSecondLevel;
        var entityTypeUrisFromConfig = (_a = (contentSecondLevel && connection.parentEntityUri ? contentSecondLevel.entityTypes : content.entityTypes)) !== null && _a !== void 0 ? _a : [];
        var relationTypeObj = relationType ? getRelationType(metadata, relationType) : null;
        var suitableEntityTypeUrisForRelationType = relationTypeObj
            ? getSuitableEntityTypeUrisForRelationTypes(direction === Directions.OUT, metadata, [relationTypeObj])
            : [];
        return entityTypeUrisFromConfig.length > 0
            ? entityTypeUrisFromConfig.filter(function (uri) { return suitableEntityTypeUrisForRelationType.includes(uri); })
            : suitableEntityTypeUrisForRelationType;
    }, [config, connection.parentEntityUri, relationType, direction, metadata]);
    var isEntityHasMaskedAttrValue = pipe(values, flatten, any(isAttributeHasMaskedValue))(((_d = (_c = connection.entity) === null || _c === void 0 ? void 0 : _c.object) === null || _d === void 0 ? void 0 : _d.attributes) || {});
    var isMasked = showMasking && isEntityHasMaskedAttrValue;
    return (React.createElement(React.Fragment, null,
        React.createElement("div", { className: styles.editor },
            React.createElement(Typography, { className: styles.title }, isNew ? i18n.text('Add new item') : i18n.text('Edit item')),
            defaultRelationType === null && (React.createElement(ConnectionRelationTypeSelector, { className: styles.item, relation: connection.relation, inRelationTypes: inRelationTypes, outRelationTypes: outRelationTypes, onChange: onRelationTypeChange })),
            Boolean(relationType) && (React.createElement(ConfigPermissionsContextProvider, null,
                React.createElement("div", { ref: ref, className: errorClassName },
                    React.createElement(ErrorWrapper, { errorMessage: errorMessage },
                        React.createElement(AddressAutoCompleteProvider, { entityUri: entityUri },
                            React.createElement(EntitySelector, { className: classnames(styles.item, (_b = {},
                                    _b[styles.dense] = errorMessage ||
                                        (connection.entity && isTempUri(connection.entity.entityUri)),
                                    _b)), entity: connection.entity || {}, entityTypesUris: entityTypesUris, globalSearchRequestOptions: globalSearchRequestOptions, mode: mode, onChange: onChangeEntity, onCreate: canCreateNewEntity ? onCreateEntity : undefined, metadata: metadata, attributeTypesSelectionStrategy: relationEditorAttributeTypesSelectionStrategy, isMasked: isMasked })))),
                React.createElement(HiddenAttributesContext.Provider, { value: emptyHiddenAttributes },
                    React.createElement(EditModeAttributesList, { className: styles.item, attrTypes: firstLevelRelationAttrTypes, entity: attributeListEntity, showEmptyEditors: true, mode: mode, parentUri: relationUri, onAddAttributes: onAddAttributes, onChangeAttribute: onChangeAttribute, onDeleteAttribute: onDeleteAttribute }))))),
        React.createElement("div", { className: styles.actionButtons },
            React.createElement(Button, { onClick: onCancel }, i18n.text('Cancel')),
            React.createElement(Button, { color: "primary", disabled: !hasChanges, onClick: onApply }, i18n.text('Apply')))));
};
export default withContext(ScrollToElementContext, function (contextValue, _a) {
    var connection = _a.connection;
    var _b = contextValue || {}, _c = _b.element, element = _c === void 0 ? {} : _c, type = _b.type;
    var _d = connection.relation, relationUri = _d.relationUri, relationType = _d.relationType;
    var isHighlighted = isHighlightedErrorType(type) && isAttributeTypeError(element, relationUri, relationType);
    return { highlightedError: isHighlighted ? contextValue : null };
}, RelationEditor);
