"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CnAiChatShell = void 0;
const i18n_1 = __importDefault(require("../../locales/i18n.js"));
const React = __importStar(require("react"));
const react_markdown_1 = __importDefault(require("react-markdown"));
const remark_gfm_1 = __importDefault(require("remark-gfm"));
const cn_sender_1 = require("../../cn-sender");
const use_typewriter_1 = require("../../cn-ai-agent/hooks/use-typewriter");
const markdown_components_1 = require("../../cn-ai-agent/view/components/markdown-components");
const constants_1 = require("../types/constants");
const use_chat_runtime_1 = require("../hooks/use-chat-runtime");
require("./cn-ai-chat-shell.scss");
// ---------------------------------------------------------------------------
// MessageItem (internal)
// ---------------------------------------------------------------------------
const MessageItem = ({ message, isGenerating = false }) => {
    const typewriterContent = (0, use_typewriter_1.useTypewriter)(message.content, {
        speed: 30,
        enabled: message.role === 'assistant' && isGenerating,
    });
    const displayContent = message.role === 'assistant' && isGenerating ? typewriterContent : message.content;
    if (message.role === 'assistant') {
        return (React.createElement("div", { className: "cn-ai-chat-shell__message cn-ai-chat-shell__message--assistant" },
            React.createElement("div", { className: "cn-ai-chat-shell__message-content" },
                React.createElement(react_markdown_1.default, { remarkPlugins: [remark_gfm_1.default], components: markdown_components_1.markdownComponents }, displayContent || ''))));
    }
    return (React.createElement("div", { className: "cn-ai-chat-shell__message cn-ai-chat-shell__message--user" },
        React.createElement("div", { className: "cn-ai-chat-shell__message-text" }, displayContent || '')));
};
MessageItem.displayName = 'CnAiChatShellMessageItem';
const isMessageDone = (status) => status === 'done' || status === undefined;
const QuickReplies = ({ message, disabled, onReplyClick }) => {
    const { quickReplies, quickRepliesConsumed, status, role } = message;
    if (role !== 'assistant' ||
        !isMessageDone(status) ||
        quickRepliesConsumed ||
        !(quickReplies === null || quickReplies === void 0 ? void 0 : quickReplies.length)) {
        return null;
    }
    return (React.createElement("div", { className: "cn-ai-chat-shell__quick-replies" }, quickReplies.map((reply) => (React.createElement("button", { key: reply.id, type: "button", className: "cn-ai-chat-shell__quick-reply-btn", disabled: disabled || reply.disabled, onClick: () => onReplyClick(reply) }, reply.label)))));
};
QuickReplies.displayName = 'CnAiChatShellQuickReplies';
// ---------------------------------------------------------------------------
// 历史与当前会话分割线
// ---------------------------------------------------------------------------
const NEW_SESSION_SCROLL_SELECTOR = '[data-cn-ai-chat-new-session-anchor]';
/** 将分割线顶边对齐到消息滚动容器可视区域顶部（不依赖 scrollIntoView，避免误滚外层） */
function scrollNewSessionDividerToTopOfMessages(scrollContainer, divider) {
    const top = divider.getBoundingClientRect().top -
        scrollContainer.getBoundingClientRect().top +
        scrollContainer.scrollTop;
    const next = Math.max(0, Math.floor(top));
    Object.assign(scrollContainer, { scrollTop: next });
}
const NewSessionDivider = () => (React.createElement("div", { className: "cn-ai-chat-shell__session-divider", role: "separator", "data-cn-ai-chat-new-session-anchor": "", "aria-label": i18n_1.default.get({
        id: 'TheFollowingIsTheCurrentNewSessi_1804967384',
        dm: '以下为当前新会话',
    }) },
    React.createElement("span", { className: "cn-ai-chat-shell__session-divider-line", "aria-hidden": true }),
    React.createElement("span", { className: "cn-ai-chat-shell__session-divider-text" }, i18n_1.default.get({ id: 'NewConversation', dm: '新会话' })),
    React.createElement("span", { className: "cn-ai-chat-shell__session-divider-line", "aria-hidden": true })));
NewSessionDivider.displayName = 'CnAiChatShellNewSessionDivider';
// ---------------------------------------------------------------------------
// CnAiChatShell
// ---------------------------------------------------------------------------
const CnAiChatShell = (props) => {
    const { adapter, context, initialConversationId, pollingConfig, senderProps, onEvent, autoInit, disableSenderBeforeInit = true, initWhen, onInitStatusChange, enableHistory, historyPageSize, historyThreshold = constants_1.DEFAULT_HISTORY_THRESHOLD, messages: controlledMessages, onMessagesChange, onQuickReplyClick, renderMessage, renderMessageActions, renderEmpty, renderHeader, renderFooterExtra, className, ...rest } = props;
    const senderRef = React.useRef(null);
    const scrollRef = React.useRef(null);
    const { messages, isPolling, generatingMessageId, handleSubmit, handleCancel, setMessages, initStatus, retryInit, isHistoryLoading, historyHasMore, loadHistory, historySessionDividerBeforeMessageId, } = (0, use_chat_runtime_1.useChatRuntime)({
        adapter,
        context,
        initialConversationId,
        pollingConfig,
        onEvent,
        controlledMessages,
        onMessagesChange,
        autoInit,
        initWhen,
        enableHistory,
        historyPageSize,
    });
    React.useEffect(() => {
        onInitStatusChange === null || onInitStatusChange === void 0 ? void 0 : onInitStatusChange(initStatus, { retryInit });
    }, [initStatus, onInitStatusChange, retryInit]);
    // Track previous message count and first message id to distinguish prepend vs append
    const prevMsgSnapshotRef = React.useRef({
        count: 0,
    });
    // Anchor for restoring scroll position after history prepend
    const scrollAnchorRef = React.useRef(null);
    /** 仅在「进入时内容未撑满可视区」类场景自动滚到新会话一次，避免用户已滚到顶读历史时被抢走滚动位置 */
    const didAutoScrollToNewSessionRef = React.useRef(false);
    React.useEffect(() => {
        if (!historySessionDividerBeforeMessageId) {
            didAutoScrollToNewSessionRef.current = false;
        }
    }, [historySessionDividerBeforeMessageId]);
    /** 仅当锚点消息上方确有历史消息时展示「新会话」分割线，避免首进无历史时误显 */
    const historyDividerAnchorIndex = React.useMemo(() => {
        if (!enableHistory || !historySessionDividerBeforeMessageId)
            return -1;
        return messages.findIndex((m) => m.id === historySessionDividerBeforeMessageId);
    }, [enableHistory, messages, historySessionDividerBeforeMessageId]);
    const shouldShowHistorySessionDivider = enableHistory && !!historySessionDividerBeforeMessageId && historyDividerAnchorIndex > 0;
    React.useLayoutEffect(() => {
        var _a;
        const el = scrollRef.current;
        if (!el)
            return;
        const prev = prevMsgSnapshotRef.current;
        const firstId = (_a = messages[0]) === null || _a === void 0 ? void 0 : _a.id;
        const isPrepend = messages.length > prev.count && firstId !== prev.firstId && prev.count > 0;
        if (isPrepend && scrollAnchorRef.current) {
            const anchor = scrollAnchorRef.current;
            el.scrollTop = el.scrollHeight - anchor.scrollHeight + anchor.scrollTop;
            scrollAnchorRef.current = null;
            const contentHadNoScroll = anchor.scrollHeight <= el.clientHeight + historyThreshold;
            const shouldFocusNewSession = shouldShowHistorySessionDivider &&
                !didAutoScrollToNewSessionRef.current &&
                contentHadNoScroll;
            if (shouldFocusNewSession) {
                const focusEl = el.querySelector(NEW_SESSION_SCROLL_SELECTOR);
                if (focusEl) {
                    didAutoScrollToNewSessionRef.current = true;
                    scrollNewSessionDividerToTopOfMessages(el, focusEl);
                }
            }
        }
        else if (!isPrepend) {
            el.scrollTop = el.scrollHeight;
        }
        prevMsgSnapshotRef.current = { count: messages.length, firstId };
    }, [
        messages,
        historySessionDividerBeforeMessageId,
        historyThreshold,
        shouldShowHistorySessionDivider,
    ]);
    /**
     * 当消息区内容高度不足以产生滚动条时，用户无法通过 onScroll 触顶，
     * 历史加载永远不会开始。此时若仍有更早消息，自动连续拉取直到出现滚动或已无更多。
     */
    React.useEffect(() => {
        if (!enableHistory || !historyHasMore || isHistoryLoading)
            return;
        const el = scrollRef.current;
        if (!el || messages.length === 0)
            return;
        const fitsInViewport = el.scrollHeight <= el.clientHeight + historyThreshold;
        if (!fitsInViewport)
            return;
        scrollAnchorRef.current = {
            scrollHeight: el.scrollHeight,
            scrollTop: el.scrollTop,
        };
        loadHistory();
    }, [messages, enableHistory, historyHasMore, isHistoryLoading, historyThreshold, loadHistory]);
    // Scroll handler: trigger history load when near top
    const handleScroll = React.useCallback(() => {
        const el = scrollRef.current;
        if (!el || !enableHistory || isHistoryLoading || !historyHasMore)
            return;
        if (el.scrollTop <= historyThreshold) {
            scrollAnchorRef.current = {
                scrollHeight: el.scrollHeight,
                scrollTop: el.scrollTop,
            };
            loadHistory();
        }
    }, [enableHistory, isHistoryLoading, historyHasMore, historyThreshold, loadHistory]);
    const onSenderSubmit = React.useCallback((value) => {
        handleSubmit(value);
        setTimeout(() => { var _a; return (_a = senderRef.current) === null || _a === void 0 ? void 0 : _a.clear(); }, 0);
    }, [handleSubmit]);
    // Gap 3: 仅在 pending 时禁用，failed 时不锁死用户
    const senderDisabled = autoInit && disableSenderBeforeInit && initStatus === 'pending';
    const quickReplyDisabled = isPolling || !!senderDisabled;
    const submitQuickReply = React.useCallback((message, reply) => {
        var _a;
        if (quickReplyDisabled)
            return;
        const prompt = (_a = reply.prompt) !== null && _a !== void 0 ? _a : reply.label;
        onQuickReplyClick === null || onQuickReplyClick === void 0 ? void 0 : onQuickReplyClick({ messageId: message.id, reply, prompt });
        onEvent === null || onEvent === void 0 ? void 0 : onEvent({
            name: 'quick_reply_click',
            payload: { messageId: message.id, replyId: reply.id, prompt },
        });
        setMessages((prev) => prev.map((m) => (m.id === message.id ? { ...m, quickRepliesConsumed: true } : m)));
        handleSubmit(prompt);
    }, [quickReplyDisabled, onQuickReplyClick, onEvent, setMessages, handleSubmit]);
    const rootCls = ['cn-ai-chat-shell', className].filter(Boolean).join(' ');
    return (React.createElement("div", { className: rootCls, ...rest },
        renderHeader && React.createElement("div", { className: "cn-ai-chat-shell__header" }, renderHeader()),
        React.createElement("div", { className: "cn-ai-chat-shell__messages", ref: scrollRef, onScroll: handleScroll },
            enableHistory && isHistoryLoading && (React.createElement("div", { className: "cn-ai-chat-shell__history-loading" }, i18n_1.default.get({ id: 'LoadEarlierMessages', dm: '加载更早消息…' }))),
            enableHistory &&
                !historyHasMore &&
                messages.length > 0 &&
                shouldShowHistorySessionDivider && (React.createElement("div", { className: "cn-ai-chat-shell__history-no-more" }, i18n_1.default.get({ id: 'NoMoreHistoricalNews', dm: '没有更多历史消息了' }))),
            messages.length === 0 && (React.createElement("div", { className: "cn-ai-chat-shell__empty" }, renderEmpty
                ? renderEmpty()
                : i18n_1.default.get({ id: 'LetsStartAConversation', dm: '开始对话吧' }))),
            messages.map((msg, idx) => {
                const showNewSessionDivider = shouldShowHistorySessionDivider && msg.id === historySessionDividerBeforeMessageId;
                return renderMessage ? (React.createElement(React.Fragment, { key: msg.id },
                    showNewSessionDivider && React.createElement(NewSessionDivider, null),
                    renderMessage(msg, idx))) : (React.createElement(React.Fragment, { key: msg.id },
                    showNewSessionDivider && React.createElement(NewSessionDivider, null),
                    React.createElement(MessageItem, { message: msg, isGenerating: msg.role === 'assistant' && msg.id === generatingMessageId }),
                    renderMessageActions ? (renderMessageActions(msg, idx, {
                        handleSubmit,
                        submitQuickReply: (reply) => submitQuickReply(msg, reply),
                    })) : (React.createElement(QuickReplies, { message: msg, disabled: quickReplyDisabled, onReplyClick: (reply) => submitQuickReply(msg, reply) }))));
            })),
        React.createElement("div", { className: "cn-ai-chat-shell__input-area" },
            React.createElement("div", { className: "cn-ai-chat-shell__input-area-content" },
                React.createElement(cn_sender_1.CnSender, { ref: senderRef, className: "cn-ai-chat-shell__sender", placeholder: i18n_1.default.get({ id: 'TellMeWhatYouWantToDo', dm: '告诉我你想做什么' }), suffix: false, footer: (actionNode) => (React.createElement("div", { className: "cn-ai-chat-shell__sender-footer" }, actionNode)), loading: isPolling, disabled: senderDisabled, onCancel: handleCancel, onSubmit: onSenderSubmit, autoSize: { minRows: 1, maxRows: 8 }, ...senderProps }))),
        renderFooterExtra && (React.createElement("div", { className: "cn-ai-chat-shell__footer-extra" }, renderFooterExtra()))));
};
exports.CnAiChatShell = CnAiChatShell;
exports.CnAiChatShell.displayName = 'CnAiChatShell';
