import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useRef, useState, useMemo, memo, useCallback } from 'react';
import { ArrowDown } from 'lucide-react';
import { useQueryClient } from '@tanstack/react-query';
import { AssistantMessageGroup } from './AssistantMessageGroup';
import { UserMessageGroup } from './UserMessageGroup';
import { SessionHeader } from '../sessions/SessionHeader';
import { LeanHeader } from '../sessions/LeanHeader';
import { TopupApprovalCard } from './TopupApprovalCard';
import { useTopupApprovalStore } from '../../stores/topupApprovalStore';
import { apiClient } from '../../lib/api-client';
import { toast } from '../../stores/toastStore';
export const MessageThread = memo(function MessageThread({ messages, session, sessionId, isGenerating, disableAutoScroll = false, onSelectSession, }) {
    const queryClient = useQueryClient();
    const bottomRef = useRef(null);
    const scrollContainerRef = useRef(null);
    const sessionHeaderRef = useRef(null);
    const [autoScroll, setAutoScroll] = useState(true);
    const autoScrollRef = useRef(true);
    const [showLeanHeader, setShowLeanHeader] = useState(false);
    const userScrollingRef = useRef(false);
    const userScrollTimeoutRef = useRef(undefined);
    const targetScrollRef = useRef(0);
    const animationFrameRef = useRef(undefined);
    const initialScrollDoneRef = useRef(false);
    const lastSessionIdRef = useRef(session?.id);
    const prevMessagesLengthRef = useRef(messages.length);
    const prevIsGeneratingRef = useRef(isGenerating);
    const lastScrollHeightRef = useRef(0);
    const lastScrollTopRef = useRef(0);
    const pendingTopup = useTopupApprovalStore((s) => s.pendingTopup);
    const clearPendingTopup = useTopupApprovalStore((s) => s.clearPendingTopup);
    const showTopupApproval = pendingTopup && pendingTopup.sessionId === sessionId;
    const handleScroll = useCallback(() => {
        const container = scrollContainerRef.current;
        if (!container)
            return;
        const { scrollTop, scrollHeight, clientHeight } = container;
        const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
        const scrollHeightIncreased = scrollHeight > lastScrollHeightRef.current;
        const userScrolledUp = scrollTop < lastScrollTopRef.current - 5;
        lastScrollHeightRef.current = scrollHeight;
        lastScrollTopRef.current = scrollTop;
        if (distanceFromBottom < 100) {
            autoScrollRef.current = true;
            setAutoScroll(true);
        }
        else if (userScrolledUp) {
            autoScrollRef.current = false;
            setAutoScroll(false);
            userScrollingRef.current = true;
        }
        else if (!scrollHeightIncreased && autoScrollRef.current) {
            autoScrollRef.current = false;
            setAutoScroll(false);
        }
        if (userScrolledUp ||
            (!autoScrollRef.current && distanceFromBottom >= 100)) {
            if (userScrollTimeoutRef.current) {
                clearTimeout(userScrollTimeoutRef.current);
            }
            userScrollTimeoutRef.current = setTimeout(() => {
                userScrollingRef.current = false;
            }, 150);
        }
        const headerElement = sessionHeaderRef.current;
        if (headerElement) {
            const headerRect = headerElement.getBoundingClientRect();
            const containerRect = container.getBoundingClientRect();
            setShowLeanHeader(headerRect.bottom < containerRect.top);
        }
    }, []);
    // Immediate scroll to bottom on initial load or session change
    useEffect(() => {
        if (disableAutoScroll)
            return;
        const sessionChanged = session?.id !== lastSessionIdRef.current;
        lastSessionIdRef.current = session?.id;
        if (sessionChanged) {
            initialScrollDoneRef.current = false;
        }
        if (!initialScrollDoneRef.current && messages.length > 0) {
            initialScrollDoneRef.current = true;
            const container = scrollContainerRef.current;
            if (container) {
                container.scrollTop = container.scrollHeight;
            }
        }
    }, [messages.length, session?.id, disableAutoScroll]);
    useEffect(() => {
        if (disableAutoScroll)
            return;
        const justStartedGenerating = isGenerating && !prevIsGeneratingRef.current;
        const messagesAdded = messages.length > prevMessagesLengthRef.current;
        prevIsGeneratingRef.current = isGenerating;
        prevMessagesLengthRef.current = messages.length;
        // Scroll to bottom when generation starts (user just sent a message)
        if (justStartedGenerating) {
            userScrollingRef.current = false;
            autoScrollRef.current = true;
            setAutoScroll(true);
            requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                    const container = scrollContainerRef.current;
                    if (container) {
                        container.scrollTop = container.scrollHeight;
                    }
                });
            });
        }
        else if (messagesAdded && !userScrollingRef.current && !isGenerating) {
            autoScrollRef.current = true;
            setAutoScroll(true);
        }
    }, [messages.length, isGenerating, disableAutoScroll]);
    // biome-ignore lint/correctness/useExhaustiveDependencies: messages dep needed for streaming content updates
    useEffect(() => {
        if (disableAutoScroll)
            return;
        const container = scrollContainerRef.current;
        if (!container || !autoScroll || userScrollingRef.current)
            return;
        targetScrollRef.current = container.scrollHeight - container.clientHeight;
        const animate = () => {
            const el = scrollContainerRef.current;
            if (!el || userScrollingRef.current)
                return;
            const current = el.scrollTop;
            const target = el.scrollHeight - el.clientHeight;
            const diff = target - current;
            if (Math.abs(diff) < 1) {
                el.scrollTop = el.scrollHeight - el.clientHeight;
                return;
            }
            // If very close, just snap to bottom
            if (Math.abs(diff) < 10) {
                el.scrollTop = el.scrollHeight - el.clientHeight;
                return;
            }
            el.scrollTop = current + diff * 0.15;
            animationFrameRef.current = requestAnimationFrame(animate);
        };
        if (animationFrameRef.current) {
            cancelAnimationFrame(animationFrameRef.current);
        }
        animationFrameRef.current = requestAnimationFrame(animate);
    }, [messages, autoScroll]);
    useEffect(() => {
        return () => {
            if (userScrollTimeoutRef.current) {
                clearTimeout(userScrollTimeoutRef.current);
            }
            if (animationFrameRef.current) {
                cancelAnimationFrame(animationFrameRef.current);
            }
        };
    }, []);
    const scrollToBottom = () => {
        userScrollingRef.current = false;
        autoScrollRef.current = true;
        setAutoScroll(true);
        const container = scrollContainerRef.current;
        if (container) {
            container.scrollTop = container.scrollHeight;
        }
    };
    // Memoize filtered messages to avoid recalculating on every render
    const filteredMessages = useMemo(() => {
        return messages.filter((message) => message.role !== 'system');
    }, [messages]);
    // Create a retry handler for error messages
    const createRetryHandler = useCallback((messageId) => {
        return async () => {
            if (!sessionId)
                return;
            if (!messageId)
                return;
            // Optimistically update the message to pending state, removing only error parts
            queryClient.setQueryData(['messages', sessionId], (oldMessages) => {
                if (!oldMessages)
                    return oldMessages;
                return oldMessages.map((msg) => {
                    if (msg.id !== messageId)
                        return msg;
                    const partsToKeep = msg.parts?.filter((part) => {
                        if (part.type === 'error')
                            return false;
                        if (part.type === 'tool_call' && part.toolName === 'finish')
                            return false;
                        return true;
                    }) ?? [];
                    return {
                        ...msg,
                        status: 'pending',
                        parts: partsToKeep,
                        error: null,
                    };
                });
            });
            try {
                await apiClient.retryMessage(sessionId, messageId);
            }
            catch (error) {
                toast.error(error instanceof Error ? error.message : 'Failed to retry');
            }
        };
    }, [sessionId, queryClient]);
    if (messages.length === 0) {
        return (_jsx("div", { className: "flex-1 flex items-center justify-center text-muted-foreground text-sm", children: "No messages yet. Start a conversation below." }));
    }
    return (_jsxs("div", { className: "absolute inset-0 flex flex-col", children: [session && (_jsx(LeanHeader, { session: session, isVisible: showLeanHeader, isGenerating: isGenerating, onNavigateToSession: onSelectSession })), _jsxs("div", { ref: scrollContainerRef, className: "flex-1 overflow-y-auto", onScroll: handleScroll, children: [_jsx("div", { ref: sessionHeaderRef, children: session && (_jsx(SessionHeader, { session: session, onNavigateToSession: onSelectSession })) }), _jsx("div", { className: "p-6 pb-64", children: _jsxs("div", { className: "max-w-3xl mx-auto space-y-6", children: [filteredMessages.map((message, idx) => {
                                    const prevMessage = filteredMessages[idx - 1];
                                    const nextMessage = filteredMessages[idx + 1];
                                    const isLastMessage = idx === filteredMessages.length - 1;
                                    if (message.role === 'user') {
                                        const nextAssistantMessage = nextMessage && nextMessage.role === 'assistant'
                                            ? nextMessage
                                            : undefined;
                                        return (_jsx(UserMessageGroup, { sessionId: sessionId, message: message, isFirst: idx === 0, nextAssistantMessageId: nextAssistantMessage?.id }, message.id));
                                    }
                                    if (message.role === 'assistant') {
                                        const showHeader = !prevMessage || prevMessage.role !== 'assistant';
                                        const nextIsAssistant = nextMessage && nextMessage.role === 'assistant';
                                        return (_jsx(AssistantMessageGroup, { sessionId: sessionId, message: message, showHeader: showHeader, hasNextAssistantMessage: nextIsAssistant, isLastMessage: isLastMessage, onBranchCreated: onSelectSession, onRetry: createRetryHandler(message.id) }, message.id));
                                    }
                                    return null;
                                }), showTopupApproval && pendingTopup && (_jsx("div", { className: "py-4", children: _jsx(TopupApprovalCard, { pendingTopup: pendingTopup, onMethodSelected: () => clearPendingTopup(), onCancel: () => clearPendingTopup() }) })), _jsx("div", { ref: bottomRef })] }) })] }), !autoScroll && (_jsxs("button", { type: "button", onClick: scrollToBottom, className: "absolute bottom-36 left-1/2 -translate-x-1/2 flex items-center gap-2 px-4 py-2 bg-background border border-border rounded-full shadow-lg hover:bg-muted/50 transition-all text-sm text-foreground z-10", children: [_jsx(ArrowDown, { className: "w-4 h-4" }), _jsx("span", { children: "Scroll to bottom" })] }))] }));
});
//# sourceMappingURL=MessageThread.js.map