"use strict";
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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
// import { chalk, fsExtra as fs, logger } from '@fishx/base-utils';
const chalk_1 = require("chalk");
const fs = require("fs-extra");
const path = require("path");
const checkRoot_1 = require("./lowcode-generator/utils/generate/checkRoot");
const getProjectType_1 = require("./lowcode-generator/utils/generate/getProjectType");
const createAppDir_1 = require("./lowcode-generator/utils/generate/createAppDir");
const customizeAppInfo_1 = require("./lowcode-generator/utils/generate/customizeAppInfo");
const dirTree_1 = require("./lowcode-generator/utils/dirTree");
const t = require("@babel/types");
const generator_1 = require("@babel/generator");
const traverse_1 = require("@babel/traverse");
const parser_1 = require("@babel/parser");
// 去掉后面'/'
function formatPath(path) {
    if (!path) {
        return '';
    }
    if (path !== '/' && path.endsWith('/')) {
        return path.substring(0, path.length - 1);
    }
    return path;
}
function getRoutePath(routePath) {
    const pathsList = routePath.split(':');
    const onlyPath = pathsList[0];
    return formatPath(onlyPath);
}
function getRouteNode(path, exact, component) {
    return t.objectExpression([
        t.objectProperty(t.identifier('path'), t.stringLiteral(path)),
        t.objectProperty(t.identifier('exact'), t.booleanLiteral(exact)),
        t.objectProperty(t.identifier('component'), t.stringLiteral(component)),
    ]);
}
/**
 * 更新路由配置
 */
function updateRuterConfig(newRouteInfo, isForce, root) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const { component, params, exact = true } = newRouteInfo;
            let routesPath = path.join(root, 'src', 'config/routes.js');
            if (!fs.existsSync(routesPath)) {
                routesPath = path.join(root, 'src', 'config/routes.ts');
            }
            if (fs.existsSync(routesPath)) {
                const urlPath = formatPath(newRouteInfo.path);
                let wholePath = urlPath;
                if (params) {
                    wholePath += urlPath === '/' ? params : `/${params}`;
                }
                const routesFile = yield fs.readFile(routesPath, 'utf8');
                const ast = (0, parser_1.parse)(routesFile, {
                    sourceType: 'module',
                    plugins: ['typescript'],
                });
                (0, traverse_1.default)(ast, {
                    ExportDefaultDeclaration: (path) => {
                        const { node } = path;
                        // export default is array
                        if (t.isArrayExpression(node.declaration)) {
                            path.traverse({
                                // 直接输出数组
                                ArrayExpression: (path) => {
                                    let isUpdate = false;
                                    path.traverse({
                                        ObjectProperty: (innerPath) => {
                                            const { node } = innerPath;
                                            if (t.isIdentifier(node.key) &&
                                                t.isStringLiteral(node.value) &&
                                                node.key.name === 'path' &&
                                                getRoutePath(node.value.value) === urlPath) {
                                                if (!isForce) {
                                                    throw (0, chalk_1.red)(`
Error: Route Pathname "${newRouteInfo.path}" exist, config/routes updated failed!
                        `);
                                                }
                                                else {
                                                    console.log((0, chalk_1.yellow)(`
Warning: Route Pathname "${newRouteInfo.path}" already exist, replaced with ${wholePath}
                        `));
                                                }
                                                isUpdate = true;
                                                node.value.value = wholePath;
                                                const parentNode = innerPath.parentPath.node;
                                                Array.isArray(parentNode.properties) &&
                                                    parentNode.properties.forEach((item) => {
                                                        if (t.isObjectProperty(item) && t.isIdentifier(item.key) && item.key.name === 'component') {
                                                            item.value.value = component;
                                                        }
                                                    });
                                                innerPath.stop();
                                                return;
                                            }
                                        },
                                    });
                                    if (!isUpdate) {
                                        const { node } = path;
                                        if (t.isArrayExpression(node)) {
                                            node.elements.push(getRouteNode(wholePath, !!exact, component));
                                        }
                                    }
                                },
                            });
                        }
                        // export default is a variable
                        if (t.isIdentifier(node.declaration)) {
                            const { name } = node.declaration; // routes数组变量名称
                            const bindingPath = path.parentPath.scope.bindings[name];
                            if (bindingPath && bindingPath.path) {
                                const routesListPath = bindingPath.path.get('init');
                                if (t.isArrayExpression(routesListPath.node)) {
                                    let isUpdate = false;
                                    routesListPath.traverse({
                                        ObjectProperty: (innerPath) => {
                                            const { node } = innerPath;
                                            if (t.isIdentifier(node.key) &&
                                                t.isStringLiteral(node.value) &&
                                                node.key.name === 'path' &&
                                                getRoutePath(node.value.value) === urlPath) {
                                                if (!isForce) {
                                                    throw (0, chalk_1.red)(`
Error: Route Pathname "${newRouteInfo.path}" exist, config/routes updated failed!
                        `);
                                                }
                                                else {
                                                    console.log((0, chalk_1.yellow)(`
Warning: Route Pathname "${newRouteInfo.path}" already exist, replaced with ${wholePath}
                        `));
                                                }
                                                isUpdate = true;
                                                node.value.value = wholePath;
                                                const parentNode = innerPath.parentPath.node;
                                                t.isObjectExpression(parentNode) &&
                                                    parentNode.properties.forEach((item) => {
                                                        if (t.isObjectProperty(item) &&
                                                            t.isIdentifier(item.key) &&
                                                            t.isStringLiteral(item.value) &&
                                                            item.key.name === 'component') {
                                                            item.value.value = component;
                                                        }
                                                    });
                                                innerPath.stop();
                                                return;
                                            }
                                        },
                                    });
                                    if (!isUpdate) {
                                        routesListPath.node.elements.push(getRouteNode(wholePath, !!exact, component));
                                    }
                                }
                            }
                        }
                    },
                });
                const output = (0, generator_1.default)(ast, {
                    /* options */
                    sourceMaps: false,
                }, routesFile);
                fs.writeFile(routesPath, output.code, 'utf8');
            }
        }
        catch (err) {
            console.log(err);
        }
    });
}
exports.default = (flags) => __awaiter(void 0, void 0, void 0, function* () {
    // 判断当前目录是否为 项目的根目录
    const root = (0, checkRoot_1.checkRoot)(process.cwd());
    // 获取当前项目类型 ts 或 js
    const templateType = (0, getProjectType_1.getProjectType)(root);
    const isForce = flags.force;
    let projectName;
    let projectPath;
    let urlParam;
    if (flags.page) {
        projectName = flags.page;
    }
    if (flags.path) {
        projectPath = flags.path;
        urlParam = flags.param || '';
        if (!/^\//.test(projectPath)) {
            return console.log((0, chalk_1.yellow)('required project path Format error, eg: '), (0, chalk_1.green)('fishx generate --path /home'));
        }
    }
    if (flags.component) {
        projectName = flags.component;
    }
    if (!projectName && !projectPath) {
        return console.log((0, chalk_1.yellow)('required project name or path, eg: '), (0, chalk_1.green)('fishx generate --page Home --path /home'));
    }
    console.log('---------------', urlParam, isForce);
    // 当projectPath === '/'时，在pages下创建 projectName.tsx文件即可
    if (projectPath && projectPath === '/') {
        // 如果pages目录下有index. 结束
        let pageIndexPath = path.join(root, 'src', 'pages/index.js');
        if (templateType === 'ts') {
            pageIndexPath = path.join(root, 'src', 'pages/index.tsx');
        }
        try {
            if (!fs.existsSync(pageIndexPath)) {
                if (!projectName) {
                    projectName = projectPath.split('/')[1];
                }
                projectName = projectName.replace(projectName[0], projectName[0].toUpperCase());
                const templateDir = path.join(__dirname, '..', 'pages/index', templateType);
                const pagePath = path.join(root, 'src', 'pages');
                yield fs.copy(templateDir, pagePath, {
                    overwrite: true,
                });
                // 模板语法 替换
                (0, customizeAppInfo_1.replaceFileName)(templateType === 'js' ? pagePath + '/Sample.js' : pagePath + '/Sample.tsx', projectName);
                projectName = projectName.replace(projectName[0], projectName[0].toLowerCase());
                // 更新路由文件
                yield updateRuterConfig({
                    path: projectPath,
                    exact: true,
                    component: `./${projectName}`,
                    params: urlParam,
                }, isForce, root);
                console.log((0, chalk_1.green)('generate successful!'));
                console.log();
            }
            else {
                // 只更新routes文件
                if (!projectName) {
                    projectName = projectPath.split('/')[1];
                }
                projectName = projectName.replace(projectName[0], projectName[0].toLowerCase());
                // 更新路由参数
                yield updateRuterConfig({
                    path: projectPath,
                    exact: true,
                    component: `./${projectName}`,
                    params: urlParam,
                }, isForce, root);
            }
        }
        catch (error) {
            // @ts-ignore
            console.error(error);
        }
    }
    else {
        if (!projectPath) {
            projectPath = `./${projectName}`;
        }
        if (!projectName) {
            projectName = projectPath.split('/')[1];
        }
        projectName = projectName.replace(projectName[0], projectName[0].toUpperCase());
        const templateDir = path.join(__dirname, '..', 'pages', templateType);
        console.log(`generating ${projectName}...`);
        try {
            const pagePath = path.join(root, 'src', 'pages');
            const isFileExisted = fs.readdirSync(pagePath).includes(projectName);
            let oldRoutePath;
            if (isFileExisted && isForce) {
                console.log((0, chalk_1.yellow)(`Found ${projectName}, replacing...`));
                fs.removeSync(path.join(pagePath, projectName));
                oldRoutePath = projectPath;
            }
            const projectDir = (0, createAppDir_1.createAppDir)(pagePath, projectName);
            yield fs.copy(templateDir, projectDir, {
                overwrite: true,
            });
            // 模板语法 替换
            (0, customizeAppInfo_1.customizeAppInfo)(projectDir, projectName);
            // 更新路由信息
            yield updateRuterConfig({
                path: projectPath,
                exact: true,
                component: `./${projectName}/index`,
                params: urlParam,
            }, isForce, root);
            console.log((0, chalk_1.green)('generate successful!'));
            console.log();
            (0, dirTree_1.default)(root, projectDir, projectName);
        }
        catch (error) {
            // @ts-ignore
            console.log(error);
        }
    }
});
