'use client';
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createComment as apiCreateComment, deleteComment as apiDeleteComment, groupCommentsIntoThreads, listComments as apiListComments, reopenComment as apiReopenComment, resolveComment as apiResolveComment, updateComment as apiUpdateComment, } from '../lib/comments-client.js';
import { MentionableTextarea } from './MentionableTextarea.js';
const defaultApi = {
    list: (documentId, opts) => apiListComments(documentId, opts),
    create: (input) => apiCreateComment(input),
    update: (id, body) => apiUpdateComment(id, body),
    resolve: (id) => apiResolveComment(id),
    reopen: (id) => apiReopenComment(id),
    remove: (id) => apiDeleteComment(id),
};
export function CommentSidePanel({ documentId, currentUserId, isAdmin = false, onComposeAnchor, onActiveCommentChange, onError, api = defaultApi, mentionSearch, className, }) {
    const [comments, setComments] = useState([]);
    const [includeResolved, setIncludeResolved] = useState(false);
    const [loading, setLoading] = useState(true);
    const [draft, setDraft] = useState('');
    const [activeCommentId, setActiveCommentId] = useState(null);
    const mountedRef = useRef(true);
    useEffect(() => {
        return () => {
            mountedRef.current = false;
        };
    }, []);
    const setActive = useCallback((id) => {
        setActiveCommentId(id);
        onActiveCommentChange?.(id);
    }, [onActiveCommentChange]);
    const refresh = useCallback(async () => {
        setLoading(true);
        const out = await api.list(documentId, { includeResolved });
        if (!mountedRef.current)
            return;
        if (!out.ok) {
            onError?.(out.error);
            setComments([]);
        }
        else {
            setComments(out.result);
        }
        setLoading(false);
    }, [api, documentId, includeResolved, onError]);
    useEffect(() => {
        void refresh();
    }, [refresh]);
    const threads = useMemo(() => groupCommentsIntoThreads(comments), [comments]);
    const handleCompose = useCallback(async () => {
        const body = draft.trim();
        if (!body)
            return;
        const anchor = onComposeAnchor?.() ?? null;
        const out = await api.create({ documentId, body, anchor });
        if (!out.ok) {
            onError?.(out.error);
            return;
        }
        setDraft('');
        setActive(out.result.id);
        await refresh();
    }, [draft, onComposeAnchor, api, documentId, onError, setActive, refresh]);
    const handleReply = useCallback(async (parentId, body) => {
        const trimmed = body.trim();
        if (!trimmed)
            return;
        const out = await api.create({ documentId, body: trimmed, parentId });
        if (!out.ok) {
            onError?.(out.error);
            return;
        }
        await refresh();
    }, [api, documentId, onError, refresh]);
    const handleEdit = useCallback(async (id, body) => {
        const out = await api.update(id, body);
        if (!out.ok) {
            onError?.(out.error);
            return false;
        }
        await refresh();
        return true;
    }, [api, onError, refresh]);
    const handleResolve = useCallback(async (id, alreadyResolved) => {
        const out = alreadyResolved ? await api.reopen(id) : await api.resolve(id);
        if (!out.ok) {
            onError?.(out.error);
            return;
        }
        await refresh();
    }, [api, onError, refresh]);
    const handleDelete = useCallback(async (id) => {
        const out = await api.remove(id);
        if (!out.ok) {
            onError?.(out.error);
            return;
        }
        if (activeCommentId === id)
            setActive(null);
        await refresh();
    }, [api, onError, refresh, activeCommentId, setActive]);
    const wrapperClass = `flex h-full flex-col gap-3 border-l border-border bg-card p-3 ${className ?? ''}`;
    return (_jsxs("aside", { className: wrapperClass, "data-testid": "comment-side-panel", children: [_jsxs("header", { className: "flex items-center justify-between", children: [_jsx("h2", { className: "text-foreground text-sm font-medium", children: "Comments" }), _jsxs("label", { className: "text-muted-foreground flex cursor-pointer items-center gap-1 text-xs", children: [_jsx("input", { type: "checkbox", checked: includeResolved, onChange: (e) => setIncludeResolved(e.target.checked), "data-testid": "include-resolved-toggle" }), "Show resolved"] })] }), _jsxs("section", { className: "flex flex-col gap-1.5", children: [_jsx(MentionableTextarea, { className: "border-border focus-visible:border-ring w-full resize-none rounded border p-2 text-sm focus:outline-none", rows: 3, placeholder: "Write a comment\u2026  Type @ to mention a teammate.", value: draft, onChange: setDraft, "data-testid": "comment-composer", search: mentionSearch }), _jsx("div", { className: "flex justify-end", children: _jsx("button", { type: "button", className: "bg-primary hover:bg-info rounded px-3 py-1 text-xs font-medium text-white disabled:opacity-50", disabled: !draft.trim(), onClick: handleCompose, "data-testid": "comment-submit", children: "Comment" }) })] }), _jsx("section", { className: "flex-1 overflow-y-auto", "aria-busy": loading, "data-testid": "comment-list", children: loading ? (_jsx("p", { className: "text-muted-foreground text-xs", children: "Loading\u2026" })) : threads.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-xs", children: "No comments yet." })) : (_jsx("ul", { className: "flex flex-col gap-2", children: threads.map((thread) => (_jsx(CommentThreadView, { thread: thread, currentUserId: currentUserId, isAdmin: isAdmin, isActive: activeCommentId === thread.root.id, onFocus: () => setActive(thread.root.id), onReply: (body) => handleReply(thread.root.id, body), onEdit: (id, body) => handleEdit(id, body), onToggleResolve: (id, alreadyResolved) => handleResolve(id, alreadyResolved), onDelete: (id) => handleDelete(id), mentionSearch: mentionSearch }, thread.root.id))) })) })] }));
}
function CommentThreadView({ thread, currentUserId, isAdmin, isActive, onFocus, onReply, onEdit, onToggleResolve, onDelete, mentionSearch, }) {
    const [replyDraft, setReplyDraft] = useState('');
    const cardClass = `rounded border bg-muted p-2 ${isActive ? 'border-yellow-400 ring-1 ring-yellow-300' : 'border-border'}`;
    return (_jsxs("li", { className: cardClass, onClick: onFocus, "data-testid": `comment-thread-${thread.root.id}`, "data-active": isActive ? 'true' : 'false', children: [_jsx(CommentRow, { comment: thread.root, currentUserId: currentUserId, isAdmin: isAdmin, onEdit: onEdit, onToggleResolve: () => onToggleResolve(thread.root.id, thread.root.isResolved), onDelete: () => onDelete(thread.root.id), mentionSearch: mentionSearch }), thread.replies.length > 0 && (_jsx("ul", { className: "border-border mt-2 flex flex-col gap-1 border-l-2 pl-2", children: thread.replies.map((reply) => (_jsx("li", { children: _jsx(CommentRow, { comment: reply, currentUserId: currentUserId, isAdmin: isAdmin, onEdit: onEdit, 
                        // Replies inherit the thread's resolve action; we only
                        // expose delete here.
                        onToggleResolve: null, onDelete: () => onDelete(reply.id), mentionSearch: mentionSearch }) }, reply.id))) })), _jsxs("div", { className: "mt-2 flex items-start gap-1", children: [_jsx("div", { className: "flex-1", children: _jsx(MentionableTextarea, { className: "border-border focus-visible:border-ring w-full resize-none rounded border px-2 py-1 text-xs focus:outline-none", placeholder: "Reply\u2026  Type @ to mention.", rows: 1, value: replyDraft, onChange: setReplyDraft, "data-testid": `reply-input-${thread.root.id}`, onClick: (e) => e.stopPropagation(), search: mentionSearch }) }), _jsx("button", { type: "button", className: "bg-foreground hover:bg-foreground/90 rounded px-2 py-1 text-xs font-medium text-white disabled:opacity-50", disabled: !replyDraft.trim(), onClick: (e) => {
                            e.stopPropagation();
                            onReply(replyDraft);
                            setReplyDraft('');
                        }, "data-testid": `reply-submit-${thread.root.id}`, children: "Reply" })] })] }));
}
function CommentRow({ comment, currentUserId, isAdmin, onEdit, onToggleResolve, onDelete, mentionSearch, }) {
    const [editing, setEditing] = useState(false);
    const [draft, setDraft] = useState(comment.body);
    useEffect(() => {
        setDraft(comment.body);
    }, [comment.body]);
    const canMutate = isAdmin || comment.userId === currentUserId;
    const meta = (_jsxs("div", { className: "text-muted-foreground text-xs", children: [comment.userId ?? 'deleted user', " \u00B7 ", formatRelative(comment.createdAt), comment.isResolved ? ' · resolved' : null] }));
    return (_jsxs("div", { className: "flex flex-col gap-1", children: [meta, editing ? (_jsxs(_Fragment, { children: [_jsx(MentionableTextarea, { className: "border-border w-full resize-none rounded border p-1 text-sm", rows: 3, value: draft, onChange: setDraft, "data-testid": `edit-textarea-${comment.id}`, onClick: (e) => e.stopPropagation(), search: mentionSearch }), _jsxs("div", { className: "flex gap-1", children: [_jsx("button", { type: "button", className: "bg-primary rounded px-2 py-1 text-xs font-medium text-white", onClick: async (e) => {
                                    e.stopPropagation();
                                    const ok = await onEdit(comment.id, draft);
                                    if (ok)
                                        setEditing(false);
                                }, "data-testid": `edit-save-${comment.id}`, children: "Save" }), _jsx("button", { type: "button", className: "border-border rounded border px-2 py-1 text-xs", onClick: (e) => {
                                    e.stopPropagation();
                                    setDraft(comment.body);
                                    setEditing(false);
                                }, "data-testid": `edit-cancel-${comment.id}`, children: "Cancel" })] })] })) : (_jsx("p", { className: "text-foreground text-sm whitespace-pre-wrap", "data-testid": `comment-body-${comment.id}`, children: comment.body })), !editing && (_jsxs("div", { className: "flex flex-wrap gap-1 text-xs", children: [canMutate && (_jsx("button", { type: "button", className: "text-muted-foreground hover:text-foreground", onClick: (e) => {
                            e.stopPropagation();
                            setEditing(true);
                        }, "data-testid": `edit-${comment.id}`, children: "Edit" })), onToggleResolve && (_jsx("button", { type: "button", className: "text-muted-foreground hover:text-foreground", onClick: (e) => {
                            e.stopPropagation();
                            onToggleResolve();
                        }, "data-testid": `resolve-${comment.id}`, children: comment.isResolved ? 'Reopen' : 'Resolve' })), canMutate && (_jsx("button", { type: "button", className: "text-destructive hover:text-destructive", onClick: (e) => {
                            e.stopPropagation();
                            onDelete();
                        }, "data-testid": `delete-${comment.id}`, children: "Delete" }))] }))] }));
}
/**
 * Cheap "n seconds / minutes / hours / days ago" formatter. We avoid
 * importing a full date-fns dependency just for this view; the panel is
 * fine being approximate.
 */
function formatRelative(iso) {
    const t = Date.parse(iso);
    if (Number.isNaN(t))
        return iso;
    const deltaSec = Math.max(0, Math.round((Date.now() - t) / 1000));
    if (deltaSec < 60)
        return `${deltaSec}s ago`;
    const m = Math.round(deltaSec / 60);
    if (m < 60)
        return `${m}m ago`;
    const h = Math.round(m / 60);
    if (h < 24)
        return `${h}h ago`;
    const d = Math.round(h / 24);
    return `${d}d ago`;
}
// Re-export for tests that build their own CommentsApi.
export { formatRelative as _formatRelative };
//# sourceMappingURL=CommentSidePanel.js.map