import {exec, execSync} from 'child_process';
import {program} from 'commander';
import {prompt} from 'enquirer';
import EventEmitter from 'events';
import fs from 'fs-extra';
import log4js from 'log4js';
import path from 'path';
import MixedApiAdapter from './builtin/MixedApiAdapter';
import {ApplicationConfig, ConfigMeta} from './config';
import createApp, {Mirai, MiraiApiHttpAdapter, Pie} from './miraipie';
import {makeTemplate} from './utils';

log4js.configure({
    appenders: {
        console: {type: 'console'}
    },
    categories: {
        default: {
            appenders: ['console'],
            level: 'info'
        }
    }
});
const logger = log4js.getLogger('cli');
const banner = `
 __    __     __     ______     ______     __     ______   __     ______    
/\\ "-./  \\   /\\ \\   /\\  == \\   /\\  __ \\   /\\ \\   /\\  == \\ /\\ \\   /\\  ___\\   
\\ \\ \\-./\\ \\  \\ \\ \\  \\ \\  __<   \\ \\  __ \\  \\ \\ \\  \\ \\  _-/ \\ \\ \\  \\ \\  __\\   
 \\ \\_\\ \\ \\_\\  \\ \\_\\  \\ \\_\\ \\_\\  \\ \\_\\ \\_\\  \\ \\_\\  \\ \\_\\    \\ \\_\\  \\ \\_____\\ 
  \\/_/  \\/_/   \\/_/   \\/_/ /_/   \\/_/\\/_/   \\/_/   \\/_/     \\/_/   \\/_____/ 
`;

// 提供虚拟环境
function MiraiPieApplicationMock() {
    return Object.assign(new EventEmitter(), {
        config: {},
        extensions: [],
        install(extension: MiraiApiHttpAdapter | Pie) {
            this.extensions.push(extension);
        },
        adapter(adapter: MiraiApiHttpAdapter) {
            this.extensions.push(adapter);
        },
        pie(pie: Pie) {
            this.extensions.push(pie);
        },
        useAdapter: () => undefined,
        uninstallAdapter: () => undefined,
        enable: () => undefined,
        disable: () => undefined,
        uninstallPie: () => undefined,
        listen: () => undefined,
        stop: () => undefined
    });
}

// 检查 miraipie 更新
function checkForUpdate() {
    const version = require('../package.json').version;
    exec('npm search miraipie -p --no-description', (error, stdout) => {
        if (!error && stdout) {
            const lines = stdout.split('\n');
            for (const line of lines) {
                const desc = line.split('\t');
                if (desc[0] === 'miraipie' && version < desc[3]) {
                    logger.warn(`新的 miraipie 版本可用: ${desc[3]}, 当前版本: ${version}, 使用命令 npm install miraipie -g 更新 miraipie`);
                }
            }
        }
    });
}

// 填写用户配置
async function writeUserConfig(configMeta: ConfigMeta) {
    const unsupportedConfig = {};
    const questions = Object.keys(configMeta).map((name) => {
        const meta = configMeta[name];
        const question = {
            type: null,
            name,
            message: `请输入配置项 - ${name}` + (meta.description ? `(${meta.description})` : ''),
            initial: typeof meta.default === 'function' ? meta.default() : null
        };
        const type = typeof meta.type();
        if (type === 'number') question.type = 'numeral';
        else if (type === 'boolean') question.type = 'confirm';
        else if (type === 'string') question.type = 'input';
        else {
            logger.warn(`暂不支持CLI填写的配置项: ${name} 类型: ${type} , 请手动填写配置文件`);
            unsupportedConfig[name] = typeof meta.default === 'function' ? meta.default() : null
        }
        return question.type ? question : null;
    }).filter((question) => !!question);
    try {
        return Object.assign(await prompt(questions), unsupportedConfig);
    } catch (e) {
        return {};
    }
}

// 加载配置文件
async function useConfigFile(path: string): Promise<ApplicationConfig> {
    return new Promise(async (resolve, reject) => {
        if (!fs.existsSync(path)) {
            prompt({
                type: 'confirm',
                name: 'new',
                message: '没有检测到配置文件, 是否新建配置文件?',
                initial: true
            }).then((confirm: any) => {
                if (confirm.new) {
                    prompt([
                        {
                            type: 'numeral',
                            name: 'qq',
                            message: '请输入机器人服务的QQ号'
                        },
                        {
                            type: 'select',
                            name: 'adapter',
                            message: '请选择默认服务客户端adapter',
                            choices: ['mixed', 'http', 'ws'],
                            initial: 0
                        }
                    ]).then(async (pro: any) => {
                        const configMeta = Object.assign({}, MixedApiAdapter.configMeta);
                        delete configMeta['qq'];
                        const adapterConfigs = await writeUserConfig(configMeta);
                        const config = ApplicationConfig.make({qq: pro.qq, adapterInUse: pro.adapter});
                        config.adapters[pro.adapter] = {configs: adapterConfigs};
                        saveConfigFile(config, path);
                        resolve(config);
                    }).catch(reject);
                } else reject();
            }).catch(reject);
        } else {
            fs.readJson(path).then((content) => {
                resolve(ApplicationConfig.make(content));
            }).catch(reject);
        }
    });
}

// 保存配置文件
function saveConfigFile(config: ApplicationConfig, path: string) {
    fs.writeJsonSync(path, config, {
        spaces: 4
    });
}

// miraipie
program
    .version(`miraipie ${require('../package.json').version}  for mirai-api-http ${Mirai.MIRAI_API_HTTP_VERSION}`, '-V, --version', '显示版本信息')
    .option('-c, --config <path>', 'miraipie的配置文件路径', 'miraipie.json')
    .helpOption('-h, --help', '显示帮助信息')
    .addHelpCommand('help [command]', '显示命令帮助');

// miraipie start
program
    .command('start')
    .description('启动miraipie应用程序')
    .option('-m, --modules <paths...>', 'miraipie需要额外加载的模块路径')
    .option('-l, --log-level <level>', 'miraipie日志输出级别')
    .option('-v, --verbose', '控制台打印miraipie接收到的消息和事件')
    .action((opts) => {
        checkForUpdate();
        useConfigFile(program.opts().config).then(async (config) => {
            if (opts.logLevel) config.logLevel = opts.logLevel;
            if (opts.modules) config.extensions = [...config.extensions, ...opts.modules];
            if (opts.verbose) config.verbose = true;
            const app = createApp(config);
            app.once('listen', () => {
                logger.info('miraipie应用程序已成功启动!\n' + banner);
            });
            await app.listen();
        }).catch((err) => {
            logger.error('启动应用程序失败:', err);
        });
    });

// miraipie install
program
    .command('install')
    .description('添加本地模块(客户端adapter或pie)')
    .argument('module', '待安装的本地模块路径')
    .aliases(['i', 'add'])
    .action((module) => {
        useConfigFile(program.opts().config).then((config) => {
            const mockApp = MiraiPieApplicationMock();
            try {
                require(path.join(process.cwd(), module))(mockApp);
                for (const extension of mockApp.extensions) {
                    if ((extension.__isPie || extension.__isApiAdapter) && extension.id) {
                        writeUserConfig(extension.configMeta || {}).then((userConfigs) => {
                            if (extension.__isPie) {
                                config.pies[extension.id] = {
                                    module,
                                    enabled: true,
                                    configs: userConfigs
                                };
                                logger.info(`已添加pie '${extension.id}'`);
                            } else if (extension.__isApiAdapter) {
                                config.adapters[extension.id] = {
                                    module,
                                    configs: userConfigs
                                };
                                logger.info(`已添加adapter '${extension.id}'`);
                            }
                            saveConfigFile(config, program.opts().config);
                        });
                    } else {
                        config.extensions.push(module);
                        logger.info(`已添加模块 ${module}`);
                        saveConfigFile(config, program.opts().config);
                    }
                }
            } catch (err) {
                logger.error('添加模块失败:', err);
            }
        }).catch(() => {
            logger.error('没有找到miraipie配置文件');
        });
    });

// miraipie pie
const pie = program
    .command('pie [command]')
    .description('miraipie关于pie相关选项')

// miraipie pie create
pie
    .command('create')
    .description('使用模板创建pie项目')
    .aliases(['new', 'init'])
    .argument('[id]', 'pie id')
    .option('-j, --javascript', '使用javascript模板')
    .action((id, opts) => {
        prompt([
            {
                type: 'input',
                name: 'id',
                message: '请输入pie id',
                initial: id || 'my-pie'
            },
            {
                type: 'input',
                name: 'name',
                message: '请输入pie名称',
                initial: id || 'my-pie'
            },
            {
                type: 'input',
                name: 'path',
                message: '请输入pie存放位置',
                initial: `pies/${id || 'my-pie'}/`
            }
        ]).then((pro: any) => {
            const src = path.join(__dirname, opts.javascript ? '../template/pie-js' : '../template/pie-ts');
            fs.ensureDirSync(pro.path);
            fs.readdir(src, (err, files) => {
                if (err) {
                    logger.error('创建pie项目失败:', err);
                    return;
                }
                for (const file of files) makeTemplate(path.resolve(src, file), path.resolve(pro.path, file), pro);
            });
            logger.info('已使用模板创建pie项目');
            prompt({
                type: 'confirm',
                name: 'init',
                message: '是否使用npm初始化pie项目',
                initial: true
            }).then((proConfirm: any) => {
                if (proConfirm.init) execSync(`cd "${pro.path}" && npm install`);
            }).catch(() => undefined);
        }).catch(() => {
            logger.info('已取消创建项目');
        });
    });

// miraipie pie list
pie
    .command('list')
    .description('显示已安装pie的列表')
    .option('-e, --enabled', '只显示已启用的pie')
    .action((opts) => {
        useConfigFile(program.opts().config).then((config) => {
            const pies = config.pies;
            if (opts.enabled) {
                for (const id in pies) {
                    if (!pies[id].enabled) delete pies[id];
                }
            }
            if (Object.keys(pies).length === 0) {
                logger.info('当前没有已添加的pie');
                return;
            }
            const header = `${'id'.padEnd(32)}${'enabled'.padEnd(12)}path\n` +
                `${'-'.repeat(2).padEnd(32)}${'-'.repeat(7).padEnd(12)}${'-'.repeat(4)}`;
            const content = Object.keys(pies).map((id) => {
                return `${id.padEnd(32)}${pies[id].enabled.toString().padEnd(12)}${pies[id].module.toString()}`
            }).join('\n');
            logger.info(`当前加载的pie列表:\n${header}\n${content}`);
        }).catch(() => {
            logger.error('没有找到miraipie配置文件');
        });
    });

// miraipie pie enable
pie
    .command('enable')
    .description('启用已添加的pie')
    .argument('id', 'pie id')
    .action((id) => {
        useConfigFile(program.opts().config).then((config) => {
            if (id in config.pies) {
                config.pies[id].enabled = true;
                logger.info(`已启用pie '${id}'`);
                saveConfigFile(config, program.opts().config);
            } else {
                logger.error(`配置文件中不存在pie '${id}'`)
            }
        }).catch(() => {
            logger.error('没有找到miraipie配置文件');
        });
    });

// miraipie pie disable
pie
    .command('disable')
    .description('禁用已添加的pie')
    .argument('id', 'pie id')
    .action((id) => {
        useConfigFile(program.opts().config).then((config) => {
            if (id in config.pies) {
                config.pies[id].enabled = false;
                logger.info(`已禁用pie '${id}'`);
                saveConfigFile(config, program.opts().config);
            } else {
                logger.error(`配置文件中不存在pie '${id}'`)
            }
        }).catch(() => {
            logger.error('没有找到miraipie配置文件');
        });
    });

// miraipie pie delete
pie
    .command('delete')
    .description('移除已添加的pie')
    .alias('remove')
    .argument('id', 'pie id')
    .action((id) => {
        useConfigFile(program.opts().config).then((config) => {
            if (id in config.pies) {
                delete config.pies[id];
                logger.info(`已移除pie '${id}'`);
                saveConfigFile(config, program.opts().config);
            } else {
                logger.error(`配置文件中不存在pie '${id}'`)
            }
        }).catch(() => {
            logger.error('没有找到miraipie配置文件');
        });
    });

program.parse(process.argv);
