"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import * as React from "react";
import { useState } from "react";
import uuid from "react-uuid";
import { useEditContext } from "../client/editContext";
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
import { Button } from "../../components/ui/button";
import { MentionTextarea } from "../../components/ui/mention-textarea";
import { deleteComment, resolveComment, unresolveComment, createOrUpdateComment, } from "../services/reviewsService";
import { toast } from "sonner";
import { openAiAgentForComment } from "./commentAi";
import { searchUsers } from "../services/userService";
import { LicenseFeatures, useFeature } from "../../licensing";
import { CheckCircle2, MoreHorizontal } from "lucide-react";
import { CloseButton } from "../../components/ui/close-button";
import { DeleteIcon } from "../../components/ui/delete-icon";
import { formatFeedbackAccountName, getFeedbackAuthorName, } from "./feedbackAvatar";
import { canReplyToComment, getCommentParentSnippet, } from "./feedbackCommentRules";
import { CommentView } from "./CommentView";
import { FeedbackContextHeader, getCommentContextSegments, } from "./FeedbackCard";
import { CommentEditor } from "./CommentEditor";
export function CommentDisplayPopover({ comment, children, onCommentUpdated, }) {
    const editContext = useEditContext();
    const [isOpen, setIsOpen] = useState(false);
    const canUseAi = useFeature(LicenseFeatures.AI);
    const [isSaving, setIsSaving] = useState(false);
    const [editText, setEditText] = useState(comment.text || "");
    const [editingCommentId, setEditingCommentId] = useState(null);
    const [replyingToCommentId, setReplyingToCommentId] = useState(null);
    const [resolvedAuthorNames, setResolvedAuthorNames] = useState({});
    const contentRef = React.useRef(null);
    const commentsListRef = React.useRef(null);
    const shouldScrollToBottomRef = React.useRef(false);
    const threadComments = React.useMemo(() => {
        const comments = editContext?.comments || [];
        const byId = new Map(comments.map((c) => [c.id, c]));
        const findRoot = (item) => {
            let current = byId.get(item.id) || item;
            const seen = new Set();
            while (current.parentCommentId && !seen.has(current.id)) {
                seen.add(current.id);
                const parent = byId.get(current.parentCommentId);
                if (!parent)
                    break;
                current = parent;
            }
            return current;
        };
        const root = findRoot(comment);
        const getRootId = (item) => {
            let current = item;
            const seen = new Set();
            while (current.parentCommentId && !seen.has(current.id)) {
                seen.add(current.id);
                const parent = byId.get(current.parentCommentId);
                if (!parent)
                    return current.parentCommentId;
                current = parent;
            }
            return current.id;
        };
        const items = comments.filter((item) => item.id === root.id || getRootId(item) === root.id);
        if (!items.some((item) => item.id === root.id)) {
            items.unshift(root);
        }
        return items.sort((a, b) => {
            if (a.id === root.id)
                return -1;
            if (b.id === root.id)
                return 1;
            return (new Date(a.created || "").getTime() -
                new Date(b.created || "").getTime());
        });
    }, [comment, editContext?.comments]);
    const rootComment = threadComments[0] || comment;
    const contextSegments = getCommentContextSegments(rootComment, editContext?.currentItemDescriptor?.name);
    React.useEffect(() => {
        if (!isOpen)
            return;
        const authors = Array.from(new Set(threadComments
            .map((threadComment) => threadComment.author?.trim())
            .filter((author) => !!author))).filter((author) => !resolvedAuthorNames[author]);
        if (authors.length === 0)
            return;
        let cancelled = false;
        const resolveAuthors = async () => {
            const entries = await Promise.all(authors.map(async (author) => {
                try {
                    const users = await searchUsers(author, 5);
                    const exact = users.find((user) => user.userName.localeCompare(author, undefined, {
                        sensitivity: "accent",
                    }) === 0) || users[0];
                    const displayName = exact?.displayName?.trim();
                    if (displayName &&
                        displayName.localeCompare(author, undefined, {
                            sensitivity: "accent",
                        }) !== 0) {
                        return [author, displayName];
                    }
                }
                catch {
                    // Best effort: old comments may only have the account id stored.
                }
                return [author, formatFeedbackAccountName(author)];
            }));
            if (cancelled)
                return;
            setResolvedAuthorNames((current) => {
                const next = { ...current };
                entries.forEach(([author, displayName]) => {
                    next[author] = displayName;
                });
                return next;
            });
        };
        resolveAuthors();
        return () => {
            cancelled = true;
        };
    }, [isOpen, resolvedAuthorNames, threadComments]);
    const canDeleteComment = (target) => !target.isNew &&
        (target.author === editContext?.user?.name ||
            editContext?.user?.isAdministrator === true);
    const canEditComment = (target) => !target.isNew &&
        !target.isResolved &&
        target.author === editContext?.user?.name;
    const canResolveComment = (target) => !target.isNew && !editContext?.readonly;
    const defaultReplyTarget = rootComment;
    const replyTarget = threadComments.find((threadComment) => threadComment.id === replyingToCommentId) || defaultReplyTarget;
    const canReplyToRoot = canReplyToComment(defaultReplyTarget, rootComment, editContext?.readonly);
    const canOpenCommentsPanel = !editContext?.configuration?.forceFullscreen;
    const canDeleteThread = canDeleteComment(rootComment);
    const closePopover = () => {
        setIsOpen(false);
        setEditingCommentId(null);
        setReplyingToCommentId(null);
        setEditText(comment.text || "");
    };
    const shouldKeepOpenForPortalTarget = (target) => target instanceof HTMLElement &&
        !!target.closest('[data-comment-display-popover-menu], [data-mention-textarea-popover], [data-slot="tooltip-content"]');
    const handleSave = async (target) => {
        const trimmedText = editText.trim();
        if (!trimmedText)
            return;
        setIsSaving(true);
        try {
            const updatedComment = { ...target, text: trimmedText };
            const result = await createOrUpdateComment(updatedComment);
            if (result.type === "error" || result.type === "unauthorized") {
                console.error("Failed to save comment:", result);
                toast.error(result.details || result.summary || "Failed to save comment");
                return;
            }
            setEditingCommentId(null);
            onCommentUpdated?.();
            await editContext?.loadComments?.();
        }
        finally {
            setIsSaving(false);
        }
    };
    const handleDelete = async (target) => {
        try {
            const result = await deleteComment(target);
            if (result.type === "error" || result.type === "unauthorized") {
                console.error("Failed to delete comment:", result);
                if (result.rawDetails) {
                    console.error("Full error details (including stack trace):\n", result.rawDetails);
                }
                toast.error(result.details || result.summary || "Failed to delete comment");
                return;
            }
            if (target.id === rootComment.id) {
                closePopover();
            }
            onCommentUpdated?.();
            await editContext?.loadComments?.();
        }
        catch (error) {
            console.error("Failed to delete comment:", error);
            toast.error(error instanceof Error ? error.message : "Failed to delete comment");
        }
    };
    const handleResolve = async (target) => {
        await resolveComment(target);
        onCommentUpdated?.();
        await editContext?.loadComments?.();
    };
    const handleUnresolve = async (target) => {
        await unresolveComment(target);
        onCommentUpdated?.();
        await editContext?.loadComments?.();
    };
    const handleAiAction = (target) => openAiAgentForComment(target, editContext);
    const handleReplySave = async (text) => {
        const trimmedText = text.trim();
        if (!trimmedText || !editContext?.currentItemDescriptor || !replyTarget) {
            return;
        }
        setIsSaving(true);
        try {
            const buildReplyFrom = (base, text) => {
                const descriptor = editContext.currentItemDescriptor;
                return {
                    id: uuid(),
                    isNew: false,
                    parentCommentId: base.id,
                    itemId: base.itemId,
                    itemName: base.itemName,
                    fieldId: base.fieldId,
                    fieldName: base.fieldName,
                    mainItemId: base.mainItemId,
                    language: base.language || descriptor.language,
                    version: base.version || descriptor.version,
                    position: 0,
                    rangeStart: base.rangeStart,
                    rangeEnd: base.rangeEnd,
                    author: editContext?.user?.name,
                    authorDisplayName: editContext?.user?.fullName,
                    created: new Date().toISOString(),
                    text,
                    tags: "",
                    fieldValue: base.fieldValue,
                };
            };
            const replyComment = buildReplyFrom(replyTarget, trimmedText);
            const result = await createOrUpdateComment(replyComment);
            if (result.type === "error" || result.type === "unauthorized") {
                console.error("Failed to save reply comment:", result);
                toast.error(result.details || result.summary || "Failed to save reply comment");
                return;
            }
            setReplyingToCommentId(null);
            shouldScrollToBottomRef.current = true;
            onCommentUpdated?.();
            await editContext.loadComments?.();
        }
        finally {
            setIsSaving(false);
        }
    };
    React.useEffect(() => {
        if (!shouldScrollToBottomRef.current)
            return;
        shouldScrollToBottomRef.current = false;
        requestAnimationFrame(() => {
            const commentsList = commentsListRef.current;
            if (!commentsList)
                return;
            commentsList.scrollTo({
                top: commentsList.scrollHeight,
                behavior: "smooth",
            });
        });
    }, [threadComments.length]);
    const getAuthorName = (target) => {
        return getFeedbackAuthorName(target, target.author ? resolvedAuthorNames[target.author] : undefined);
    };
    const openCommentsPanel = () => {
        closePopover();
        if (editContext?.fullscreen) {
            editContext.setFullscreen(false);
        }
        editContext?.toggleSidebar("feedback");
    };
    const handleDeleteThread = () => {
        const replyCount = threadComments.filter((threadComment) => threadComment.id !== rootComment.id).length;
        const replyText = replyCount > 0
            ? ` and ${replyCount} ${replyCount === 1 ? "reply" : "replies"}`
            : "";
        editContext?.confirm({
            header: "Delete thread",
            message: `Delete this comment thread${replyText}? This action cannot be undone.`,
            acceptLabel: "Delete",
            showCancel: true,
            accept: () => {
                void handleDelete(rootComment);
            },
        });
    };
    return (_jsxs(Popover, { open: isOpen, onOpenChange: (open) => {
            if (open) {
                setIsOpen(true);
            }
            else {
                closePopover();
            }
        }, enableIframeClickDetection: true, children: [_jsx(PopoverTrigger, { asChild: true, children: children }), _jsx(PopoverContent, { ref: contentRef, tabIndex: -1, "data-testid": "comment-display-popover", className: "border-border-default w-[380px] overflow-hidden rounded-2xl border bg-white p-0 shadow-xl", side: "bottom", align: "start", onOpenAutoFocus: (e) => {
                    e.preventDefault();
                    contentRef.current?.focus();
                }, onInteractOutside: (e) => {
                    if (shouldKeepOpenForPortalTarget(e.target)) {
                        e.preventDefault();
                    }
                }, onPointerDownOutside: (e) => {
                    if (shouldKeepOpenForPortalTarget(e.target)) {
                        e.preventDefault();
                    }
                }, onFocusOutside: (e) => {
                    if (shouldKeepOpenForPortalTarget(e.target)) {
                        e.preventDefault();
                    }
                }, children: _jsxs("div", { className: "flex h-full max-h-[500px] flex-col", children: [_jsxs("div", { className: "border-border-default flex h-10 shrink-0 items-center border-b px-4", children: [_jsx("div", { className: "text-neutral-grey-100 text-base font-semibold", children: "Comments" }), _jsxs("div", { className: "ml-auto flex items-center gap-1", children: [(canOpenCommentsPanel || canDeleteThread) && (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "text-neutral-grey-100 hover:bg-neutral-grey-5 inline-flex h-8 w-8 items-center justify-center rounded-full", "aria-label": "More options", children: _jsx(MoreHorizontal, { className: "h-4 w-4", strokeWidth: 2 }) }) }), _jsxs(PopoverContent, { className: "w-52 p-1", align: "end", "data-comment-display-popover-menu": true, children: [canOpenCommentsPanel && (_jsx("button", { type: "button", className: "hover:bg-neutral-grey-5 w-full rounded px-2 py-1.5 text-left text-sm", onClick: openCommentsPanel, children: "View in Feedback Panel" })), canDeleteThread && (_jsxs("button", { type: "button", className: "hover:bg-neutral-grey-5 flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm", onClick: handleDeleteThread, children: [_jsx(DeleteIcon, { size: "lg" }), "Delete Thread"] }))] })] })), canUseAi &&
                                            canResolveComment(rootComment) &&
                                            !rootComment.isResolved && (_jsx("button", { type: "button", className: "text-neutral-grey-100 hover:bg-neutral-grey-5 inline-flex h-8 w-8 items-center justify-center rounded-full", "aria-label": "Resolve with AI", title: "Resolve with AI", onClick: () => handleAiAction(rootComment), children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [_jsx("path", { d: "M10.0041 4.31178C10.1564 4.27502 10.2962 4.23826 10.4361 4.2015C10.6048 4.15657 10.7777 4.11981 10.9422 4.06263C11.58 3.82573 11.9832 3.37645 12.156 2.72703C12.2178 2.49013 12.2795 2.25323 12.3412 2.02042C12.3412 2.01225 12.3412 2.00817 12.3535 2C12.3659 2.04084 12.3741 2.08169 12.3865 2.12253C12.4523 2.37168 12.5058 2.62083 12.5881 2.8659C12.8103 3.5194 13.267 3.9401 13.9376 4.12389C14.1763 4.18924 14.4191 4.24643 14.6577 4.31178C14.67 4.31178 14.6783 4.31586 14.6988 4.32403C14.6083 4.34854 14.526 4.36896 14.4396 4.38938C14.2174 4.45065 13.9911 4.49558 13.7731 4.56909C13.1394 4.79374 12.7321 5.23894 12.551 5.88428C12.4811 6.13342 12.4194 6.38257 12.3453 6.63581C12.3453 6.62355 12.3371 6.6113 12.333 6.59905C12.2671 6.34581 12.2095 6.09258 12.1314 5.84343C11.9215 5.15317 11.4607 4.70796 10.7571 4.516C10.5184 4.45065 10.2798 4.39346 10.0411 4.3322C10.0288 4.3322 10.0206 4.32811 10 4.31995L10.0041 4.31178Z", fill: "currentColor" }), _jsx("path", { d: "M1 8.19596C1.36208 8.10202 1.69948 8.01624 2.03687 7.92639C2.44422 7.81611 2.85979 7.73442 3.25479 7.59147C4.78541 7.02782 5.75645 5.94545 6.17614 4.38112C6.32838 3.81339 6.4765 3.24565 6.62462 2.67792C6.62874 2.66158 6.63285 2.64524 6.65343 2.62891C6.67811 2.72693 6.70692 2.82496 6.7316 2.9189C6.88796 3.51522 7.01551 4.11972 7.21301 4.70379C7.7479 6.28037 8.84238 7.28513 10.4553 7.72625C11.0313 7.88146 11.6115 8.02441 12.1875 8.17554C12.2122 8.18371 12.2369 8.18779 12.2822 8.20413C12.06 8.26131 11.8625 8.31032 11.6609 8.36342C11.126 8.50637 10.5787 8.61257 10.0603 8.80045C8.53379 9.34368 7.55863 10.4138 7.1266 11.9659C6.95791 12.5663 6.80978 13.1667 6.63697 13.7753C6.62874 13.7467 6.62051 13.7181 6.61228 13.6854C6.45181 13.0768 6.31603 12.4642 6.13088 11.8638C5.62478 10.2014 4.51385 9.13537 2.82687 8.67383C2.25495 8.51863 1.6789 8.37567 1.10286 8.22863C1.07818 8.22047 1.05349 8.21638 1 8.20004L1 8.19596Z", fill: "currentColor" }), _jsx("path", { d: "M10.0041 12.5254C10.1564 12.4886 10.2962 12.4519 10.4361 12.4151C10.6048 12.3702 10.7777 12.3334 10.9422 12.2763C11.58 12.0394 11.9832 11.5901 12.156 10.9406C12.2178 10.7038 12.2795 10.4669 12.3412 10.234C12.3412 10.2259 12.3412 10.2218 12.3535 10.2136C12.3659 10.2545 12.3741 10.2953 12.3865 10.3362C12.4523 10.5853 12.5058 10.8345 12.5881 11.0795C12.8103 11.733 13.267 12.1537 13.9376 12.3375C14.1763 12.4029 14.4191 12.46 14.6577 12.5254C14.67 12.5254 14.6783 12.5295 14.6988 12.5377C14.6083 12.5622 14.526 12.5826 14.4396 12.603C14.2174 12.6643 13.9911 12.7092 13.7731 12.7827C13.1394 13.0074 12.7321 13.4526 12.551 14.0979C12.4811 14.347 12.4194 14.5962 12.3453 14.8494C12.3453 14.8372 12.3371 14.8249 12.333 14.8127C12.2671 14.5594 12.2095 14.3062 12.1314 14.0571C11.9215 13.3668 11.4607 12.9216 10.7571 12.7296C10.5184 12.6643 10.2798 12.6071 10.0411 12.5458C10.0288 12.5458 10.0206 12.5417 10 12.5336L10.0041 12.5254Z", fill: "currentColor" })] }) })), canResolveComment(rootComment) && (_jsx("button", { type: "button", className: `hover:bg-neutral-grey-5 inline-flex h-8 w-8 items-center justify-center rounded-full ${rootComment.isResolved
                                                ? "text-feedback-green"
                                                : "text-neutral-grey-100"}`, "aria-label": rootComment.isResolved ? "Unresolve" : "Resolve", onClick: () => rootComment.isResolved
                                                ? handleUnresolve(rootComment)
                                                : handleResolve(rootComment), children: _jsx(CheckCircle2, { className: "h-5 w-5", strokeWidth: 2 }) })), _jsx(CloseButton, { label: "Close comments", onClick: closePopover })] })] }), _jsx(FeedbackContextHeader, { segments: contextSegments, className: "border-border-default min-h-11 border-b bg-white" }), _jsx("div", { ref: commentsListRef, className: "min-h-0 flex-1 space-y-4 overflow-y-auto px-4 py-4", children: threadComments.map((threadComment) => (_jsx("div", { children: editingCommentId === threadComment.id ? (_jsxs("div", { className: "mt-2 space-y-2", children: [_jsx(MentionTextarea, { value: editText, onValueChange: setEditText, className: "min-h-[72px] resize-none rounded-md text-sm", disabled: isSaving, autoFocus: true }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
                                                        setEditingCommentId(null);
                                                        setEditText("");
                                                    }, disabled: isSaving, children: "Cancel" }), _jsx(Button, { size: "sm", onClick: () => handleSave(threadComment), disabled: !editText.trim() || isSaving, children: isSaving ? "Saving..." : "Save" })] })] })) : (_jsx(CommentView, { comment: threadComment, canEdit: canEditComment(threadComment), canDelete: canDeleteComment(threadComment), canResolve: threadComment.id === rootComment.id &&
                                        canResolveComment(threadComment), parentSnippet: getCommentParentSnippet(threadComment, threadComments), resolvedAuthorName: getAuthorName(threadComment), onEdit: () => {
                                        setReplyingToCommentId(null);
                                        setEditText(threadComment.text || "");
                                        setEditingCommentId(threadComment.id);
                                    }, onDelete: () => handleDelete(threadComment), onResolve: threadComment.id === rootComment.id
                                        ? () => handleResolve(threadComment)
                                        : undefined, onUnresolve: threadComment.id === rootComment.id
                                        ? () => handleUnresolve(threadComment)
                                        : undefined, onAiAction: canUseAi && threadComment.id === rootComment.id
                                        ? () => handleAiAction(threadComment)
                                        : undefined, onReply: canReplyToComment(threadComment, rootComment, editContext?.readonly)
                                        ? () => {
                                            setEditingCommentId(null);
                                            setReplyingToCommentId(threadComment.id);
                                        }
                                        : undefined, showCommentIcon: false, showContextInfo: false, compact: true })) }, threadComment.id))) }), canReplyToRoot && (_jsxs("div", { className: "shrink-0 px-4 pt-1 pb-3", children: [replyingToCommentId && replyTarget && (_jsxs("div", { className: "text-neutral-grey-50 mb-2 flex items-center justify-between gap-2 pl-9 text-[11px]", children: [_jsxs("span", { className: "truncate", children: ["Replying to ", getAuthorName(replyTarget)] }), _jsx("button", { type: "button", className: "badge-pad-sm text-neutral-grey-50 hover:bg-neutral-grey-5 shrink-0 rounded", onClick: () => {
                                                setReplyingToCommentId(null);
                                            }, children: "Cancel" })] })), _jsx(CommentEditor, { onSave: handleReplySave, onCancel: () => setReplyingToCommentId(null), isSaving: isSaving, placeholder: "Add a comment ...", submitLabel: "Add comment", showTagSelector: false, showCancelButton: false, clearOnSave: true, compact: true, autoFocus: !!replyingToCommentId }, replyingToCommentId || rootComment.id)] }))] }) })] }));
}
//# sourceMappingURL=CommentDisplayPopover.js.map