"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const parser = require("@babel/parser");
const t = require("@babel/types");
const traverse_1 = require("@babel/traverse");
const ast_helpers_1 = require("./ast-helpers");
const utils_1 = require("../utils/utils");
const newViewDSL_1 = require("./newViewDSL");
const newLogicDSL_1 = require("./newLogicDSL");
const prettier = require('prettier/standalone');
const parserBabel = require('prettier/parser-babel');
const parserHtml = require('prettier/parser-html');
const { generator } = require('mvvm-template-parser');
const PAGE_ACTION = 'pageAction';
const PAGE_VAR = 'variables';
const PAGE_PARAM = 'parameter';
const PAGE_COMMONSQL = 'commonSql';
const PAGE_NATIVESQL = 'nativeSql';
const FETCH_INIT = 'init';
const codeGenerator = data => {
    const { template, pageName, pagePath, projectName, pageData, blocks, isPage, manual = false, utilFunctions = [], } = data;
    const member = {
        name: (0, utils_1.camelize)(`_${pageName}Component`),
        datas: {},
        props: {},
        computeds: {},
        component: ['message'],
        mobileComponent: [],
        extraComponent: [],
        thirdPartyComponent: [],
        fishx: [],
        dataBinding: [],
        commonSqlBinding: [],
        pageBlocks: [],
        pageWidgets: [],
    };
    const errorInfo = {
        type: '',
        message: '',
    };
    const collect = {
        imports: [],
        classMethods: {},
    };
    const propsChangeState = [];
    // pageMember 包含整个页面的基本信息，其中 parameter 字段表示的是页面或者block的传参
    // variables 表示页面或者区块变量
    const pageMember = pageData[pagePath];
    const pageActionInfo = pageMember.actions;
    const hasInit = !!pageMember.pageAction.find(v => v.name === 'initialize');
    let pageAction;
    let pageVar;
    let pageInput; // 保存的是页面的输入变量，pageMember.parameter
    let pageCommonSql;
    let pageNativeSql;
    let hasMounted = false;
    const actionCodeMapList = [];
    // eslint-disable-next-line no-restricted-syntax
    for (const key in pageMember) {
        if (pageMember.hasOwnProperty(key)) {
            const element = pageMember[key];
            if (key === PAGE_ACTION) {
                pageAction = element;
            }
            if (key === PAGE_VAR) {
                pageVar = [...element];
                // 收集外部props改变内部state的场景
                element.forEach(ele => {
                    // 本地变量的初始值是prop变量，从页面中传入的变量
                    if (ele.defaultValue && ele.defaultValue.indexOf('#{') !== -1) {
                        const outProps = ele.defaultValue.replace(/(?:#{)|(?:})/g, '');
                        propsChangeState.push({
                            state: ele.name,
                            props: outProps,
                        });
                    }
                });
                if (propsChangeState.length) {
                    pageVar.push({
                        name: 'innerUpdate',
                        type: 'Boolean',
                        defaultValue: 'false',
                    });
                }
            }
            if (key === PAGE_PARAM) {
                pageInput = element;
            }
            if (key === PAGE_COMMONSQL) {
                pageCommonSql = element;
            }
            if (key === PAGE_NATIVESQL) {
                pageNativeSql = element;
            }
        }
    }
    const initCommonSqlQuery = [];
    pageCommonSql.forEach(v => {
        const sqlData = JSON.parse(v.data);
        const variables = [];
        if (sqlData.fetchTiming === FETCH_INIT) {
            sqlData.testValue.forEach(ele => {
                // variables.push(ele.placehold.replace(/(?:#{)|(?:})/g, ''));
                variables.push(ele);
            });
            pageVar.push({
                name: v.name,
                type: 'Array',
                isCommonSql: true,
                defaultValue: '[]',
            });
            // pageVar.push({name: `${v.name}_current`, type: 'Object', isCommonSql: true});
            initCommonSqlQuery.push({
                variables,
                name: v.name,
                pageIndex: sqlData.pageIndex,
                pageSize: sqlData.pageSize,
                afterFetch: sqlData.afterFetch,
                qson: `${projectName}/${v.name}`,
            });
        }
    });
    const lodashFns = [];
    const utilFns = ['setStatePromise', 'get', 'call', 'lowcode_formValid'];
    let tplParserRes;
    try {
        // 解析页面的view dsl
        tplParserRes = (0, newViewDSL_1.default)(template, member, blocks, pageVar, pageCommonSql, pageNativeSql, pageInput, isPage, propsChangeState, utilFns, lodashFns, utilFunctions);
    }
    catch (error) {
        console.error(error);
        errorInfo.type = 'viewDSL-code-error';
        errorInfo.parseCode = error.parseCode;
        if (error.loc) {
            errorInfo.message = `请检查页面【${pageName}】中的错误。处于行：${error.loc.line}，列：${error.loc.column} \n ${error.message}`;
        }
        else {
            errorInfo.message = `转换页面【${pageName}】的view dsl发生错误。\n ${error.stack}`;
        }
    }
    if (errorInfo.message) {
        return { code: '', errorInfo };
    }
    const { hasDate, tplCode, hasUpload, formInfo, modalInfo, blockInfo, remoteOptions, conditionObj, picklists, extraClassMethod, fishxIcons, hasTableEdit, expandableBlocks, } = tplParserRes;
    remoteOptions.forEach(v => {
        pageVar.push({ name: v.name, type: 'Array', isCommonSql: true });
    });
    picklists.forEach(v => {
        pageVar.push({ name: v.name, type: 'Array', isCommonSql: true });
    });
    // 解析页面下的action dsl
    const actionCode = [];
    for (let index = 0; index < pageAction.length; index += 1) {
        const action = pageAction[index];
        const originCode = pageData[`${pagePath}/${action.name}`];
        if (originCode) {
            const logicData = {
                isPage,
                pageVar,
                pagePath,
                utilFns,
                member,
                blocks,
                lodashFns,
                pageInput,
                blockInfo,
                formInfo,
                pageCommonSql,
                pageNativeSql,
                originCode,
                utilFunctions,
                pageActionInfo,
                propsChangeState,
                pageName,
                actionName: action.name,
            };
            if (action.name === 'mounted') {
                hasMounted = true;
                logicData.picklists = picklists.length > 0;
                logicData.remoteOptions = remoteOptions.length > 0;
                logicData.initCommonSqlQuery = initCommonSqlQuery.length > 0;
            }
            try {
                const transRes = (0, newLogicDSL_1.default)(logicData);
                actionCode.push(transRes);
                actionCodeMapList.push({
                    pageName,
                    name: action.name,
                    generatedCode: transRes.generatedAction.code,
                    map: transRes.generatedAction.map,
                });
            }
            catch (error) {
                errorInfo.type = 'logicDSL-code-error';
                errorInfo.parseCode = error.parseCode;
                if (error.loc) {
                    errorInfo.message = `请检查页面【${pageName}】下的action【${action.name}】中的错误。处于行：${error.loc.line}，列：${error.loc.column} \n ${error}`;
                }
                else {
                    errorInfo.message = `转换页面【${pageName}】下的action【${action.name}】的action dsl发生错误。\n ${error.stack}`;
                }
                break;
            }
        }
    }
    if (errorInfo.message) {
        return { code: '', errorInfo };
    }
    const scriptRender = `class ${(0, utils_1.parseName)(member.name)} extends Component {}
                          export default ${(0, utils_1.parseName)(member.name)};`;
    const sast = parser.parse(scriptRender, {
        sourceType: 'module',
        plugins: ['classProperties'],
    });
    (0, traverse_1.default)(sast, {
        Program(path) {
            (0, ast_helpers_1.genImports)(path, collect, member, hasDate, hasUpload, manual, pageName, pageAction, fishxIcons, hasTableEdit, expandableBlocks);
        },
        ClassBody(path) {
            if (propsChangeState.length) {
                const ifExp = [];
                propsChangeState.forEach(v => {
                    ifExp.push(t.ifStatement(t.binaryExpression('!==', (0, utils_1.objValueStr2AST)(`nextProps.${v.props}`), (0, utils_1.objValueStr2AST)(`prevState.${v.state}`)), t.blockStatement([
                        t.ifStatement(t.binaryExpression('===', (0, utils_1.objValueStr2AST)('nextProps.innerUpdate'), (0, utils_1.objValueStr2AST)('prevState.innerUpdate')), t.blockStatement([
                            t.returnStatement(t.objectExpression([
                                t.objectProperty(t.identifier(v.state), (0, utils_1.objValueStr2AST)(`nextProps.${v.props}`)),
                            ])),
                        ]), t.blockStatement([
                            t.returnStatement(t.objectExpression([
                                t.objectProperty(t.identifier('innerUpdate'), t.booleanLiteral(false)),
                            ])),
                        ])),
                    ]), null));
                });
                ifExp.push(t.returnStatement(t.nullLiteral()));
                path.node.body.push(t.classProperty(t.identifier('defaultProps'), t.objectExpression([
                    t.objectProperty(t.identifier('innerUpdate'), t.booleanLiteral(false)),
                ]), t.noop(), [], false, true), t.classMethod('method', t.identifier('getDerivedStateFromProps'), [t.identifier('nextProps'), t.identifier('prevState')], t.blockStatement(ifExp), false, true));
            }
            if (formInfo.length) {
                formInfo.forEach(v => {
                    path.node.body.push(t.classProperty(t.identifier(v), t.callExpression((0, utils_1.objValueStr2AST)('React.createRef'), [])));
                });
            }
            if (modalInfo.length) {
                modalInfo.forEach(v => {
                    path.node.body.push(t.classProperty(t.identifier(v), t.callExpression((0, utils_1.objValueStr2AST)('React.createRef'), [])));
                });
            }
            if (blockInfo.length) {
                blockInfo.forEach(v => {
                    path.node.body.push(t.classProperty(t.identifier(v.ref), t.callExpression((0, utils_1.objValueStr2AST)('React.createRef'), [])));
                });
            }
            if (picklists.length) {
                const plBlocks = [];
                (0, ast_helpers_1.genCommonSqlMethod)(plBlocks, [], [], picklists);
                if (plBlocks.length) {
                    path.node.body.push(t.classMethod('method', t.identifier('fetchPicklists'), [], t.blockStatement(plBlocks), false, false, false, true));
                }
            }
            if (remoteOptions.length) {
                const remoteBlocks = [];
                (0, ast_helpers_1.genCommonSqlMethod)(remoteBlocks, [], remoteOptions, []);
                if (remoteBlocks.length) {
                    path.node.body.push(t.classMethod('method', t.identifier('fetchRemoteOptions'), [], t.blockStatement(remoteBlocks), false, false, false, true));
                }
            }
            if (initCommonSqlQuery.length) {
                // 处理页面没有声明mounted action且有要在页面渲染完毕时处理的commonsql请求的场景
                const initBlocks = [];
                (0, ast_helpers_1.genCommonSqlMethod)(initBlocks, initCommonSqlQuery, [], []);
                if (initBlocks.length) {
                    path.node.body.push(t.classMethod('method', t.identifier('atStart'), [], t.blockStatement(initBlocks), false, false, false, true));
                }
            }
            actionCode.forEach(v => {
                const { actionName, async, params } = v;
                let formatParams = [];
                if (params && params.length > 0) {
                    formatParams = params.map((p) => {
                        if (t.isAssignmentPattern(p)) {
                            return t.assignmentExpression('=', p.left, p.right);
                        }
                        return p;
                    });
                }
                // 在页面组件引入当前action函数，特殊处理mounted与destroyed两个生命周期相关action
                switch (actionName) {
                    case 'mounted':
                        path.node.body.push(t.classMethod('method', t.identifier('componentDidMount'), params, t.blockStatement([
                            t.expressionStatement(t.callExpression(t.memberExpression(t.identifier(actionName), t.identifier('call')), [t.identifier('this'), ...formatParams])),
                        ]), false, false, false, async));
                        break;
                    case 'destroyed':
                        // path.node.body.push(t.classMethod('method', t.identifier('componentWillUnMount'), params, body, false, false, false, async));
                        path.node.body.push(t.classMethod('method', t.identifier('componentWillUnmount'), params, t.blockStatement([
                            t.expressionStatement(t.callExpression(t.memberExpression(t.identifier(actionName), t.identifier('call')), [t.identifier('this'), ...formatParams])),
                        ]), false, false, false, async));
                        break;
                    default:
                        // path.node.body.push(t.classProperty(t.identifier(actionName), t.arrowFunctionExpression(params, body, async)));
                        path.node.body.push(t.classProperty(t.identifier(actionName), t.arrowFunctionExpression(params, t.blockStatement([
                            t.returnStatement(t.callExpression(t.memberExpression(t.identifier(actionName), t.identifier('call')), [t.identifier('this'), ...formatParams])),
                        ]))));
                        break;
                }
            });
            if (Object.keys(extraClassMethod).length) {
                Object.keys(extraClassMethod).forEach(v => {
                    path.node.body.push(t.classProperty(t.identifier(v), extraClassMethod[v]));
                });
            }
            if (!hasMounted) {
                const mountExp = [];
                if (picklists.length) {
                    mountExp.push(t.expressionStatement(t.callExpression((0, utils_1.objValueStr2AST)('this.fetchPicklists'), [])));
                }
                if (remoteOptions.length) {
                    mountExp.push(t.expressionStatement(t.callExpression((0, utils_1.objValueStr2AST)('this.fetchRemoteOptions'), [])));
                }
                if (initCommonSqlQuery.length) {
                    mountExp.push(t.expressionStatement(t.callExpression((0, utils_1.objValueStr2AST)('this.atStart'), [])));
                }
                path.node.body.push(t.classMethod('method', t.identifier('componentDidMount'), [], t.blockStatement(mountExp), false, false, false, false));
            }
            lodashFns.forEach(fn => {
                path.parentPath.insertBefore(t.importDeclaration([t.importDefaultSpecifier(t.identifier(fn))], t.stringLiteral(`lodash/${fn}`)));
            });
            if (utilFns.length) {
                const importUtils = [];
                utilFns.forEach(v => {
                    importUtils.push(t.importSpecifier(t.identifier(v), t.identifier(v)));
                });
                path.parentPath.insertBefore(t.importDeclaration(importUtils, t.stringLiteral('@/utils/util')));
            }
            (0, ast_helpers_1.genConstructor)(path, pageVar, hasInit);
            (0, ast_helpers_1.genRenderMethod)(path);
        },
    });
    let { code } = generator(sast);
    code = code.replace('$DOM$', `(
          ${tplCode}
      )`);
    code = code.replace(/>{([\s\S]*?)}</g, (match, key) => {
        let newKey = key;
        let replaceArr = newKey
            .replace(/["|'](.*?)["|']/g, '')
            .replace(/true/g, '')
            .replace(/false/g, '')
            .match(/[a-zA-Z]([a-zA-Z]|[0-9]|_|\.)*/g) || [];
        replaceArr = Array.from(new Set(replaceArr));
        replaceArr.forEach(v => {
            if (v.indexOf('UploadSPLIT') !== -1) {
                let uploadModelValue = v.split('SPLIT')[1];
                const modelArr = uploadModelValue.split('.');
                if (modelArr.length > 1) {
                    uploadModelValue = modelArr.join('?.');
                }
                const uploadChildren = `this.state.${uploadModelValue} ? <img src={this.state.${uploadModelValue}} alt="avatar" style={{ width: '100%' }} /> 
                                                                : <div>
                                                                  <PlusOutlined />
                                                                  <div style={{ marginTop: 8 }}>Upload</div>
                                                                  </div>`;
                newKey = (0, utils_1.newReplaceAll)(newKey, v, uploadChildren);
            }
            else if (v.indexOf('.List.Current.') !== -1) {
                newKey = (0, utils_1.newReplaceAll)(newKey, v, v.replace(/[\S]*?\.List\.Current/, 'record'));
            }
            else if (v.indexOf('.Current.') !== -1) {
                newKey = (0, utils_1.newReplaceAll)(newKey, v, v.replace(/[\S]*?\.Current/, 'record'));
            }
            else if (pageVar.find(item => v.indexOf(item.name) !== -1 &&
                !item.isCommonSql &&
                v.indexOf(`.${item.name}`) === -1)) {
                newKey = (0, utils_1.newReplaceAll)(newKey, v, `this.state.${v}`);
            }
            else if (pageInput.find(item => v.indexOf(item.name) !== -1)) {
                newKey = (0, utils_1.newReplaceAll)(newKey, v, isPage ? `this.props.match.params.${v}` : `this.props.${v}`);
            }
        });
        return `>{${newKey}}<`;
    });
    Object.keys(conditionObj).forEach(key => {
        let value = conditionObj[key];
        let replaceArr = value
            .replace(/["|'](.*?)["|']/g, '')
            .replace(/true/g, '')
            .replace(/false/g, '')
            .match(/[a-zA-Z]([a-zA-Z]|[0-9]|_|\.)*/g) || [];
        replaceArr = Array.from(new Set(replaceArr));
        replaceArr.forEach(v => {
            // if (pageVar.find(item => v.indexOf(item.name) !== -1 && !item.isCommonSql) || v.indexOf('.List') !== -1) {
            //   value = newReplaceAll(value, v, transformAttrVar(v));
            // }
            if (v.indexOf('.List.Current.') !== -1) {
                value = (0, utils_1.newReplaceAll)(value, v, v.replace(/[\S]*?\.List\.Current/, 'record'));
            }
            else if (v.indexOf('.Current.') !== -1) {
                value = (0, utils_1.newReplaceAll)(value, v, v.replace(/[\S]*?\.Current/, 'record'));
            }
            else if (pageVar.find(item => v.indexOf(item.name) !== -1)) {
                value = (0, utils_1.newReplaceAll)(value, v, `this.state.${v}`);
            }
            else if (pageInput.find(item => v.indexOf(item.name) !== -1)) {
                value = (0, utils_1.newReplaceAll)(value, v, isPage ? `this.props.match.params.${v}` : `this.props.${v}`);
            }
        });
        code = code.replace(key, value);
    });
    code = prettier.format(code, {
        parser: 'babel',
        plugins: [parserBabel, parserHtml],
    });
    return { code, actionCodeMapList, errorInfo };
};
exports.default = codeGenerator;
