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));
};
import { useCallback, useEffect, useState, useMemo } from 'react';
import { andThen, fromPairs, map, otherwise, pipe, props, uniqBy, prop, propEq, when, always, assocPath, dissocPath, path, pathOr, mergeRight } from 'ramda';
import { createComment as createCommentRequest, createReply as createReplyRequest, getComment as getCommentRequest, getCommentsCount as getCommentsCountRequest, getTotalCommentsCount as getTotalCommentsCountRequest, updateComment as updateCommentRequest, updateReply as updateReplyRequest, deleteComment as deleteCommentRequest, deleteReply as deleteReplyRequest, getComments as getCommentsRequest, isTempUri } from '@reltio/mdm-sdk';
import { useMdmCollaborationPath, useMdmEntity, useMdmIsCollaborationEnabled, useMdmTenant } from '../../contexts/MdmModuleContext';
var filterReplies = function (replyId, replies) { return replies.filter(function (reply) { return reply.replyId !== replyId; }); };
var getUpdatedReplies = function (comment, reply) {
    var changedReplyIndex = comment.replies.findIndex(function (_a) {
        var replyId = _a.replyId;
        return replyId === reply.replyId;
    });
    var newReplies = __spreadArray([], comment.replies, true);
    newReplies[changedReplyIndex] = reply;
    return newReplies;
};
var defaultObjectIds = [];
var catchErrorInRequest = function (e) { return console.warn('Collaboration error', e); };
export var useCollaboration = function (_a) {
    var _b = _a.objectIds, objectIds = _b === void 0 ? defaultObjectIds : _b, objectTypes = _a.objectTypes, _c = _a.enabled, enabled = _c === void 0 ? true : _c, _d = _a.needsTotalCount, needsTotalCount = _d === void 0 ? false : _d;
    var entity = useMdmEntity();
    var tenant = useMdmTenant();
    var collaborationPath = useMdmCollaborationPath();
    var isCollaborationEnabled = useMdmIsCollaborationEnabled() && enabled;
    var _e = useState(null), pageToken = _e[0], setPageToken = _e[1];
    var _f = useState([]), comments = _f[0], setComments = _f[1];
    var _g = useState(null), commentsMap = _g[0], setCommentsMap = _g[1];
    var _h = useState(null), totalCommentsCount = _h[0], setTotalCommentsCount = _h[1];
    var _j = useState(null), currentComment = _j[0], setCurrentComment = _j[1];
    var _k = useState(false), sending = _k[0], setSending = _k[1];
    var _l = useState(false), loading = _l[0], setLoading = _l[1];
    var _m = useState({}), collaborationStateMap = _m[0], setCollaborationStateMap = _m[1];
    var addCommentsToCommentsMap = useCallback(function (commentsMap) {
        setCommentsMap(function (currentCommentsMap) { return (__assign(__assign({}, currentCommentsMap), commentsMap)); });
    }, []);
    var entityUri = entity === null || entity === void 0 ? void 0 : entity.uri;
    var isCollaborationEnabledForEntity = isCollaborationEnabled && entityUri && !isTempUri(entityUri);
    var getCommentsCount = useCallback(function (objectIds, shouldResetCommentsMap) {
        if (shouldResetCommentsMap === void 0) { shouldResetCommentsMap = false; }
        if (!isCollaborationEnabledForEntity) {
            setCommentsMap(null);
            return;
        }
        var onError = function (e) {
            console.warn('Collaboration error', e);
            setCommentsMap(null);
        };
        pipe(getCommentsCountRequest, andThen(map(props(['objectId', 'comments']))), andThen(fromPairs), andThen(shouldResetCommentsMap ? setCommentsMap : addCommentsToCommentsMap), otherwise(onError))({ uris: objectIds, tenant: tenant, collaborationPath: collaborationPath });
    }, [addCommentsToCommentsMap, collaborationPath, isCollaborationEnabledForEntity, tenant]);
    var getTotalCommentsCount = useCallback(function () {
        if (isCollaborationEnabledForEntity) {
            var onError = function (e) {
                console.warn('Collaboration error', e);
                setTotalCommentsCount(null);
            };
            pipe(getTotalCommentsCountRequest, andThen(prop('total')), andThen(setTotalCommentsCount), otherwise(onError))({
                relatedObjectUri: entityUri,
                collaborationPath: collaborationPath,
                tenant: tenant
            });
        }
        else {
            setTotalCommentsCount(null);
        }
    }, [isCollaborationEnabledForEntity, collaborationPath, entityUri, tenant]);
    useEffect(function () {
        if (needsTotalCount) {
            getTotalCommentsCount();
        }
    }, [getTotalCommentsCount, needsTotalCount]);
    useEffect(function () {
        getCommentsCount(objectIds, true);
    }, [objectIds, getCommentsCount]);
    var getComments = useCallback(function (relatedObjectUri, pageToken) {
        getCommentsRequest({ collaborationPath: collaborationPath, tenant: tenant, relatedObjectUri: relatedObjectUri, pageToken: pageToken })
            .then(function (_a) {
            var items = _a.items, nextPageToken = _a.nextPageToken;
            setComments(function (prevComments) {
                if (!pageToken) {
                    return items;
                }
                return uniqBy(prop('commentId'), __spreadArray(__spreadArray([], prevComments, true), items, true));
            });
            setPageToken(nextPageToken);
        })
            .catch(catchErrorInRequest);
    }, [collaborationPath, tenant]);
    var getComment = useCallback(function (commentId) {
        setLoading(true);
        getCommentRequest({ collaborationPath: collaborationPath, tenant: tenant, commentId: commentId })
            .then(setCurrentComment)
            .catch(catchErrorInRequest)
            .finally(function () { return setLoading(false); });
    }, [collaborationPath, tenant]);
    var addCommentToState = function (comment, uri) {
        setCurrentComment(comment);
        setCommentsMap(function (prevCommentsMap) {
            var _a;
            return __assign(__assign({}, prevCommentsMap), (_a = {}, _a[uri] = __spreadArray(__spreadArray([], (prevCommentsMap[uri] || []), true), [{ commentId: comment.commentId, replies: 0, status: 'open' }], false), _a));
        });
        setComments(function (prevComments) { return __spreadArray([comment], prevComments, true); });
        setTotalCommentsCount(function (prevCount) { return (prevCount || 0) + 1; });
    };
    var updateCommentInState = function (comment) {
        setCurrentComment(comment);
        setComments(map(when(propEq('commentId', comment.commentId), always(comment))));
    };
    var deleteCommentFromState = function (commentId, uri) {
        var repliesCountFromComments;
        var repliesCountFromCommentsMap;
        setCurrentComment(null);
        setCommentsMap(function (prevCommentsMap) {
            var _a;
            var _b, _c;
            repliesCountFromCommentsMap = (_c = (_b = prevCommentsMap === null || prevCommentsMap === void 0 ? void 0 : prevCommentsMap[uri]) === null || _b === void 0 ? void 0 : _b.find(function (c) { return c.commentId === commentId; })) === null || _c === void 0 ? void 0 : _c.replies;
            var comments = prevCommentsMap[uri] || [];
            return __assign(__assign({}, prevCommentsMap), (_a = {}, _a[uri] = comments.filter(function (c) { return c.commentId !== commentId; }), _a));
        });
        setComments(function (prevComments) {
            var _a;
            repliesCountFromComments = (_a = prevComments.find(function (comment) { return comment.commentId === commentId; })) === null || _a === void 0 ? void 0 : _a.replies.length;
            return prevComments.filter(function (comment) { return comment.commentId !== commentId; });
        });
        setTotalCommentsCount(function (prevCount) { var _a; return Math.max((prevCount || 0) - (1 + ((_a = repliesCountFromComments !== null && repliesCountFromComments !== void 0 ? repliesCountFromComments : repliesCountFromCommentsMap) !== null && _a !== void 0 ? _a : 0)), 0); });
    };
    var createComment = useCallback(function (_a) {
        var content = _a.content, namedUsers = _a.namedUsers, objectType = _a.objectType, uri = _a.uri, relatedObjectUris = _a.relatedObjectUris;
        setSending(true);
        var data = {
            objectId: uri,
            content: content,
            relatedObjectUris: relatedObjectUris,
            objectType: objectType,
            visibility: 'public',
            namedUsers: namedUsers,
            permanentLink: "".concat(window.location.href, "&commentId=").concat(encodeURIComponent('{commentId}'))
        };
        return createCommentRequest({ collaborationPath: collaborationPath, tenant: tenant, data: data })
            .then(function (_a) {
            var comment = _a[0];
            return addCommentToState(comment, uri);
        })
            .catch(catchErrorInRequest)
            .finally(function () { return setSending(false); });
    }, [collaborationPath, tenant]);
    var editComment = useCallback(function (_a) {
        var content = _a.content, namedUsers = _a.namedUsers, objectType = _a.objectType, uri = _a.uri, relatedObjectUris = _a.relatedObjectUris, commentId = _a.commentId, replies = _a.replies;
        setSending(true);
        var data = {
            objectId: uri,
            content: content,
            relatedObjectUris: relatedObjectUris,
            objectType: objectType,
            visibility: 'public',
            namedUsers: namedUsers,
            commentId: commentId,
            replies: replies
        };
        return updateCommentRequest({ collaborationPath: collaborationPath, tenant: tenant, data: data })
            .then(function (comment) { return updateCommentInState(__assign(__assign({}, comment), { replies: replies })); })
            .catch(catchErrorInRequest)
            .finally(function () { return setSending(false); });
    }, [collaborationPath, tenant]);
    var deleteComment = useCallback(function (_a) {
        var uri = _a.uri, commentId = _a.commentId;
        setSending(true);
        deleteCommentRequest({ collaborationPath: collaborationPath, tenant: tenant, commentId: commentId })
            .then(function () { return deleteCommentFromState(commentId, uri); })
            .catch(catchErrorInRequest)
            .finally(function () { return setSending(false); });
    }, [collaborationPath, tenant]);
    var updateReplyInState = useCallback(function (reply, uri, commentId, isDeleteReply) {
        var newStatus = reply.action === 'resolve' ? 'resolved' : 'open';
        var replyId = reply.replyId;
        if (newStatus === 'resolved') {
            setCurrentComment(function (prevCurrentComment) {
                if (prevCurrentComment) {
                    return null;
                }
            });
        }
        else {
            setCurrentComment(function (prevCurrentComment) {
                if (prevCurrentComment) {
                    return __assign(__assign({}, prevCurrentComment), { replies: isDeleteReply
                            ? filterReplies(replyId, prevCurrentComment.replies)
                            : __spreadArray(__spreadArray([], prevCurrentComment.replies, true), [reply], false) });
                }
            });
        }
        setCommentsMap(function (prevCommentsMap) {
            var _a;
            return (__assign(__assign({}, prevCommentsMap), (_a = {}, _a[uri] = (prevCommentsMap[uri] || []).map(function (comment) {
                if (comment.commentId === commentId) {
                    return __assign(__assign({}, comment), { status: newStatus, replies: isDeleteReply ? comment.replies - 1 : comment.replies + 1 });
                }
                return comment;
            }), _a)));
        });
        setComments(function (prevComments) {
            return prevComments.map(function (comment) {
                if (comment.commentId === commentId) {
                    return __assign(__assign({}, comment), { status: newStatus, replies: isDeleteReply
                            ? filterReplies(reply.replyId, comment.replies)
                            : __spreadArray(__spreadArray([], comment.replies, true), [reply], false) });
                }
                return comment;
            });
        });
        if (isDeleteReply) {
            setTotalCommentsCount(function (prevCount) { return Math.max((prevCount || 0) - 1, 0); });
        }
        else {
            setTotalCommentsCount(function (prevCount) { return (prevCount || 0) + 1; });
        }
    }, []);
    var deleteReply = useCallback(function (_a) {
        var uri = _a.uri, commentId = _a.commentId, reply = _a.reply;
        setSending(true);
        deleteReplyRequest({ collaborationPath: collaborationPath, tenant: tenant, commentId: commentId, replyId: reply.replyId })
            .then(function () { return updateReplyInState(reply, uri, commentId, true); })
            .catch(catchErrorInRequest)
            .finally(function () { return setSending(false); });
    }, [collaborationPath, updateReplyInState, tenant]);
    var editReplyInState = useCallback(function (reply, commentId) {
        setCurrentComment(function (prevCurrentComment) {
            if (prevCurrentComment) {
                return __assign(__assign({}, prevCurrentComment), { replies: getUpdatedReplies(prevCurrentComment, reply) });
            }
        });
        setComments(function (prevComments) {
            return prevComments.map(function (comment) {
                if (comment.commentId === commentId) {
                    return __assign(__assign({}, comment), { replies: getUpdatedReplies(comment, reply) });
                }
                return comment;
            });
        });
    }, []);
    var editReply = useCallback(function (_a) {
        var content = _a.content, namedUsers = _a.namedUsers, commentId = _a.commentId, replyId = _a.replyId;
        setSending(true);
        var data = {
            content: content,
            namedUsers: namedUsers
        };
        return updateReplyRequest({ collaborationPath: collaborationPath, tenant: tenant, commentId: commentId, replyId: replyId, data: data })
            .then(function (reply) { return editReplyInState(reply, commentId); })
            .catch(catchErrorInRequest)
            .finally(function () { return setSending(false); });
    }, [collaborationPath, tenant, editReplyInState]);
    var createReply = useCallback(function (_a) {
        var content = _a.content, namedUsers = _a.namedUsers, commentId = _a.commentId, action = _a.action, uri = _a.uri;
        setSending(true);
        var data = {
            content: content,
            action: action,
            namedUsers: namedUsers
        };
        return createReplyRequest({ collaborationPath: collaborationPath, tenant: tenant, commentId: commentId, data: data })
            .then(function (_a) {
            var reply = _a[0];
            return updateReplyInState(reply, uri, commentId);
        })
            .catch(catchErrorInRequest)
            .finally(function () { return setSending(false); });
    }, [collaborationPath, tenant, updateReplyInState]);
    var resolveThread = useCallback(function (_a) {
        var commentId = _a.commentId, uri = _a.uri;
        createReply({ content: '', namedUsers: [], commentId: commentId, action: 'resolve', uri: uri });
    }, [createReply]);
    var reOpenThread = useCallback(function (_a) {
        var commentId = _a.commentId, uri = _a.uri;
        createReply({ content: '', namedUsers: [], commentId: commentId, action: 'reopen', uri: uri });
    }, [createReply]);
    var clearCurrentComment = useCallback(function () {
        setCurrentComment(null);
    }, []);
    var getCommentState = useCallback(function (uri, commentUri) { return pathOr({}, [uri, commentUri], collaborationStateMap); }, [collaborationStateMap]);
    var clearCommentState = useCallback(function (uri, commentUri) { return setCollaborationStateMap(dissocPath([uri, commentUri])); }, []);
    var updateCommentState = useCallback(function (uri, commentUri, newState) {
        setCollaborationStateMap(function (prevCollaborationStateMap) {
            var pathToCommentState = [uri, commentUri];
            var prevCommentState = path(pathToCommentState, prevCollaborationStateMap);
            return assocPath(pathToCommentState, mergeRight(prevCommentState, newState), prevCollaborationStateMap);
        });
    }, []);
    return useMemo(function () { return ({
        clearCurrentComment: clearCurrentComment,
        comments: comments,
        commentsMap: commentsMap,
        getCommentState: getCommentState,
        updateCommentState: updateCommentState,
        clearCommentState: clearCommentState,
        createComment: createComment,
        createReply: createReply,
        currentComment: currentComment,
        deleteComment: deleteComment,
        editComment: editComment,
        getComment: getComment,
        getComments: getComments,
        getCommentsCount: getCommentsCount,
        loading: loading,
        objectTypes: objectTypes,
        pageToken: pageToken,
        reOpenThread: reOpenThread,
        resolveThread: resolveThread,
        sending: sending,
        deleteReply: deleteReply,
        editReply: editReply,
        totalCommentsCount: totalCommentsCount
    }); }, [
        clearCurrentComment,
        comments,
        commentsMap,
        getCommentState,
        updateCommentState,
        clearCommentState,
        createComment,
        createReply,
        currentComment,
        deleteComment,
        editComment,
        getComment,
        getComments,
        getCommentsCount,
        loading,
        objectTypes,
        pageToken,
        reOpenThread,
        resolveThread,
        sending,
        deleteReply,
        editReply,
        totalCommentsCount
    ]);
};
