var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
const fs = require('fs');
const path = require('path');
const { parse } = require('@babel/parser');
const traverse = require('@babel/traverse');
const t = require('@babel/types');
const { spawn } = require('child_process');
const upperFirst = require('lodash/upperFirst');
const cheerio = require('cheerio');
const { codeGenerator } = require('./lowcode-generator');
const { preParseGtLt, preParseMustache } = require('./utils');
function buildPage(args) {
    return new Promise((res, rej) => {
        const workerProcess = spawn('node', [
            path.join(__dirname, './createPage'),
            JSON.stringify(args),
        ]);
        workerProcess.stdout.on('data', (data) => {
            console.log(`stdout: ${data}`);
            if (data.indexOf('generate successful!') !== -1) {
                res(null);
            }
            if (data.indexOf('replaced with') !== -1) {
                res(null);
            }
        });
        workerProcess.stderr.on('data', (data) => {
            console.log(`stderr: ${data}`);
            if (data.indexOf('npm ERR!') !== -1) {
                rej();
            }
        });
    });
}
const getForListItem = blockDsl => {
    const forItems = [];
    // 预处理：<   >  ==>  _cml_lt_lmc_  _cml_gt_lmc_,这么做的目的为了防止 preParseMustache 解析 > < 中间的内容报错，所以需要将 > < 先转化 gt lt的形式，等 preParseMustache 解析完毕之后即可将其转化回来；
    let content = preParseGtLt(blockDsl);
    // 预处理 标签内的 {{item.id}} 这种语法jsx无法识别，转化为 {item.id}
    content = preParseMustache(content);
    const ast = parse(content, {
        plugins: ['jsx'],
    });
    traverse.default(ast, {
        JSXAttribute(path) {
            const { node } = path;
            if (t.isJSXIdentifier(node.name) && node.name.name === 'v-for') {
                const item = node.value.value.split(' in ')[0];
                if (forItems.indexOf(item) === -1) {
                    forItems.push(item);
                }
            }
        },
    });
    return forItems;
};
const getPlaceholdersByBlock = blockDsl => {
    const placeholders = [];
    // 预处理：<   >  ==>  _cml_lt_lmc_  _cml_gt_lmc_,这么做的目的为了防止 preParseMustache 解析 > < 中间的内容报错，所以需要将 > < 先转化 gt lt的形式，等 preParseMustache 解析完毕之后即可将其转化回来；
    let content = preParseGtLt(blockDsl);
    // 预处理 标签内的 {{item.id}} 这种语法jsx无法识别，转化为 {item.id}
    content = preParseMustache(content);
    const ast = parse(content, {
        plugins: ['jsx'],
    });
    traverse.default(ast, {
        JSXIdentifier(path) {
            const { node } = path;
            if (node.name === 'placeholder') {
                const { attributes = [] } = path.container || {};
                const targetAttrItem = attributes.find(v => v.name.name === 'name');
                const paramAttrItem = attributes.find(v => v.name.name === 'parameter');
                if (targetAttrItem &&
                    placeholders.findIndex(v => v.name === targetAttrItem.value.value) ===
                        -1) {
                    const obj = {};
                    obj.name = targetAttrItem.value.value;
                    if (paramAttrItem) {
                        obj.params = paramAttrItem.value.value.split(',');
                    }
                    placeholders.push(obj);
                }
            }
        },
    });
    return placeholders;
};
function createPage({ projectPath, pagePath, pageContent, pageName, cssStr = '', actionCodeMapList, }) {
    return __awaiter(this, void 0, void 0, function* () {
        const pathWitParamsList = pagePath.split(':');
        const urlPath = pathWitParamsList.shift();
        const isHome = urlPath === '/';
        let urlParams = '';
        if (pathWitParamsList.length > 0) {
            urlParams = pathWitParamsList.join(':');
            urlParams = `:${urlParams}`;
        }
        const filePath = isHome
            ? `${projectPath}/generatedCode/frontend/src/pages/index.tsx`
            : `${projectPath}/generatedCode/frontend/src/pages/${pageName}/index.tsx`;
        const cssPath = isHome
            ? `${projectPath}/generatedCode/frontend/src/pages/index.css`
            : `${projectPath}/generatedCode/frontend/src/pages/${pageName}/index.css`;
        yield buildPage({
            pageName,
            pagePath: urlPath,
            basePath: projectPath,
            urlParams,
        });
        fs.writeFileSync(cssPath, cssStr);
        fs.writeFileSync(filePath, pageContent);
        // 创建client action编译代码和map文件
        Array.isArray(actionCodeMapList) &&
            actionCodeMapList.forEach(item => {
                const actionCodePath = isHome
                    ? `${projectPath}/generatedCode/frontend/src/pages/lc-actions`
                    : `${projectPath}/generatedCode/frontend/src/pages/${pageName}/lc-actions`;
                if (!fs.existsSync(actionCodePath)) {
                    fs.mkdirSync(actionCodePath, { recursive: true });
                }
                fs.writeFileSync(`${actionCodePath}/${item.name}.ts`, item.generatedCode);
                fs.writeFileSync(`${actionCodePath}/${item.name}.ts.map`, JSON.stringify(item.map));
            });
    });
}
function createBlock({ projectPath, blockName, blockContent, cssStr, actionCodeMapList, }) {
    const filePath = `${projectPath}/generatedCode/frontend/src/components/${blockName}`;
    if (!fs.existsSync(filePath)) {
        fs.mkdirSync(filePath);
    }
    fs.writeFileSync(path.join(filePath, 'index.tsx'), blockContent);
    fs.writeFileSync(path.join(filePath, 'index.css'), cssStr);
    // 创建client action编译代码和map文件
    Array.isArray(actionCodeMapList) &&
        actionCodeMapList.forEach(item => {
            const actionCodePath = `${projectPath}/generatedCode/frontend/src/components/${blockName}/lc-actions`;
            if (!fs.existsSync(actionCodePath)) {
                fs.mkdirSync(actionCodePath, { recursive: true });
            }
            fs.writeFileSync(`${actionCodePath}/${item.name}.ts`, item.generatedCode);
            fs.writeFileSync(`${actionCodePath}/${item.name}.ts.map`, JSON.stringify(item.map));
        });
}
function createWidget({ projectPath, widgetName, widgetContent, cssStr, actionCodeMapList, }) {
    const filePath = `${projectPath}/generatedCode/frontend/src/widgets/${widgetName}`;
    if (!fs.existsSync(filePath)) {
        fs.mkdirSync(filePath);
    }
    fs.writeFileSync(path.join(filePath, 'index.tsx'), widgetContent);
    fs.writeFileSync(path.join(filePath, 'index.css'), cssStr);
    // 创建client action编译代码和map文件
    Array.isArray(actionCodeMapList) &&
        actionCodeMapList.forEach(item => {
            const actionCodePath = `${projectPath}/generatedCode/frontend/src/widgets/${widgetName}/lc-actions`;
            if (!fs.existsSync(actionCodePath)) {
                fs.mkdirSync(actionCodePath, { recursive: true });
            }
            fs.writeFileSync(`${actionCodePath}/${item.name}.ts`, item.generatedCode);
            fs.writeFileSync(`${actionCodePath}/${item.name}.ts.map`, JSON.stringify(item.map));
        });
}
const compileProject = (projectPath, projectName, client) => __awaiter(this, void 0, void 0, function* () {
    const resObj = {
        success: true,
        failList: [],
    };
    let errorText = '';
    try {
        const uiPath = `${projectPath}/UI/UI Flows`;
        const blocks = fs.readFileSync(`${projectPath}/UI/UI Flows/blocks.json`);
        const allBlocks = JSON.parse(blocks.toString());
        const allPages = [];
        let projectData = {};
        let uiDirs = fs.readdirSync(uiPath);
        uiDirs = uiDirs.filter(v => v !== 'blocks.json' && v !== '.DS_Store');
        uiDirs.forEach(ui => {
            const pagePath = path.join(uiPath, ui);
            let pageDirs = fs.readdirSync(pagePath);
            pageDirs = pageDirs.filter(v => v !== '.DS_Store');
            pageDirs.forEach(v => {
                projectData = Object.assign(Object.assign({}, projectData), getPageData(pagePath, ui, v));
                if (allBlocks.findIndex(item => item === `${ui}/${v}`) === -1 &&
                    ui !== 'Widget' &&
                    projectData[`${ui}/${v}`]) {
                    allPages.push({
                        name: v,
                        path: `${ui}/${v}`,
                        metaData: projectData[`${ui}/${v}`].xml,
                        componentType: 'page',
                    });
                }
            });
        });
        let allWidgets = [];
        if (fs.existsSync(path.join(projectPath, '/UI/UI Flows/Widget'))) {
            allWidgets = getWidgetData(path.join(projectPath, '/UI/UI Flows/Widget'));
        }
        const { blockNames, allBlocks: _allBlocks } = getBlockData(uiPath, allBlocks);
        const allScreen = allPages.concat(_allBlocks).concat(allWidgets);
        // 获取所有内置函数
        const utilFunctions = [];
        const baseTypes = fs
            .readFileSync(`${projectPath}/Data/Interface/utilFunctions.ts`)
            .toString();
        const baseAst = parse(baseTypes, {
            plugins: ['typescript'],
        });
        traverse.default(baseAst, {
            VariableDeclaration(path) {
                if (path.node && t.isIdentifier(path.node.declarations[0].id)) {
                    utilFunctions.push(path.node.declarations[0].id.name);
                }
            },
        });
        for (let i = 0; i < allScreen.length; i += 1) {
            const element = allScreen[i];
            const pagePath = element.path;
            const pageName = element.name;
            const { componentType } = element;
            const xmlContent = element.metaData;
            let cssStr = '';
            // @ts-ignore
            const template = xmlContent.replace(/<style>([\s\S]*?)<\/style>/g, (match, key) => {
                cssStr = key.trim();
                return '';
            });
            const pageData = {};
            for (const key in projectData) {
                // eslint-disable-line
                if (Object.prototype.hasOwnProperty.call(projectData, key)) {
                    const _element = projectData[key];
                    if (key.indexOf(pagePath) !== -1) {
                        pageData[key] = _element;
                    }
                }
            }
            const { code, actionCodeMapList, errorInfo } = codeGenerator({
                template,
                pageName,
                pagePath,
                pageData,
                projectName,
                isPage: componentType === 'page',
                blocks: blockNames,
                utilFunctions,
            });
            if (!errorInfo.type) {
                if (componentType === 'page') {
                    yield createPage({
                        projectPath,
                        pagePath: projectData[pagePath].path,
                        pageContent: code,
                        pageName: upperFirst(pageName),
                        cssStr,
                        actionCodeMapList,
                    });
                }
                if (componentType === 'block') {
                    createBlock({
                        projectPath,
                        blockName: upperFirst(pageName),
                        blockContent: code,
                        cssStr,
                        actionCodeMapList,
                    });
                }
                if (componentType === 'widget') {
                    createWidget({
                        projectPath,
                        widgetName: upperFirst(pageName),
                        widgetContent: code,
                        cssStr,
                        actionCodeMapList,
                    });
                }
                client.onMessage(`${pagePath} compiled successful (${i + 1}/${allScreen.length})`);
            }
            else {
                errorText += `${errorInfo.message}`;
                resObj.success = false;
                resObj.errorInfo = errorText;
                resObj.parseCode = errorInfo.parseCode;
                resObj.failList.push(pagePath);
                client.onMessage(`Warning-${pagePath} compiled failed`);
            }
        }
        return resObj;
    }
    catch (error) {
        resObj.success = false;
        resObj.errorInfo = `${error.name}：${error.message}`;
        return resObj;
    }
});
const getPageData = (uiPath, dir, pageName) => {
    const pageData = {};
    try {
        const pagePath = path.join(uiPath, pageName);
        const content = fs.readFileSync(path.join(pagePath, 'var.json'));
        const xml = fs.readFileSync(path.join(pagePath, `${pageName}.xml`));
        pageData[`${dir}/${pageName}`] = JSON.parse(content.toString());
        pageData[`${dir}/${pageName}`].pageAction = [];
        pageData[`${dir}/${pageName}`].commonSql = [];
        pageData[`${dir}/${pageName}`].nativeSql = [];
        pageData[`${dir}/${pageName}`].xml = xml.toString();
        const files = fs.readdirSync(pagePath);
        files.forEach((item, index) => {
            const itemContent = fs.readFileSync(path.join(pagePath, item));
            if (item.indexOf('.ts') !== -1) {
                pageData[`${dir}/${pageName}`].pageAction.push({
                    name: item.split('.')[0],
                });
                pageData[`${dir}/${pageName}/${item.split('.')[0]}`] =
                    itemContent.toString();
            }
            if (item.indexOf('.dq.json') !== -1) {
                pageData[`${dir}/${pageName}`].commonSql.push({
                    name: item.split('.')[0],
                    data: itemContent.toString(),
                });
            }
            if (item.indexOf('.sql.xml') !== -1) {
                const variables = getNativeSqlParams(itemContent);
                pageData[`${dir}/${pageName}`].nativeSql.push({
                    name: item.split('.')[0],
                    data: JSON.stringify(variables),
                });
            }
        });
        return pageData;
    }
    catch (e) {
        return pageData;
    }
};
const getWidgetData = uiPath => {
    let allWidgets = fs.readdirSync(uiPath);
    allWidgets = allWidgets.filter(v => v !== '.gitkeep');
    return allWidgets.map(widget => {
        const widgetContent = fs.readFileSync(path.join(uiPath, widget, `${widget}.xml`));
        return {
            name: widget,
            path: `Widget/${widget}`,
            metaData: widgetContent.toString(),
            componentType: 'widget',
        };
    });
};
const getBlockData = (uiPath, blocks) => {
    let allBlocks = blocks;
    allBlocks = allBlocks.map(block => {
        const blockContent = fs.readFileSync(path.join(uiPath, block, `${block.split('/')[1]}.xml`));
        let blockData = fs.readFileSync(path.join(uiPath, block, 'var.json'));
        blockData = JSON.parse(blockData.toString());
        return {
            blockData,
            path: block,
            name: block.split('/')[1],
            metaData: blockContent.toString(),
            componentType: 'block',
        };
    });
    const blockNames = allBlocks.map(_b => {
        const template = _b.metaData.replace(/<style>([\s\S]*?)<\/style>/g, () => {
            return '';
        });
        return {
            name: _b.name,
            path: _b.path,
            blockData: _b.blockData,
            placeholders: getPlaceholdersByBlock(template),
            forItems: getForListItem(template),
        };
    });
    return { allBlocks, blockNames };
};
// 获取 nativeSql 输入参数
const getNativeSqlParams = (data) => {
    const $xmlDom = cheerio.load(data, { xml: { xmlMode: true } });
    const result = {
        params: [],
        pageInfo: {
            pageSize: null,
            pageIndex: null,
        },
    };
    if ($xmlDom('inputs').children('input').length > 0) {
        $xmlDom('inputs').children('input').each(function (i, elem) {
            if (['pageSize', 'pageIndex'].includes($xmlDom(this).attr('name'))) {
                result.pageInfo[$xmlDom(this).attr('name')] = $xmlDom(this).val();
            }
            else {
                result.params.push($xmlDom(this).attr('name'));
            }
        });
    }
    return result;
};
const compileModule = (projectPath, module) => __awaiter(this, void 0, void 0, function* () {
    const resObj = {
        success: true,
    };
    let errorText = '';
    let cssStr = '';
    try {
        const [type, ui, pageName] = module.split('-');
        const projectConfigContent = fs
            .readFileSync(path.join(projectPath, '.projectType'))
            .toString();
        const projectName = projectConfigContent.match(/projectName\s=\s(\S*?);/)[1];
        const uiPath = `${projectPath}/UI/UI Flows`;
        const isPage = type === 'page';
        const pagePath = path.join(uiPath, ui);
        const blocks = fs.readFileSync(`${projectPath}/UI/UI Flows/blocks.json`);
        const allBlocks = JSON.parse(blocks.toString());
        const pageData = getPageData(pagePath, ui, pageName);
        const { blockNames } = getBlockData(uiPath, allBlocks);
        let template;
        if (pageData[`${ui}/${pageName}`]) {
            template = pageData[`${ui}/${pageName}`].xml.replace(/<style>([\s\S]*?)<\/style>/g, (match, key) => {
                cssStr = key.trim();
                return '';
            });
        }
        // 获取所有内置函数
        const utilFunctions = [];
        const baseTypes = fs
            .readFileSync(`${projectPath}/Data/Interface/utilFunctions.ts`)
            .toString();
        const baseAst = parse(baseTypes, {
            plugins: ['typescript'],
        });
        traverse.default(baseAst, {
            VariableDeclaration(path) {
                if (path.node && t.isIdentifier(path.node.declarations[0].id)) {
                    utilFunctions.push(path.node.declarations[0].id.name);
                }
            },
        });
        const { code, errorInfo, actionCodeMapList } = codeGenerator({
            template,
            pageName,
            pageData,
            pagePath: `${ui}/${pageName}`,
            projectName,
            isPage,
            blocks: blockNames,
            utilFunctions,
        });
        if (!errorInfo.type) {
            if (type === 'page') {
                yield createPage({
                    projectPath,
                    pagePath: pageData[`${ui}/${pageName}`].path,
                    pageContent: code,
                    pageName: upperFirst(pageName),
                    cssStr,
                    actionCodeMapList,
                });
            }
            if (type === 'block') {
                createBlock({
                    projectPath,
                    blockName: upperFirst(pageName),
                    blockContent: code,
                    cssStr,
                    actionCodeMapList,
                });
            }
            if (type === 'widget') {
                createWidget({
                    projectPath,
                    widgetName: upperFirst(pageName),
                    widgetContent: code,
                    cssStr,
                    actionCodeMapList,
                });
            }
        }
        else {
            errorText += `${errorInfo.message}`;
            resObj.success = false;
            resObj.errorInfo = errorText;
        }
        return resObj;
    }
    catch (error) {
        console.error(error);
        resObj.success = false;
        resObj.errorInfo = error;
        return resObj;
    }
});
module.exports = {
    compileProject,
    compileModule,
};
