"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.clone = clone;
const path_1 = __importDefault(require("path"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const cross_spawn_1 = __importDefault(require("cross-spawn"));
const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
const inquirer_1 = __importDefault(require("inquirer"));
const ora_1 = __importDefault(require("ora"));
const chalk_1 = __importDefault(require("chalk"));
const logger_1 = require("../utils/logger");
const prompt_1 = require("../utils/prompt");
const template_1 = require("../utils/template");
const file_1 = require("../utils/file");
const cache_1 = require("../utils/cache");
const projectApi_1 = require("../utils/projectApi");
const dependencies_1 = require("../constants/dependencies");
const DeclareXmlToTs_1 = require("../utils/DeclareXmlToTs");
async function clone(projectVersionId, options) {
    (0, logger_1.printBanner)();
    let authorization;
    try {
        authorization = await resolveAuthorization(options.authorization);
    }
    catch (error) {
        if ((0, cache_1.isAuthorizationInvalidError)(error)) {
            await (0, cache_1.clearAuthorizationCache)();
        }
        logger_1.logger.error(toReadableError(error, '授权信息无效，请检查 authorization'));
        process.exit(1);
        return;
    }
    const treeSpinner = (0, ora_1.default)('正在获取中台项目结构...').start();
    let treeData;
    try {
        treeData = await (0, projectApi_1.getProjectTree)(projectVersionId, authorization);
        treeSpinner.succeed('中台项目结构获取成功');
    }
    catch (error) {
        treeSpinner.fail('中台项目结构获取失败');
        if ((0, cache_1.isAuthorizationInvalidError)(error)) {
            await (0, cache_1.clearAuthorizationCache)();
        }
        logger_1.logger.error(toReadableError(error, '未知错误'));
        process.exit(1);
        return;
    }
    const projectName = resolveProjectName(options.projectName, treeData.title, projectVersionId);
    const validationResult = (0, validate_npm_package_name_1.default)(projectName);
    if (!validationResult.validForNewPackages) {
        logger_1.logger.error(`项目名称无效: ${projectName}`);
        process.exit(1);
    }
    const targetDir = path_1.default.resolve(process.cwd(), projectName);
    if (fs_extra_1.default.existsSync(targetDir)) {
        if (options.force) {
            logger_1.logger.warn(`强制覆盖目录: ${targetDir}`);
            await selectiveOverwrite(targetDir);
        }
        else {
            const overwrite = await (0, prompt_1.promptOverwrite)();
            if (!overwrite) {
                logger_1.logger.info('操作已取消');
                process.exit(0);
            }
            await selectiveOverwrite(targetDir);
        }
    }
    const templateDir = (0, file_1.getTemplateDir)('full');
    if (!fs_extra_1.default.existsSync(templateDir)) {
        logger_1.logger.error(`模板目录不存在: ${templateDir}`);
        process.exit(1);
    }
    const templateData = (0, template_1.createTemplateData)(projectName, projectVersionId, authorization, {
        description: treeData.desc || '',
        isSimple: false,
        author: '',
        features: {
            sharedLib: false,
            redis: false,
            mq: false,
            uts: false,
        },
    });
    const renderSpinner = (0, ora_1.default)('正在生成基础项目...').start();
    try {
        await (0, template_1.renderTemplate)(templateDir, targetDir, templateData);
        renderSpinner.succeed('基础项目生成完成');
    }
    catch (error) {
        renderSpinner.fail('基础项目生成失败');
        logger_1.logger.error(error.message || '未知错误');
        process.exit(1);
    }
    const packageJsonPath = path_1.default.join(targetDir, 'package.json');
    const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
    let sharedLibs = [];
    let thirdLibs = [];
    try {
        sharedLibs = await (0, projectApi_1.getProjectSharedLibs)(projectVersionId, authorization);
    }
    catch (error) {
        logger_1.logger.warn(`共享库列表获取失败，按空数组处理: ${error.message || '未知错误'}`);
        sharedLibs = [];
    }
    try {
        thirdLibs = await (0, projectApi_1.getProjectThirdLibs)(projectVersionId, authorization);
    }
    catch (error) {
        logger_1.logger.warn(`第三方开源库列表获取失败，按空数组处理: ${error.message || '未知错误'}`);
        thirdLibs = [];
    }
    packageJson.name = projectName;
    packageJson.description = treeData.desc || '';
    packageJson.author = '';
    packageJson.dependencies = {
        ...dependencies_1.BASE_DEPENDENCIES,
        ...dependencies_1.INTERNAL_DEPENDENCIES,
    };
    for (const lib of sharedLibs) {
        if (!lib.pid) {
            continue;
        }
        packageJson.dependencies[lib.pid] = lib.version || packageJson.dependencies[lib.pid] || 'latest';
    }
    for (const lib of thirdLibs) {
        if (!lib.libName) {
            continue;
        }
        packageJson.dependencies[lib.libName] = lib.libVersion || packageJson.dependencies[lib.libName] || 'latest';
    }
    packageJson.devDependencies = dependencies_1.DEV_DEPENDENCIES;
    packageJson.uploadConfig = {
        ...dependencies_1.UPLOAD_CONFIG,
        projectVersionId,
        projectId: treeData.projectId || '',
        sharedLibs: sharedLibs.map((item) => ({ pid: item.pid, msAlias: item.msAlias })),
        thirdLibs: thirdLibs.map((item) => ({ libName: item.libName, libVersion: item.libVersion })),
    };
    await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
    const remoteFiles = collectRemoteFiles(treeData.projectTree);
    const remoteFolders = collectRemoteFolders(treeData.projectTree);
    const downloadSpinner = (0, ora_1.default)(`正在拉取中台代码（${remoteFiles.length} 个文件，${remoteFolders.length} 个文件夹）...`).start();
    try {
        let completed = 0;
        for (const folder of remoteFolders) {
            const folderPath = path_1.default.join(targetDir, folder.relativePath);
            logger_1.logger.info(`创建文件夹: ${folder.relativePath}`);
            await fs_extra_1.default.ensureDir(folderPath);
        }
        for (const file of remoteFiles) {
            let content = await (0, projectApi_1.getTextFile)(projectVersionId, file.fileId, authorization, file.lockId);
            if (file.convertFromXmlDeclare) {
                content = (0, DeclareXmlToTs_1.convertDeclareXmlContentToTs)(content, file.serviceName || 'ServiceDeclare');
            }
            const targetPath = path_1.default.join(targetDir, file.relativePath);
            await fs_extra_1.default.ensureDir(path_1.default.dirname(targetPath));
            await fs_extra_1.default.writeFile(targetPath, content, 'utf-8');
            completed += 1;
            downloadSpinner.text = `正在拉取中台代码（${completed}/${remoteFiles.length}）...`;
        }
        downloadSpinner.succeed(`中台代码拉取完成，共 ${remoteFiles.length} 个文件，${remoteFolders.length} 个文件夹`);
    }
    catch (error) {
        downloadSpinner.fail('中台代码拉取失败');
        if ((0, cache_1.isAuthorizationInvalidError)(error)) {
            await (0, cache_1.clearAuthorizationCache)();
        }
        logger_1.logger.error(toReadableError(error, '未知错误'));
        process.exit(1);
    }
    if (!options.skipInstall) {
        const installSpinner = (0, ora_1.default)('正在安装依赖...').start();
        try {
            const registry = options.registry || 'https://dev365.keytop.cn/nexus/repository/npm-group/';
            await installDependencies(targetDir, registry);
            installSpinner.succeed('依赖安装完成');
        }
        catch (error) {
            installSpinner.fail('依赖安装失败');
            logger_1.logger.warn('请手动运行: pnpm install');
        }
    }
    logger_1.logger.newline();
    logger_1.logger.success(chalk_1.default.green('项目拉取成功!'));
    console.log();
    console.log(chalk_1.default.cyan('📁 项目位置:'), targetDir);
    console.log(chalk_1.default.cyan('📦 项目名称:'), projectName);
    console.log(chalk_1.default.cyan('🆔 projectVersionId:'), projectVersionId);
    console.log(chalk_1.default.cyan('📄 拉取文件数:'), remoteFiles.length);
    console.log();
    console.log(chalk_1.default.yellow('下一步操作:'));
    console.log(chalk_1.default.gray(`  cd ${projectName}`));
    console.log(chalk_1.default.gray('  pnpm run dev'));
    console.log();
}
function resolveProjectName(inputName, title, projectVersionId) {
    if (inputName && inputName.trim()) {
        return inputName.trim();
    }
    if (title) {
        const normalized = title
            .split('@')[0]
            .toLowerCase()
            .replace(/[^a-z0-9._-]+/g, '-')
            .replace(/^[^a-z0-9]+/, '')
            .replace(/-+/g, '-')
            .replace(/[-._]+$/, '');
        if (normalized) {
            return normalized;
        }
    }
    return `mid-clone-${projectVersionId.slice(0, 8)}`;
}
async function resolveAuthorization(authorizationOption) {
    const configAuthorization = await readAuthorizationFromConfig();
    if (configAuthorization) {
        try {
            await (0, projectApi_1.getUserInfo)(configAuthorization);
            await (0, cache_1.saveAuthorization)(configAuthorization);
            return configAuthorization;
        }
        catch (error) {
        }
    }
    if (authorizationOption && authorizationOption.trim()) {
        const auth = authorizationOption.trim();
        await (0, projectApi_1.getUserInfo)(auth);
        await (0, cache_1.saveAuthorization)(auth);
        return auth;
    }
    const cachedAuth = await (0, cache_1.getAuthorization)();
    if (cachedAuth) {
        return cachedAuth;
    }
    const answers = await inquirer_1.default.prompt([
        {
            type: 'input',
            name: 'authorization',
            message: '请输入授权信息 (authorization):',
            validate: (input) => input.trim() ? true : 'authorization 不能为空',
        },
    ]);
    const auth = answers.authorization.trim();
    await (0, projectApi_1.getUserInfo)(auth);
    await (0, cache_1.saveAuthorization)(auth);
    return auth;
}
async function readAuthorizationFromConfig() {
    return (0, cache_1.getAuthorization)();
}
function collectRemoteFiles(projectTree) {
    const files = new Map();
    for (const node of projectTree) {
        walkTreeNode(node, files);
    }
    return Array.from(files.values());
}
function collectRemoteFolders(projectTree) {
    const folders = [];
    for (const node of projectTree) {
        if (node.type === 'Services') {
            walkTreeForFolders(node, 'src/services', folders);
        }
        else if (node.type === 'LocalLibs') {
            walkTreeForFolders(node, 'src/local-lib', folders);
        }
        else {
            walkTreeForFolders(node, '', folders);
        }
    }
    return folders;
}
function walkTreeForFolders(node, parentPath, folders) {
    if (node.type === 'FileFolder') {
        const folderName = node.code || node.title || '';
        const folderPath = parentPath ? `${parentPath}/${folderName}` : folderName;
        if (folderPath && (folderPath.startsWith('src/services/') || folderPath.startsWith('src/local-lib/'))) {
            folders.push({ relativePath: folderPath });
        }
        const children = node.children || [];
        for (const child of children) {
            walkTreeForFolders(child, folderPath, folders);
        }
    }
    else {
        const children = node.children || [];
        for (const child of children) {
            walkTreeForFolders(child, parentPath, folders);
        }
    }
}
function walkTreeNode(node, files) {
    addRemoteFileByPath(node, files);
    addServiceDeclareFile(node, files);
    const children = node.children || [];
    for (const child of children) {
        walkTreeNode(child, files);
    }
}
function addRemoteFileByPath(node, files) {
    const relativePath = toSupportedRelativePath(node.path);
    if (!relativePath || !node.key) {
        return;
    }
    files.set(node.key, {
        fileId: node.key,
        lockId: node.versionStatus?.lockId || '',
        relativePath,
    });
}
function addServiceDeclareFile(node, files) {
    if (node.type !== 'Service' || !node.code || !node.children?.length) {
        return;
    }
    const declareNode = node.children.find((child) => child.type === 'ServiceDeclare');
    if (!declareNode?.key) {
        return;
    }
    // 根据服务节点的子文件路径确定 xxxDeclare.ts 的位置
    let serviceDir = 'src/services';
    // 方法1: 从服务节点自身的 path 获取目录
    if (node.path) {
        const serviceRelativePath = toSupportedRelativePath(node.path);
        if (serviceRelativePath) {
            const pathParts = serviceRelativePath.split('/');
            if (pathParts.length > 2) {
                serviceDir = pathParts.slice(0, -1).join('/');
            }
        }
    }
    // 方法2: 如果方法1没有结果，从子节点的 .ts 文件路径推断目录
    if (serviceDir === 'src/services') {
        for (const child of node.children) {
            if (child.path && child.path.endsWith('.ts') && !child.path.endsWith('Declare.ts') && !child.path.endsWith('.test.ts')) {
                const childRelativePath = toSupportedRelativePath(child.path);
                if (childRelativePath) {
                    const pathParts = childRelativePath.split('/');
                    if (pathParts.length > 2) {
                        serviceDir = pathParts.slice(0, -1).join('/');
                        break;
                    }
                }
            }
        }
    }
    files.set(declareNode.key, {
        fileId: declareNode.key,
        lockId: declareNode.versionStatus?.lockId || '',
        relativePath: `${serviceDir}/${node.code}Declare.ts`,
        convertFromXmlDeclare: true,
        serviceName: node.code,
    });
}
function toSupportedRelativePath(rawPath) {
    if (!rawPath || !rawPath.startsWith('file:///')) {
        return null;
    }
    const cleaned = rawPath
        .replace(/^file:\/\/\//, '')
        .replace(/^\/+/, '')
        .replace(/\\/g, '/');
    if (cleaned === 'src/init/initScript.ts') {
        return cleaned;
    }
    if (cleaned.startsWith('src/services/') || cleaned.startsWith('src/local-lib/')) {
        return cleaned;
    }
    return null;
}
function installDependencies(targetDir, registry) {
    return new Promise((resolve, reject) => {
        const child = (0, cross_spawn_1.default)('pnpm', ['install', '--registry', registry], {
            cwd: targetDir,
            stdio: 'inherit',
            shell: true,
        });
        child.on('close', (code) => {
            if (code === 0) {
                resolve();
            }
            else {
                reject(new Error(`pnpm install 失败，退出码: ${code}`));
            }
        });
        child.on('error', (err) => {
            reject(err);
        });
    });
}
function toReadableError(error, fallback) {
    const message = error?.message || fallback;
    const code = error?.code || '';
    if (code === 'ECONNREFUSED' || message.includes('ECONNREFUSED')) {
        const proxy = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
        if (proxy) {
            return `${message}。检测到代理配置(${proxy})，请检查代理是否可达或临时关闭代理后重试。`;
        }
        return `${message}。网络连接被拒绝，请确认已连接公司网络/VPN，且可访问 https://me.keytop.cn。`;
    }
    if (code === 'ETIMEDOUT' || message.includes('ETIMEDOUT')) {
        return `${message}。请求超时，请检查网络或 VPN 后重试。`;
    }
    if (code === 'ENOTFOUND' || message.includes('ENOTFOUND')) {
        return `${message}。域名解析失败，请检查 DNS 或网络环境。`;
    }
    return message;
}
async function selectiveOverwrite(targetDir) {
    const foldersToOverwrite = ['src/services', 'src/local-lib', 'src/init'];
    for (const folder of foldersToOverwrite) {
        const folderPath = path_1.default.join(targetDir, folder);
        if (fs_extra_1.default.existsSync(folderPath)) {
            await fs_extra_1.default.remove(folderPath);
        }
    }
}
//# sourceMappingURL=clone.js.map