"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 });
// import { CaretDownFilled } from '@ant-design/icons';
const cn_ui_1 = require("@cainiaofe/cn-ui");
const classnames_1 = __importDefault(require("classnames"));
const react_1 = __importStar(require("react"));
const react_dom_1 = require("react-dom");
const warning_1 = __importDefault(require("./warning"));
const context_1 = require("../context");
const use_cursor_1 = __importDefault(require("../hooks/use-cursor"));
const use_input_height_1 = __importDefault(require("../hooks/use-input-height"));
const use_slot_builder_1 = __importDefault(require("../hooks/use-slot-builder"));
const use_slot_config_state_1 = __importDefault(require("../hooks/use-slot-config-state"));
const skill_area_1 = __importDefault(require("./skill-area"));
const SlotTextArea = react_1.default.forwardRef((_unusedProps, ref) => {
    var _a;
    const { form, formProps, onChange, onKeyUp, onKeyDown, onPaste, onPasteFile, disabled, readOnly, submitType = 'enter', prefixCls: customizePrefixCls, styles = {}, classNames = {}, autoSize, triggerSend, placeholder, onFocus, onBlur, slotConfig, schema, skill, popupTriggerKey = '/', // 新增，默认值为 "/"
    customPopupProps, // 新增
    // 从 restProps 剥离，避免 spread 到 contentEditable 根节点；Formily 注册项由 FieldInputForm 消费
    components: _senderComponents, ...restProps } = react_1.default.useContext(context_1.SenderContext);
    // ============================= MISC =============================
    const prefixCls = `cn-sender`;
    const inputCls = `${prefixCls}-input`;
    // ============================ Refs =============================
    const editableRef = (0, react_1.useRef)(null);
    const slotDomMap = (0, react_1.useRef)(new Map());
    const isCompositionRef = (0, react_1.useRef)(false);
    const keyLockRef = (0, react_1.useRef)(false);
    const lastSelectionRef = (0, react_1.useRef)(null);
    const skillDomRef = (0, react_1.useRef)(null);
    const skillRef = (0, react_1.useRef)(null);
    // formMap 用于缓存字段的 labels、values 和 options，结构为 {name: {labels:[], values:[], options:[]}}
    const [formMap, setFormMap] = (0, react_1.useState)({});
    // 使用 ref 存储最新的 formMap，供 formatResult 等闭包函数使用
    const formMapRef = (0, react_1.useRef)({});
    // ============================ Style =============================
    const mergeStyle = { ...styles.input };
    const inputHeightStyle = (0, use_input_height_1.default)(mergeStyle, autoSize, editableRef);
    // ============================ Attrs =============================
    const inputProps = {
        ref: editableRef,
    };
    // ============================ State =============================
    const [slotConfigMap, { getSlotValues, setSlotValues, getNodeInfo, mergeSlotConfig, getNodeTextValue },] = (0, use_slot_config_state_1.default)(slotConfig, formMapRef);
    const [slotPlaceholders, setSlotPlaceholders] = (0, react_1.useState)(new Map());
    const [skillPlaceholders, setSkillPlaceholders] = (0, react_1.useState)(null);
    // Schema 弹层相关状态
    const [showSchemaMenu, setShowSchemaMenu] = (0, react_1.useState)(false);
    const [schemaMenuPosition, setSchemaMenuPosition] = (0, react_1.useState)(null);
    const [schemaSearchKeyword, setSchemaSearchKeyword] = (0, react_1.useState)('');
    const [selectedField, setSelectedField] = (0, react_1.useState)(null);
    const [fieldInputValue, setFieldInputValue] = (0, react_1.useState)('');
    // Store cursor position when popup is opened
    const [fieldInsertPosition, setFieldInsertPosition] = (0, react_1.useState)(null);
    // Schema 弹层内容区 ref，用于让 CnAsyncSelect 下拉挂载到该容器内，避免被遮罩挡住导致点击无效
    const schemaMenuContentRef = (0, react_1.useRef)(null);
    // 点击外部关闭 Schema 弹层（遮罩用了 pointer-events: none，需用 document 监听）
    (0, react_1.useEffect)(() => {
        if (!showSchemaMenu)
            return;
        const onDocClick = (e) => {
            var _a, _b, _c, _d, _e;
            const target = e.target;
            if ((_a = schemaMenuContentRef.current) === null || _a === void 0 ? void 0 : _a.contains(target))
                return;
            // 点击在下拉 overlay 内不关闭（cn-ui 下拉可能带 overlay 类名）
            if (((_c = (_b = target).closest) === null || _c === void 0 ? void 0 : _c.call(_b, '.cn-next-overlay-wrapper')) ||
                ((_e = (_d = target).closest) === null || _e === void 0 ? void 0 : _e.call(_d, '[class*="next-overlay"]'))) {
                return;
            }
            setShowSchemaMenu(false);
            setSchemaMenuPosition(null);
            setSelectedField(null);
            setFieldInputValue('');
            setSchemaSearchKeyword('');
            setFieldInsertPosition(null);
        };
        document.addEventListener('mousedown', onDocClick, true);
        return () => document.removeEventListener('mousedown', onDocClick, true);
    }, [showSchemaMenu]);
    // 使用 ref 保存最新值，避免闭包问题
    const selectedFieldRef = (0, react_1.useRef)(selectedField);
    const fieldInputValueRef = (0, react_1.useRef)(fieldInputValue);
    // 同步更新 ref
    react_1.default.useEffect(() => {
        selectedFieldRef.current = selectedField;
    }, [selectedField]);
    react_1.default.useEffect(() => {
        fieldInputValueRef.current = fieldInputValue;
    }, [fieldInputValue]);
    // 同步 formMap state 到 formMapRef
    react_1.default.useEffect(() => {
        formMapRef.current = formMap;
    }, [formMap]);
    // 更新 formMap 的回调函数
    const onFormMapUpdate = react_1.default.useCallback((key, data) => {
        setFormMap((prev) => {
            const next = { ...prev, [key]: data };
            formMapRef.current = next;
            return next;
        });
    }, []);
    /**
     * CnAiFilter 与字段弹层共用一个 Formily 实例（filterForm）。
     * DOM / formMap 删除 tag 后若不同步清理 form.values，再次「/」插入同名字段会带上一次遗留值；
     * 联动字段（x-reactions / target）也会残留对侧状态。
     */
    const clearSharedFilterFormValues = react_1.default.useCallback((fieldKeys) => {
        if (!form || fieldKeys.length === 0)
            return;
        const patch = {};
        fieldKeys.forEach((k) => {
            if (k)
                patch[k] = undefined;
        });
        try {
            form.setValues(patch);
        }
        catch {
            /* noop */
        }
    }, [form]);
    // ============================ Cursor =============================
    const { setEndCursor, setStartCursor, setAllSelectCursor, setCursorPosition, setSlotFocus, setAfterNodeFocus, getTextBeforeCursor, removeAllRanges, getRange, getInsertPosition, getEndRange, getStartRange, getSelection, copySelectionString, getCleanedText, } = (0, use_cursor_1.default)({
        prefixCls,
        getSlotDom: (key) => slotDomMap.current.get(key),
        slotConfigMap,
        getNodeInfo,
        getEditorValue: () => getEditorValue(),
    });
    // ============================ Slot Builder =============================
    const { buildSkillSpan, buildEditSlotSpan, buildSlotSpan, buildSpaceSpan, getSlotDom, saveSlotDom, getSlotLastDom, } = (0, use_slot_builder_1.default)({
        prefixCls,
        placeholder,
        slotDomMap,
        slotConfigMap,
    });
    // ============================ Methods =============================
    /**
     * 清理已从 DOM 中删除的 slot 对应的内存状态
     *
     * 背景：用户通过选中+Delete、粘贴覆盖等方式删除 slot 时，DOM 被浏览器直接删除，
     * 但 slotValues/formMap 等内存状态不会自动清理。如果不清理，再次插入同一字段时会显示旧值。
     *
     * 原理：扫描 DOM 中实际存在的 slot keys，删除内存中多余的条目。
     */
    const cleanupRemovedSlots = () => {
        const editableDom = editableRef.current;
        if (!editableDom)
            return;
        // 收集 DOM 中实际存在的 slot keys
        const existingSlotKeys = new Set();
        editableDom.querySelectorAll('[data-slot-key]').forEach((element) => {
            const slotKey = element.getAttribute('data-slot-key');
            if (slotKey) {
                existingSlotKeys.add(slotKey);
            }
        });
        const slotSnapshot = getSlotValues();
        const keysMissingFromDom = [
            ...new Set([
                ...Object.keys(formMapRef.current).filter((key) => !existingSlotKeys.has(key)),
                ...Object.keys(slotSnapshot).filter((key) => !existingSlotKeys.has(key)),
            ]),
        ];
        clearSharedFilterFormValues(keysMissingFromDom);
        // 清理 slotValues 中 DOM 已不存在的 slot
        setSlotValues((prev) => {
            const keysToRemove = Object.keys(prev).filter((key) => !existingSlotKeys.has(key));
            if (keysToRemove.length === 0)
                return prev;
            const next = { ...prev };
            keysToRemove.forEach((key) => delete next[key]);
            return next;
        });
        // 清理 formMap 中 DOM 已不存在的 slot
        setFormMap((prev) => {
            const keysToRemove = Object.keys(prev).filter((key) => !existingSlotKeys.has(key));
            if (keysToRemove.length === 0)
                return prev;
            const next = { ...prev };
            keysToRemove.forEach((key) => delete next[key]);
            formMapRef.current = next;
            return next;
        });
        // 清理 slotPlaceholders 中 DOM 已不存在的 slot
        setSlotPlaceholders((prev) => {
            const keysToRemove = Array.from(prev.keys()).filter((key) => !existingSlotKeys.has(key));
            if (keysToRemove.length === 0)
                return prev;
            const next = new Map(prev);
            keysToRemove.forEach((key) => next.delete(key));
            return next;
        });
        // 清理 slotDomMap 中 DOM 已不存在的 slot
        const slotDomKeysToRemove = Array.from(slotDomMap.current.keys()).filter((key) => !existingSlotKeys.has(key));
        slotDomKeysToRemove.forEach((key) => slotDomMap.current.delete(key));
    };
    const triggerValueChange = (e) => {
        // 在获取值之前，先清理已删除的 slot 的状态
        cleanupRemovedSlots();
        const newValue = getEditorValue();
        if (skillDomRef.current) {
            if (!(newValue === null || newValue === void 0 ? void 0 : newValue.value) && newValue.slotConfig.length === 0 && placeholder) {
                skillDomRef.current.setAttribute('contenteditable', 'true');
                skillDomRef.current.classList.add(`${prefixCls}-skill-empty`);
            }
            else {
                skillDomRef.current.setAttribute('contenteditable', 'false');
                skillDomRef.current.classList.remove(`${prefixCls}-skill-empty`);
            }
        }
        onChange === null || onChange === void 0 ? void 0 : onChange(newValue.value, e, newValue.slotConfig, newValue.skill);
    };
    const updateSlot = (key, value, e) => {
        const slotDom = getSlotDom(key);
        const config = slotConfigMap.get(key);
        setSlotValues((prev) => ({ ...prev, [key]: value }));
        if (slotDom && config) {
            const newReactNode = renderSlot(config, slotDom);
            setSlotPlaceholders((prev) => {
                const newMap = new Map(prev);
                newMap.set(key, newReactNode);
                return newMap;
            });
            // 触发 onChange 回调
            triggerValueChange(e);
        }
    };
    const renderSlot = (config, slotSpan) => {
        if (!config.key)
            return null;
        const slotValue = getSlotValues()[config.key];
        const renderContent = () => {
            var _a, _b, _c, _d, _e;
            switch (config.type) {
                case 'content':
                    // eslint-disable-next-line no-param-reassign
                    slotSpan.innerHTML = slotValue || '';
                    slotSpan.setAttribute('data-placeholder', ((_a = config.props) === null || _a === void 0 ? void 0 : _a.placeholder) || '');
                    return null;
                case 'input':
                    return (react_1.default.createElement(cn_ui_1.Input, { readOnly: readOnly, className: `${prefixCls}-slot-input`, placeholder: ((_b = config.props) === null || _b === void 0 ? void 0 : _b.placeholder) || '', "data-slot-input": config.key, size: "small", value: slotValue || '', tabIndex: 0, onKeyDown: onInternalKeyDown, onChange: (inputValue, e) => {
                            updateSlot(config.key, inputValue, e);
                        }, spellCheck: false }));
                case 'select':
                    return null;
                // return (
                //   <Dropdown
                //     disabled={readOnly}
                //     menu={{
                //       items: config.props?.options?.map((opt: any) => ({
                //         label: opt,
                //         key: opt,
                //       })),
                //       defaultSelectedKeys: config.props?.defaultValue ? [config.props.defaultValue] : [],
                //       selectable: true,
                //       onSelect: ({ key, domEvent }) => {
                //         updateSlot(config.key as string, key, domEvent as unknown as EventType);
                //       },
                //     }}
                //     trigger={['click']}
                //   >
                //     <span
                //       className={clsx(`${prefixCls}-slot-select`, {
                //         placeholder: !value,
                //         [`${prefixCls}-slot-select-selector-value`]: value,
                //       })}
                //     >
                //       <span
                //         data-placeholder={config.props?.placeholder}
                //         className={`${prefixCls}-slot-select-value`}
                //       >
                //         {value || ''}
                //       </span>
                //       <span className={`${prefixCls}-slot-select-arrow`}>
                //         <CaretDownFilled />
                //       </span>
                //     </span>
                //   </Dropdown>
                // );
                case 'tag':
                    return (react_1.default.createElement("span", { className: `${prefixCls}-slot-tag` }, ((_c = config.props) === null || _c === void 0 ? void 0 : _c.label) || ((_d = config.props) === null || _d === void 0 ? void 0 : _d.value) || ''));
                case 'custom':
                    return (_e = config.customRender) === null || _e === void 0 ? void 0 : _e.call(config, slotValue, (customValue) => {
                        updateSlot(config.key, customValue);
                    }, { disabled, readOnly }, config, formMapRef, onFormMapUpdate);
                default:
                    return null;
            }
        };
        return (0, react_dom_1.createPortal)(renderContent(), slotSpan);
    };
    const getSlotListNode = (slotConfigList) => {
        return slotConfigList.reduce((nodeList, config) => {
            if (config.type === 'text') {
                nodeList.push(document.createTextNode(config.value || ''));
                return nodeList;
            }
            const slotKey = config.key;
            (0, warning_1.default)(!!slotKey, 'Sender', `Slot key is missing: ${slotKey}`);
            if (slotKey) {
                let slotSpan;
                let slotDom;
                if (config.type === 'content') {
                    slotDom = buildEditSlotSpan(config);
                    const before = buildSpaceSpan(slotKey, 'before');
                    const after = buildSpaceSpan(slotKey, 'after');
                    slotSpan = [before, slotDom, after];
                    saveSlotDom(`${slotKey}_before`, before);
                    saveSlotDom(slotKey, slotDom);
                    saveSlotDom(`${slotKey}_after`, after);
                }
                else {
                    slotDom = buildSlotSpan(slotKey);
                    slotSpan = [slotDom];
                    saveSlotDom(slotKey, slotDom);
                }
                if (slotDom) {
                    const reactNode = renderSlot(config, slotDom);
                    if (reactNode) {
                        setSlotPlaceholders((ori) => {
                            const newMap = new Map(ori);
                            newMap.set(slotKey, reactNode);
                            return newMap;
                        });
                        nodeList.push(...slotSpan);
                    }
                }
            }
            return nodeList;
        }, []);
    };
    const getEditorValue = () => {
        const editableDom = editableRef.current;
        const emptyRes = { value: '', slotConfig: [], skill: undefined };
        if (!editableDom) {
            return emptyRes;
        }
        const { childNodes } = editableDom;
        if (childNodes.length === 0) {
            return emptyRes;
        }
        const hasSkill = !!skill;
        const result = new Array(childNodes.length);
        const currentSlotConfig = [];
        let currentSkillConfig;
        let resultIndex = 0;
        for (let i = 0; i < childNodes.length; i++) {
            const node = childNodes[i];
            const textValue = getNodeTextValue(node);
            result[resultIndex++] = textValue;
            if (node.nodeType === Node.TEXT_NODE) {
                if (textValue) {
                    currentSlotConfig.push({
                        type: 'text',
                        value: textValue,
                    });
                }
            }
            else if (node.nodeType === Node.ELEMENT_NODE) {
                const el = node;
                const nodeInfo = getNodeInfo(el);
                if (nodeInfo) {
                    const { skillKey, slotKey, nodeType } = nodeInfo;
                    if (skillKey && hasSkill) {
                        currentSkillConfig = skill;
                    }
                    if (slotKey && nodeType !== 'nbsp') {
                        const nodeConfig = slotConfigMap.get(slotKey);
                        if (nodeConfig) {
                            currentSlotConfig.push({ ...nodeConfig, value: textValue });
                        }
                    }
                }
            }
        }
        if (resultIndex === 0) {
            editableDom.innerHTML = '';
            skillDomRef.current = null;
            return emptyRes;
        }
        const finalValue = result.slice(0, resultIndex).join('');
        if (!currentSkillConfig) {
            skillDomRef.current = null;
        }
        return {
            value: finalValue,
            slotConfig: currentSlotConfig,
            formValue: formMapRef.current,
            skill: currentSkillConfig,
        };
    };
    const initClear = () => {
        var _a;
        const div = editableRef.current;
        if (!div)
            return;
        div.innerHTML = '';
        skillDomRef.current = null;
        skillRef.current = null;
        lastSelectionRef.current = null;
        removeAllRanges();
        (_a = slotDomMap === null || slotDomMap === void 0 ? void 0 : slotDomMap.current) === null || _a === void 0 ? void 0 : _a.clear();
    };
    const appendNodeList = (slotNodeList) => {
        slotNodeList.forEach((element) => {
            var _a;
            (_a = editableRef.current) === null || _a === void 0 ? void 0 : _a.appendChild(element);
        });
    };
    const removeSlot = (key, e) => {
        const editableDom = editableRef.current;
        if (!editableDom)
            return;
        editableDom.querySelectorAll(`[data-slot-key="${key}"]`).forEach((element) => {
            element.remove();
        });
        slotDomMap.current.delete(key);
        clearSharedFilterFormValues([key]);
        setSlotValues((prev) => {
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const { [key]: removed, ...rest } = prev;
            return rest;
        });
        setSlotPlaceholders((prev) => {
            const next = new Map(prev);
            next.delete(key);
            return next;
        });
        // 删除 formMap 中对应的值
        setFormMap((prev) => {
            const next = { ...prev };
            delete next[key];
            formMapRef.current = next;
            return next;
        });
        // 触发onChange回调
        triggerValueChange(e);
    };
    const insertSkill = () => {
        if (skill && skillRef.current !== skill) {
            removeSkill(false);
            skillRef.current = skill;
            const skillSpan = buildSkillSpan(skill.value);
            const reactNode = (0, react_dom_1.createPortal)(react_1.default.createElement(skill_area_1.default, { removeSkill: removeSkill, ...skill, prefixCls: prefixCls }), skillSpan);
            setSkillPlaceholders(reactNode);
            const range = document.createRange();
            const editableDom = editableRef.current;
            if (!editableDom)
                return;
            range.setStart(editableDom, 0);
            range.insertNode(skillSpan);
            skillDomRef.current = skillSpan;
            triggerValueChange();
        }
    };
    const removeSkill = (isChange = true) => {
        var _a;
        const editableDom = editableRef.current;
        if (!editableDom || !skillDomRef.current)
            return;
        (_a = skillDomRef.current) === null || _a === void 0 ? void 0 : _a.remove();
        skillDomRef.current = null;
        skillRef.current = null;
        if (isChange) {
            triggerValueChange();
        }
    };
    // 移除<br>标签（仅在enter模式下）
    const removeSpecificBRs = (element) => {
        if (submitType !== 'enter' || !element)
            return;
        element.querySelectorAll('br').forEach((br) => {
            br.remove();
        });
    };
    const initRenderSlot = () => {
        if (slotConfig && slotConfig.length > 0 && editableRef.current) {
            initClear();
            appendNodeList(getSlotListNode(slotConfig));
        }
    };
    // 检查是否应该跳过键盘事件处理
    const shouldSkipKeyHandling = (e) => {
        const eventRes = onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e);
        return keyLockRef.current || isCompositionRef.current || eventRes === false;
    };
    // 处理删除操作（退格键、剪切等）
    const handleDeleteOperation = (e, operationType) => {
        var _a, _b, _c;
        if (!editableRef.current)
            return false;
        const { range, selection } = getRange();
        if (!selection || selection.rangeCount === 0)
            return false;
        const { focusOffset, anchorNode } = selection;
        if (!anchorNode || !editableRef.current.contains(anchorNode)) {
            return false;
        }
        // 处理文本节点中的slot删除
        if (anchorNode.nodeType === Node.TEXT_NODE && range) {
            const parentElement = anchorNode.parentNode;
            const nodeInfo = getNodeInfo(parentElement);
            const selectedText = range.toString();
            const isFullTextSelected = ((_a = anchorNode.textContent) === null || _a === void 0 ? void 0 : _a.length) === selectedText.length;
            const isSingleCharAtEnd = ((_b = anchorNode.textContent) === null || _b === void 0 ? void 0 : _b.length) === 1 && focusOffset === 1;
            if (((_c = nodeInfo === null || nodeInfo === void 0 ? void 0 : nodeInfo.slotConfig) === null || _c === void 0 ? void 0 : _c.type) === 'content' && (isFullTextSelected || isSingleCharAtEnd)) {
                e.preventDefault();
                if (operationType === 'cut') {
                    copySelectionString();
                }
                anchorNode.parentNode.innerHTML = '';
                return true;
            }
        }
        // 处理退格键删除前一个元素
        if (operationType === 'backspace' && focusOffset === 0) {
            const { previousSibling } = anchorNode;
            if (previousSibling) {
                const nodeInfo = getNodeInfo(previousSibling);
                if (nodeInfo) {
                    const { slotKey, skillKey } = nodeInfo;
                    if (slotKey) {
                        e.preventDefault();
                        removeSlot(slotKey, e);
                        return true;
                    }
                    if (skillKey) {
                        e.preventDefault();
                        removeSkill();
                        return true;
                    }
                }
            }
        }
        return false;
    };
    // 检查是否应该提交表单
    const shouldSubmitForm = (e) => {
        const { key, shiftKey, ctrlKey, altKey, metaKey } = e;
        if (key !== 'Enter')
            return false;
        const isModifierPressed = ctrlKey || altKey || metaKey;
        return ((submitType === 'enter' && !shiftKey && !isModifierPressed) ||
            (submitType === 'shiftEnter' && shiftKey && !isModifierPressed));
    };
    //  处理skill区域的键盘事件
    const handleSkillAreaKeyEvent = () => {
        if (!skillDomRef.current ||
            !editableRef.current ||
            skillDomRef.current.getAttribute('contenteditable') === 'false') {
            return;
        }
        const selection = getSelection();
        if (!(selection === null || selection === void 0 ? void 0 : selection.anchorNode) ||
            !skillDomRef.current.contains(selection.anchorNode) ||
            !editableRef.current.contains(selection.anchorNode))
            return;
        skillDomRef.current.setAttribute('contenteditable', 'false');
        skillDomRef.current.classList.remove(`${prefixCls}-skill-empty`);
        focus({ cursor: 'end' });
    };
    // ============================ Events =============================
    const onInternalCompositionStart = () => {
        isCompositionRef.current = true;
    };
    const onInternalCompositionEnd = () => {
        isCompositionRef.current = false;
        keyLockRef.current = false;
    };
    const onInternalKeyDown = (e) => {
        var _a;
        // 检查是否应该跳过处理
        if (shouldSkipKeyHandling(e)) {
            onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e);
            return;
        }
        // 确保事件目标是可编辑区域
        if (e.target !== editableRef.current && !((_a = editableRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
            return;
        }
        // 处理退格键删除
        if (e.key === 'Backspace') {
            if (handleDeleteOperation(e, 'backspace'))
                return;
        }
        // 处理Enter键提交
        if (shouldSubmitForm(e)) {
            e.preventDefault();
            keyLockRef.current = true;
            triggerSend === null || triggerSend === void 0 ? void 0 : triggerSend();
            return;
        }
        // 处理全选 (支持 Ctrl+A 和 Cmd+A)
        if (e.key === 'a' && (e.ctrlKey || e.metaKey) && !e.shiftKey && !e.altKey) {
            setAllSelectCursor(editableRef.current, skillDomRef.current);
            e.preventDefault();
            return;
        }
        // 处理 popupTriggerKey 键触发 schema 弹层
        if (e.key === popupTriggerKey && !e.ctrlKey && !e.metaKey && !e.altKey && (schema === null || schema === void 0 ? void 0 : schema.properties)) {
            const editableDom = editableRef.current;
            if (editableDom) {
                const selection = window.getSelection();
                if (selection && selection.rangeCount > 0) {
                    const range = selection.getRangeAt(0).cloneRange();
                    // Save the cursor position for later use when inserting the field
                    setFieldInsertPosition(range.cloneRange());
                    // 在阻止默认行为之前获取光标位置
                    // 如果 range 是折叠的（光标位置），创建一个临时元素来获取更准确的位置
                    let positionRect;
                    if (range.collapsed) {
                        // 创建一个临时的 span 元素来获取准确的光标位置
                        const tempSpan = document.createElement('span');
                        tempSpan.style.position = 'absolute';
                        tempSpan.style.visibility = 'hidden';
                        tempSpan.style.whiteSpace = 'pre';
                        tempSpan.textContent = '\u200b'; // 零宽空格
                        try {
                            range.insertNode(tempSpan);
                            // 使用 Range 的 getBoundingClientRect 获取位置
                            const tempRange = document.createRange();
                            tempRange.selectNodeContents(tempSpan);
                            positionRect = tempRange.getBoundingClientRect();
                            // 移除临时节点
                            tempSpan.remove();
                        }
                        catch (error) {
                            // 如果插入失败，使用原始 range 的位置
                            positionRect = range.getBoundingClientRect();
                        }
                    }
                    else {
                        positionRect = range.getBoundingClientRect();
                    }
                    setSchemaMenuPosition({
                        x: positionRect.left,
                        y: positionRect.bottom,
                    });
                    // 先清空搜索关键词，再显示弹层
                    setSchemaSearchKeyword('');
                    setSelectedField(null);
                    setFieldInputValue('');
                    setShowSchemaMenu(true);
                    e.preventDefault(); // 阻止触发字符被输入到 contentEditable
                }
            }
        }
        // 处理skill区域的键盘事件
        handleSkillAreaKeyEvent();
    };
    const onInternalFocus = (e) => {
        onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
    };
    const onInternalBlur = (e) => {
        if (keyLockRef.current) {
            keyLockRef.current = false;
        }
        const { range } = getRange();
        lastSelectionRef.current = range;
        const timer = setTimeout(() => {
            lastSelectionRef.current = null;
            clearTimeout(timer);
            // 清除光标位置
        }, 200);
        onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
    };
    const onInternalInput = (e) => {
        removeSpecificBRs(editableRef === null || editableRef === void 0 ? void 0 : editableRef.current);
        // 检查是否输入了 popupTriggerKey 字符
        const editableDom = editableRef.current;
        if (editableDom && (schema === null || schema === void 0 ? void 0 : schema.properties)) {
            const text = editableDom.innerText || '';
            const lastChar = text[text.length - 1];
            // 如果输入了 popupTriggerKey，显示 schema 字段选择弹层
            // 注意：这个逻辑现在主要用于兜底，因为 onKeyDown 中已经处理了
            if (lastChar === popupTriggerKey) {
                const selection = window.getSelection();
                if (selection && selection.rangeCount > 0) {
                    const range = selection.getRangeAt(0);
                    const rect = range.getBoundingClientRect();
                    // 先清空搜索关键词，再显示弹层
                    setSchemaSearchKeyword('');
                    setSelectedField(null);
                    setFieldInputValue('');
                    setSchemaMenuPosition({
                        x: rect.left,
                        y: rect.bottom,
                    });
                    setShowSchemaMenu(true);
                }
            }
        }
        triggerValueChange(e);
    };
    const onInternalCut = (e) => {
        handleDeleteOperation(e, 'cut');
    };
    const onInternalPaste = (e) => {
        var _a, _b;
        e.preventDefault();
        const files = (_a = e.clipboardData) === null || _a === void 0 ? void 0 : _a.files;
        const text = (_b = e.clipboardData) === null || _b === void 0 ? void 0 : _b.getData('text/plain');
        if (!text && (files === null || files === void 0 ? void 0 : files.length) && onPasteFile) {
            onPasteFile(files);
            return;
        }
        if (text) {
            let success = false;
            const cleanedText = getCleanedText(text);
            try {
                success = document.execCommand('insertText', false, cleanedText);
            }
            catch (err) {
                (0, warning_1.default)(false, 'Sender', `insertText command failed: ${err}`);
            }
            if (!success) {
                insert([{ type: 'text', value: cleanedText }]);
            }
        }
        onPaste === null || onPaste === void 0 ? void 0 : onPaste(e);
    };
    const onInternalKeyUp = (e) => {
        // 只在松开 Enter 键时解除锁定
        if (e.key === 'Enter') {
            keyLockRef.current = false;
        }
        // 只处理外部传入的 onKeyUp 回调
        onKeyUp === null || onKeyUp === void 0 ? void 0 : onKeyUp(e);
    };
    const onInternalSelect = () => {
        const editableDom = editableRef.current;
        const selection = getSelection();
        if (editableDom &&
            (selection === null || selection === void 0 ? void 0 : selection.focusNode) === editableDom &&
            selection.focusOffset === 0 &&
            getEditorValue().skill) {
            setCursorPosition(editableDom, editableRef.current, 1);
        }
    };
    // ============================ Ref Method ============================
    const insert = (slotConfigList, position, replaceCharacters, preventScroll) => {
        const editableDom = editableRef.current;
        if (!editableDom)
            return;
        try {
            // 合并配置并生成节点
            mergeSlotConfig(slotConfigList);
            const slotNodes = getSlotListNode(slotConfigList);
            if (slotNodes.length === 0)
                return;
            // 获取插入位置和范围
            const insertContext = getInsertContext(position, editableDom);
            if (!insertContext.range || !insertContext.selection)
                return;
            const { range, selection } = insertContext;
            // 处理字符替换
            if (replaceCharacters === null || replaceCharacters === void 0 ? void 0 : replaceCharacters.length) {
                handleCharacterReplacement(range, replaceCharacters, editableDom);
            }
            // 执行节点插入
            insertNodesWithPosition(slotNodes, range, insertContext);
            // 设置光标位置并触发更新
            finalizeInsertion(slotNodes, range, selection, preventScroll);
        }
        catch (error) {
            (0, warning_1.default)(false, 'Sender', `Insert operation failed: ${error}`);
        }
    };
    // 获取插入上下文信息
    const getInsertContext = (position, editableDom) => {
        const { type, slotKey, slotType, range: lastRange, selection, } = getInsertPosition(position, editableRef, lastSelectionRef);
        if (!selection) {
            return { range: null, selection: null, type };
        }
        let range = null;
        switch (type) {
            case 'end':
                range = getEndRange(editableDom);
                break;
            case 'start':
                range = getStartRange(editableDom);
                break;
            case 'slot':
                range = getRange().range;
                break;
            case 'box':
                range = lastRange || null;
                if (range && skillDomRef.current && range.collapsed && range.startOffset === 0) {
                    range.setStartAfter(skillDomRef.current);
                }
                break;
            default:
                range = getEndRange(editableDom);
        }
        return { range, selection, type, slotKey, slotType };
    };
    // 处理字符替换
    const handleCharacterReplacement = (range, replaceCharacters, editableDom) => {
        const { value: textBeforeCursor, startContainer, startOffset, } = getTextBeforeCursor(editableDom);
        const cursorPosition = textBeforeCursor.length;
        if (cursorPosition >= replaceCharacters.length &&
            textBeforeCursor.endsWith(replaceCharacters) &&
            startContainer &&
            startOffset >= 0) {
            range.setStart(startContainer, startOffset - replaceCharacters.length);
            range.setEnd(startContainer, startOffset);
            range.deleteContents();
        }
    };
    /**
     * 根据位置插入节点
     */
    const insertNodesWithPosition = (slotNodes, range, context) => {
        const { type, slotKey, slotType } = context;
        let shouldSkipFirstNode = true;
        slotNodes.forEach((node) => {
            // 处理slot插入的特殊逻辑
            if (shouldSkipFirstNode &&
                type === 'slot' &&
                (slotType !== 'content' || node.nodeType !== Node.TEXT_NODE) &&
                slotKey) {
                shouldSkipFirstNode = false;
                const lastDom = getSlotLastDom(slotKey);
                if (lastDom) {
                    range.setStartAfter(lastDom);
                }
            }
            range.insertNode(node);
            range.setStartAfter(node);
        });
    };
    // 完成插入操作，设置光标并触发更新
    const finalizeInsertion = (slotNodes, range, selection, preventScroll) => {
        selection.deleteFromDocument();
        const lastNode = slotNodes[slotNodes.length - 1];
        setAfterNodeFocus(lastNode, editableRef.current, range, selection, preventScroll);
        // 延迟触发输入事件以确保DOM更新完成
        setTimeout(() => {
            onInternalInput(null);
        }, 0);
    };
    const focus = (options) => {
        var _a, _b;
        const mergeOptions = {
            preventScroll: (_a = options === null || options === void 0 ? void 0 : options.preventScroll) !== null && _a !== void 0 ? _a : false,
            cursor: (_b = options === null || options === void 0 ? void 0 : options.cursor) !== null && _b !== void 0 ? _b : 'end',
            key: options === null || options === void 0 ? void 0 : options.key,
        };
        switch (mergeOptions.cursor) {
            case 'slot':
                setSlotFocus(editableRef, options === null || options === void 0 ? void 0 : options.key, mergeOptions.preventScroll);
                break;
            case 'start':
                setStartCursor(editableRef.current, mergeOptions.preventScroll);
                break;
            case 'all':
                setAllSelectCursor(editableRef.current, skillDomRef.current, mergeOptions.preventScroll);
                break;
            case 'end':
                setEndCursor(editableRef.current, mergeOptions.preventScroll);
                break;
        }
    };
    const clear = () => {
        var _a, _b;
        const editableDom = editableRef.current;
        if (!editableDom)
            return;
        try {
            (_a = form === null || form === void 0 ? void 0 : form.clearFormGraph) === null || _a === void 0 ? void 0 : _a.call(form);
        }
        catch {
            /* noop */
        }
        setFormMap({});
        formMapRef.current = {};
        editableDom.innerHTML = '';
        skillRef.current = null;
        skillDomRef.current = null;
        slotConfigMap.clear();
        insertSkill();
        setSlotValues({});
        setSlotPlaceholders(new Map());
        lastSelectionRef.current = null;
        (_b = slotDomMap === null || slotDomMap === void 0 ? void 0 : slotDomMap.current) === null || _b === void 0 ? void 0 : _b.clear();
        onInternalInput(null);
    };
    // ============================ Effects =============================
    (0, react_1.useEffect)(() => {
        initRenderSlot();
        if (!skill) {
            triggerValueChange();
        }
        else {
            insertSkill();
        }
    }, [slotConfig]);
    (0, react_1.useEffect)(() => {
        insertSkill();
    }, [skill]);
    // 获取可用的字段列表
    const getAvailableFields = react_1.default.useCallback(() => {
        if (!(schema === null || schema === void 0 ? void 0 : schema.properties))
            return [];
        const fields = [];
        const walk = (properties, parentPath = '') => {
            Object.entries(properties || {}).forEach(([key, config]) => {
                var _a;
                const currentPath = parentPath ? `${parentPath}.${key}` : key;
                if (!config || typeof config !== 'object')
                    return;
                if ((_a = config['x-data']) === null || _a === void 0 ? void 0 : _a.hidden)
                    return;
                // 叶子字段：有 x-component 且不是纯容器
                if (config['x-component']) {
                    const title = config.title || currentPath;
                    if (!schemaSearchKeyword ||
                        title.toLowerCase().includes(schemaSearchKeyword.toLowerCase())) {
                        fields.push({ key: currentPath, config, title });
                    }
                }
                if (config.properties && typeof config.properties === 'object') {
                    walk(config.properties, currentPath);
                }
            });
        };
        walk(schema.properties);
        return fields;
    }, [schema, schemaSearchKeyword]);
    (0, react_1.useImperativeHandle)(ref, () => {
        return {
            nativeElement: editableRef.current,
            focus,
            blur: () => { var _a; return (_a = editableRef.current) === null || _a === void 0 ? void 0 : _a.blur(); },
            insert,
            clear,
            getValue: getEditorValue,
        };
    });
    (0, react_1.useEffect)(() => {
        setTimeout(() => {
            const dom = document.querySelector('.cn-sender-schema-menu-async-select');
            if (dom && showSchemaMenu) {
                dom.click();
            }
        }, 200);
    }, [showSchemaMenu]);
    // ============================ Render =============================
    return (react_1.default.createElement(react_1.default.Fragment, null,
        react_1.default.createElement("div", { ...inputProps, role: "textbox", tabIndex: 0, style: { ...mergeStyle, ...inputHeightStyle }, className: (0, classnames_1.default)(inputCls, `${inputCls}-slot`, classNames.input), "data-placeholder": placeholder, contentEditable: !readOnly, suppressContentEditableWarning: true, spellCheck: false, onCut: onInternalCut, onKeyDown: onInternalKeyDown, onKeyUp: onInternalKeyUp, onPaste: onInternalPaste, onCompositionStart: onInternalCompositionStart, onCompositionEnd: onInternalCompositionEnd, onFocus: onInternalFocus, onBlur: onInternalBlur, onSelect: onInternalSelect, onInput: onInternalInput, ...restProps }),
        react_1.default.createElement("div", { style: {
                display: 'none',
            }, id: `${prefixCls}-slot-placeholders` },
            Array.from(slotPlaceholders.values()),
            skillPlaceholders),
        showSchemaMenu &&
            schemaMenuPosition &&
            (schema === null || schema === void 0 ? void 0 : schema.properties) &&
            (0, react_dom_1.createPortal)(react_1.default.createElement("div", { className: "cn-sender-schema-menu-overlay", style: {
                    position: 'fixed',
                    left: 0,
                    top: 0,
                    right: 0,
                    bottom: 0,
                    zIndex: 1050,
                    pointerEvents: 'none', // 遮罩不拦截点击，让下拉列表能收到点击；关弹层通过 document 点击判断
                } },
                react_1.default.createElement("div", { ref: schemaMenuContentRef, className: "cn-sender-schema-menu-content", style: {
                        position: 'fixed',
                        left: schemaMenuPosition.x,
                        top: schemaMenuPosition.y,
                        marginTop: 8,
                        zIndex: 1051,
                        pointerEvents: 'auto', // 内容区可点击，与遮罩的 pointer-events: none 配合
                    } },
                    react_1.default.createElement(cn_ui_1.CnAsyncSelect, { showSearch: true, className: "cn-sender-schema-menu-async-select", ...((customPopupProps === null || customPopupProps === void 0 ? void 0 : customPopupProps.asyncSelectProps) || {}), dataSource: ((_a = customPopupProps === null || customPopupProps === void 0 ? void 0 : customPopupProps.asyncSelectProps) === null || _a === void 0 ? void 0 : _a.dataSource) ||
                            getAvailableFields().map((field) => ({
                                label: field.title,
                                value: field.key,
                                fieldConfig: field.config,
                            })), popupProps: {
                            v2: true,
                            // 下拉挂载到当前内容容器内，避免被全屏遮罩挡住导致点击选项无反应
                            getPopupContainer: () => { var _a; return (_a = schemaMenuContentRef.current) !== null && _a !== void 0 ? _a : document.body; },
                        }, onChange: (value, actionType, item) => {
                            var _a;
                            // 如果提供了自定义 onChange，优先使用
                            if ((_a = customPopupProps === null || customPopupProps === void 0 ? void 0 : customPopupProps.asyncSelectProps) === null || _a === void 0 ? void 0 : _a.onChange) {
                                customPopupProps.asyncSelectProps.onChange(value, actionType, item);
                            }
                            else {
                                // 默认行为：如果有 onFieldSelect 回调，调用它
                                const fieldConfig = item === null || item === void 0 ? void 0 : item.fieldConfig;
                                if (value && fieldConfig && (customPopupProps === null || customPopupProps === void 0 ? void 0 : customPopupProps.onFieldSelect)) {
                                    // 使用存储的位置信息来确保在正确位置插入字段
                                    const fieldSelectCallback = {
                                        fieldKey: value,
                                        fieldConfig,
                                    };
                                    // 恢复之前保存的光标位置
                                    const savedPosition = fieldInsertPosition;
                                    if (savedPosition) {
                                        const selection = window.getSelection();
                                        if (selection) {
                                            selection.removeAllRanges();
                                            selection.addRange(savedPosition);
                                        }
                                    }
                                    customPopupProps.onFieldSelect(fieldSelectCallback);
                                }
                            }
                            // 关闭弹层
                            setShowSchemaMenu(false);
                            setSchemaMenuPosition(null);
                            setSelectedField(null);
                            setFieldInputValue('');
                            setSchemaSearchKeyword('');
                            setFieldInsertPosition(null);
                        } }))), document.body)));
});
if (process.env.NODE_ENV !== 'production') {
    SlotTextArea.displayName = 'SlotTextArea';
}
exports.default = SlotTextArea;
