import * as Path from 'path';
import * as FS from 'fs';
import * as ChildProcess from 'child_process';
import * as Walk from 'walk';
import { ACUtils } from './cube-utils';
import { ACProjectType } from './cube-project';
import { ACPackTemplateLoader } from './cube-pack-template-loader';

export default class ACPack {
    private _wxtoolPath: string = Path.join(process.env.HOME ? process.env.HOME : '', '.antcube/wxtool');
    private _tempLoader: ACPackTemplateLoader = new ACPackTemplateLoader();
    
    isReady(): boolean {
        return true;
    }
    async buildProject(path: string, output: string): Promise<ACPackResult> {
        const pkg = ACUtils.pickProjectPackage(path);
        switch (pkg.type) {
            case ACProjectType.ACProjectApplication:
                if (pkg.subtype === 'app') {
                    return await this.buildApplication(path, output);
                }
                return Promise.reject(new Error(`无效的待编译项目子类型 [${pkg.subtype}] [${path}]`));
            case ACProjectType.ACProjectTemplates: 
                return await this.buildTemplates(path, output);
            default:
                return Promise.reject(new Error(`未发现可编译项目 [${path}]`));
        }
    }
    async buildFile(path: string, output: string): Promise<ACPackResult> {
        return this.buildApplication(path, output, false);
    }
    private async buildApplication(path: string, output: string, isProject: boolean=true): Promise<ACPackResult> {
        const result = await ACUtils.spawn(`node`, [Path.join(this._wxtoolPath, 'dist/index.js'), (isProject ? '-p' : '-f'), path, '-o', output]);
        if (result.status === 0) {
            return Promise.resolve({success: true, message: result.stdout.toString()});
        } else {
            return Promise.reject({success: false, message: result.stderr.toString()});
        }
    }
    private async buildTemplates(path: string, output: string, isProject: boolean=true): Promise<ACPackResult> {
        return new Promise((resolve, rejects) => {
            const errMsgs: Array<Error> = new Array();
            //  准备
            ACUtils.rmdirs(output);
            ACUtils.mkdirs(output);
            //  解析转译
            const builders: Array<Promise<any>> = new Array();
            const pickTplDistPath = (root: string): string => {
                const manifest = JSON.parse(FS.readFileSync(Path.join(root, 'manifest.json')).toString());
                if (manifest.name) {
                    const distpath = Path.join(output, `${manifest.name}`);
                    if (!FS.existsSync(distpath)) {
                        FS.mkdirSync(distpath);
                    }
                    return distpath;
                } else {
                    return '';
                }
            };
            Walk.walkSync(path, {
                listeners: {
                    file: (root: string, names: Walk.WalkStats, next: Walk.WalkNext) => {
                        if (names.type === 'file' && names.name === 'main.vue') {
                            builders.push(this._tempLoader.build(Path.join(root, names.name)).then(value => {
                                const distpath = pickTplDistPath(root);
                                value.meta = FS.readFileSync(Path.join(root, 'manifest.json')).toString();
                                const jsonpath = Path.join(distpath, `main.json`);
                                FS.writeFileSync(jsonpath, JSON.stringify(value, undefined, 2), {encoding:'utf8'});
                                const binpath = Path.join(distpath, 'main.bin');
                                const cmd = Path.join(ACUtils.respath, 'capnp/falcon_convert');
                                const schema = Path.join(ACUtils.respath, 'capnp/cube-v1.capnp');
                                const ccapnp = ChildProcess.spawnSync(cmd, [schema, jsonpath, binpath]);
                                if (ccapnp.status !== 0) {
                                    errMsgs.push(new Error(`${ccapnp.stderr.toString()}`));
                                }
                            }).finally(() => {
                                next();
                            }));
                        } else if (names.type === 'file' && Path.extname(names.name) === '.json' && names.name.indexOf('mock') === 0) {
                            const distpath = pickTplDistPath(root);
                            const mockpath = Path.join(distpath, 'main.mock');
                            let mock = new Array();
                            if (FS.existsSync(mockpath)) {
                                mock = JSON.parse(FS.readFileSync(mockpath).toString());
                            }
                            mock.push({
                                data: FS.readFileSync(Path.join(root, names.name)).toString()
                            });
                            FS.writeFileSync(mockpath, JSON.stringify(mock, undefined, 2), {encoding: 'utf8'});
                        } else {
                            next();
                        }
                    },
                    errors: (root: string, names: Walk.WalkStats[], next: Walk.WalkNext) => {
                        errMsgs.push(new Error('遍历编译路径失败'));
                        next();
                    }
                }
            });
            Promise.all(builders).then(results => {
                resolve({
                    success: errMsgs.length === 0 ? true : false,
                    message: errMsgs.length !== 0 ? `${errMsgs.join('\n')}` : ''
                });
            }).catch(reason => {
                rejects({
                    success: false,
                    message: errMsgs.length !== 0 ? `${errMsgs.join('\n')}` : ''
                });
            });
        });
    }
}

export interface ACPackResult {
    success: boolean;
    message?: string;
}