"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createReactNodeGenerator = exports.generateVue = exports.createNodeGenerator = exports.generateReactExprInJS = exports.generateConditionReactCtrl = exports.generateReactLoopCtrl = exports.isPureString = void 0;
const tslib_1 = require("tslib");
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const function_1 = require("fp-ts/function");
const lowcode_types_1 = require("@alilc/lowcode-types");
const lowcode_code_generator_1 = require("@alilc/lowcode-code-generator");
const lowcode_code_generator_2 = tslib_1.__importDefault(require("@alilc/lowcode-code-generator"));
const aopHelper_1 = require("./aopHelper");
const encodeJsxAttrString_1 = require("./encodeJsxAttrString");
const jsxHelpers_1 = require("./jsxHelpers");
const transformThis2Context_1 = require("../plugins/component/vue3/core/jsx/handlers/transformThis2Context");
const { compositeType: { generateCompositeType }, common: { getStaticExprValue }, validate: { isValidIdentifier } } = lowcode_code_generator_2.default.utils;
function mergeNodeGeneratorConfig(cfg1, cfg2 = {}) {
    const resCfg = {};
    if (cfg1.handlers || cfg2.handlers) {
        resCfg.handlers = Object.assign(Object.assign({}, (cfg1.handlers || {})), (cfg2.handlers || {}));
    }
    if (cfg1.tagMapping || cfg2.tagMapping) {
        resCfg.tagMapping = cfg2.tagMapping || cfg1.tagMapping;
    }
    if (cfg1.attrPlugins || cfg2.attrPlugins) {
        resCfg.attrPlugins = [];
        resCfg.attrPlugins.push(...(cfg2.attrPlugins || []));
        resCfg.attrPlugins.push(...(cfg1.attrPlugins || []));
    }
    if (cfg1.nodePlugins || cfg2.nodePlugins) {
        resCfg.nodePlugins = [];
        resCfg.nodePlugins.push(...(cfg2.nodePlugins || []));
        resCfg.nodePlugins.push(...(cfg1.nodePlugins || []));
    }
    return resCfg;
}
function isPureString(v) {
    // FIXME: 目前的方式不够严谨
    return (v[0] === "'" && v[v.length - 1] === "'") || (v[0] === '"' && v[v.length - 1] === '"');
}
exports.isPureString = isPureString;
function generateAttrValue(attrData, scope, config) {
    const { attrValue, attrName } = attrData;
    if ((attrValue === null || attrValue === void 0 ? void 0 : attrValue.type) === 'JSExpression') {
        attrData.attrValue = `${attrValue.value}`;
        attrData.attrName = ':' + attrData.attrName;
    }
    const valueStr = generateCompositeType(attrData.attrValue, scope, {
        handlers: config === null || config === void 0 ? void 0 : config.handlers,
        nodeGenerator: config === null || config === void 0 ? void 0 : config.self,
    });
    return [
        {
            type: lowcode_code_generator_1.PIECE_TYPE.ATTR,
            name: attrData.attrName,
            value: valueStr,
        },
    ];
}
function generateAttr(attrName, attrValue, scope, config) {
    let pieces;
    if (config === null || config === void 0 ? void 0 : config.attrPlugins) {
        pieces = (0, aopHelper_1.executeFunctionStack)({ attrName, attrValue }, scope, config.attrPlugins, generateAttrValue, config);
    }
    else {
        pieces = generateAttrValue({ attrName, attrValue }, scope, config);
    }
    pieces = pieces.map((p) => {
        // FIXME: 在经过 generateCompositeType 处理过之后，其实已经无法通过传入值的类型判断传出值是否为纯字面值字符串了（可能包裹了加工函数之类的）
        //        因此这个处理最好的方式是对传出值做语法分析，判断以哪种模版产出 Attr 值
        let newValue;
        if (p.value && isPureString(p.value)) {
            // 似乎多次一举，目前的诉求是处理多种引号类型字符串的 case，正确处理转义
            const content = getStaticExprValue(p.value);
            newValue = JSON.stringify((0, encodeJsxAttrString_1.encodeJsxStringNode)(content));
        }
        else {
            p.name = ':' + p.name;
            newValue = `'${p.value}'`;
        }
        return {
            value: `${p.name}=${newValue}`,
            type: lowcode_code_generator_1.PIECE_TYPE.ATTR,
        };
    });
    return pieces;
}
function generateAttrs(nodeItem, scope, config) {
    const { props } = nodeItem;
    let pieces = [];
    if (props) {
        if (!Array.isArray(props)) {
            Object.keys(props).forEach((propName) => {
                if (propName) {
                    pieces = pieces.concat(generateAttr(propName, props[propName], scope, config));
                }
            });
        }
        else {
            props.forEach((prop) => {
                if (prop.name && !prop.spread) {
                    pieces = pieces.concat(generateAttr(prop.name, prop.value, scope, config));
                }
                // TODO: 处理 spread 场景（<Xxx {...(something)}/>)
                // 这种在 schema 里面怎么描述
            });
        }
    }
    return pieces;
}
function generateBasicNode(nodeItem, scope, config) {
    const pieces = [];
    const tagName = ((config === null || config === void 0 ? void 0 : config.tagMapping) || lodash_1.default.identity)(nodeItem.componentName);
    pieces.push({
        value: tagName || '',
        type: lowcode_code_generator_1.PIECE_TYPE.TAG,
    });
    return pieces;
}
function generateSimpleNode(nodeItem, scope, config) {
    const basicParts = generateBasicNode(nodeItem, scope, config) || [];
    const attrParts = generateAttrs(nodeItem, scope, config) || [];
    const childrenParts = [];
    if (nodeItem.children && (config === null || config === void 0 ? void 0 : config.self)) {
        const childrenStr = config.self(nodeItem.children, scope);
        childrenParts.push({
            type: lowcode_code_generator_1.PIECE_TYPE.CHILDREN,
            value: childrenStr,
        });
    }
    return [...basicParts, ...attrParts, ...childrenParts];
}
function linkPieces(pieces) {
    const tagsPieces = pieces.filter((p) => p.type === lowcode_code_generator_1.PIECE_TYPE.TAG);
    if (tagsPieces.length !== 1) {
        throw new lowcode_code_generator_1.CodeGeneratorError('Only one tag definition required', tagsPieces);
    }
    const tagName = tagsPieces[0].value;
    const beforeParts = pieces
        .filter((p) => p.type === lowcode_code_generator_1.PIECE_TYPE.BEFORE)
        .map((p) => p.value)
        .join('');
    const afterParts = pieces
        .filter((p) => p.type === lowcode_code_generator_1.PIECE_TYPE.AFTER)
        .map((p) => p.value)
        .join('');
    const childrenParts = pieces
        .filter((p) => p.type === lowcode_code_generator_1.PIECE_TYPE.CHILDREN)
        .map((p) => p.value)
        .join('');
    let attrsParts = pieces
        .filter((p) => p.type === lowcode_code_generator_1.PIECE_TYPE.ATTR)
        .map((p) => p.value)
        .join(' ');
    attrsParts = attrsParts ? ` ${attrsParts}` : '';
    if (childrenParts) {
        return `${beforeParts}<${tagName}${attrsParts}>${childrenParts}</${tagName}>${afterParts}`;
    }
    return `${beforeParts}<${tagName}${attrsParts} />${afterParts}`;
}
function generateNodeSchema(nodeItem, scope, config) {
    const pieces = [];
    if (config === null || config === void 0 ? void 0 : config.nodePlugins) {
        const res = (0, aopHelper_1.executeFunctionStack)(nodeItem, scope, config.nodePlugins, generateSimpleNode, config);
        pieces.push(...res);
    }
    else {
        pieces.push(...generateSimpleNode(nodeItem, scope, config));
    }
    return linkPieces(pieces);
}
// TODO: 生成文档
// 为包裹的代码片段生成子上下文，集成父级上下文，并传入子级上下文新增内容。（如果存在多级上下文怎么处理？）
// 创建新的上下文，并从作用域中取对应同名变量塞到作用域里面？
// export function createSubContext() {}
/**
 * JSX 生成逻辑插件。在 React 代码模式下生成 loop 相关的逻辑代码
 * @type NodePlugin Extended
 *
 * @export
 * @param {IPublicTypeNodeSchema} nodeItem 当前 UI 节点
 * @returns {CodePiece[]} 实现功能的相关代码片段
 */
function generateReactLoopCtrl(nodeItem, scope, config, next) {
    var _a, _b, _c;
    if (nodeItem.loop) {
        const tolerateEvalErrors = (_a = config === null || config === void 0 ? void 0 : config.tolerateEvalErrors) !== null && _a !== void 0 ? _a : true;
        const loopItemName = ((_b = nodeItem.loopArgs) === null || _b === void 0 ? void 0 : _b[0]) || 'item';
        const loopIndexName = ((_c = nodeItem.loopArgs) === null || _c === void 0 ? void 0 : _c[1]) || 'index';
        // 新建作用域
        const subScope = scope.createSubScope([loopItemName, loopIndexName]);
        const pieces = next ? next(nodeItem, subScope, config) : [];
        // 生成循环变量表达式
        const loopDataExpr = (0, function_1.pipe)(nodeItem.loop, 
        // 将 JSExpression 转换为 JS 表达式代码:
        (expr) => generateCompositeType(expr, scope, {
            handlers: config === null || config === void 0 ? void 0 : config.handlers,
            tolerateEvalErrors: false, // 这个内部不需要包 try catch, 下面会统一加的
        }), 
        // 将 this.xxx 转换为 __$$context.xxx:
        (expr) => (0, transformThis2Context_1.transformThis2Context)(expr, scope, { ignoreRootScope: true }));
        pieces.unshift({
            value: `(${loopDataExpr}).map((${loopItemName}, ${loopIndexName}) => ((__$$context) => (`,
            type: lowcode_code_generator_1.PIECE_TYPE.BEFORE,
        });
        pieces.push({
            value: `))(__$$createChildContext(__$$context, { ${loopItemName}, ${loopIndexName} })))`,
            type: lowcode_code_generator_1.PIECE_TYPE.AFTER,
        });
        return pieces;
    }
    return next ? next(nodeItem, scope, config) : [];
}
exports.generateReactLoopCtrl = generateReactLoopCtrl;
/**
 * JSX 生成逻辑插件。在 React 代码模式下生成 condition 相关的逻辑代码
 * @type NodePlugin
 *
 * @export
 * @param {IPublicTypeNodeSchema} nodeItem 当前 UI 节点
 * @returns {CodePiece[]} 实现功能的相关代码片段
 */
function generateConditionReactCtrl(nodeItem, scope, config, next) {
    const pieces = next ? next(nodeItem, scope, config) : [];
    if (nodeItem.condition != null && nodeItem.condition !== true) {
        const value = generateCompositeType(nodeItem.condition, scope, {
            handlers: config === null || config === void 0 ? void 0 : config.handlers,
        });
        pieces.unshift({
            value: `!!(${value}) && (`,
            type: lowcode_code_generator_1.PIECE_TYPE.BEFORE,
        });
        pieces.push({
            value: ')',
            type: lowcode_code_generator_1.PIECE_TYPE.AFTER,
        });
    }
    return pieces;
}
exports.generateConditionReactCtrl = generateConditionReactCtrl;
/**
 * JSX 生成逻辑插件。在 React 代码模式下，如果 Node 生成结果是一个表达式，则对其进行 { Expression } 包装
 * @type NodePlugin
 *
 * @export
 * @param {IPublicTypeNodeSchema} nodeItem 当前 UI 节点
 * @returns {CodePiece[]} 实现功能的相关代码片段
 */
function generateReactExprInJS(nodeItem, scope, config, next) {
    const pieces = next ? next(nodeItem, scope, config) : [];
    if ((nodeItem.condition != null && nodeItem.condition !== true) || nodeItem.loop != null) {
        pieces.unshift({
            value: '{',
            type: lowcode_code_generator_1.PIECE_TYPE.BEFORE,
        });
        pieces.push({
            value: '}',
            type: lowcode_code_generator_1.PIECE_TYPE.AFTER,
        });
    }
    return pieces;
}
exports.generateReactExprInJS = generateReactExprInJS;
const handleChildren = (v) => v.join('');
const handleEvent = (props = {}) => {
    let result = Object.assign({}, props);
    const { __events: events } = props;
    const redundancePro = ['onClick', '__events'];
    const VueEvent = {
        onClick: '@click',
    };
    const { eventList = [], eventDataList = [] } = events;
    const eventArr = eventList.filter((event) => {
        const eventData = eventDataList.find((item) => item.name === event.name);
        event.relatedEventName = eventData === null || eventData === void 0 ? void 0 : eventData.relatedEventName;
        return event === null || event === void 0 ? void 0 : event.disabled;
    }).map((item) => {
        result = Object.assign(Object.assign({}, props), { [VueEvent[item.name]]: item.relatedEventName });
    });
    redundancePro.forEach((property) => {
        if (result.hasOwnProperty(property)) {
            delete result[property];
        }
    });
    return result;
};
function createNodeGenerator(cfg = {}) {
    const generateNode = (nodeItem, scope) => {
        if (lodash_1.default.isArray(nodeItem)) {
            const resList = nodeItem.map((n) => generateNode(n, scope));
            return handleChildren(resList);
        }
        if ((0, lowcode_types_1.isNodeSchema)(nodeItem)) {
            const { props, loop, } = nodeItem;
            if (loop) {
                const items = JSON.stringify(loop);
                nodeItem.props = Object.assign(Object.assign({}, props), { "v-for": `(item,index) in ${items}` });
            }
            if (props === null || props === void 0 ? void 0 : props["__events"]) {
                const newProps = handleEvent(props);
                nodeItem.props = newProps;
            }
            return generateNodeSchema(nodeItem, scope, Object.assign(Object.assign({}, cfg), { self: generateNode }));
        }
        const valueStr = generateCompositeType(nodeItem, scope, {
            handlers: cfg.handlers,
            nodeGenerator: generateNode,
        });
        if (isPureString(valueStr)) {
            return (0, encodeJsxAttrString_1.encodeJsxStringNode)(getStaticExprValue(valueStr));
        }
        return `{${valueStr}}`;
    };
    return (nodeItem, scope) => (0, jsxHelpers_1.unwrapJsExprQuoteInJsx)(generateNode(nodeItem, scope));
}
exports.createNodeGenerator = createNodeGenerator;
function generateVue(nodeItem, scope, config, next) {
    const pieces = next ? next(nodeItem, scope, config) : [];
    if ((nodeItem.condition != null && nodeItem.condition !== true) || nodeItem.loop != null) {
        pieces.unshift({
            value: '{',
            type: lowcode_code_generator_1.PIECE_TYPE.BEFORE,
        });
        pieces.push({
            value: '}',
            type: lowcode_code_generator_1.PIECE_TYPE.AFTER,
        });
    }
    return pieces;
}
exports.generateVue = generateVue;
const defaultReactGeneratorConfig = {
    // nodePlugins: [generateReactExprInJS, generateReactLoopCtrl, generateConditionReactCtrl],
    nodePlugins: [],
};
function createReactNodeGenerator(cfg) {
    const newCfg = mergeNodeGeneratorConfig(defaultReactGeneratorConfig, cfg);
    return createNodeGenerator(newCfg);
}
exports.createReactNodeGenerator = createReactNodeGenerator;
//# sourceMappingURL=nodeToVue.js.map