#!/usr/bin/env node

import * as fs from 'fs-extra';
import * as serializer from 'js-yaml';
import * as program from 'commander';
import { traverse } from './jsonTraverse';
import { groupOrphanFunctions, insertFunctionToIndex, postTransform } from './postTransformer';
import { generateToc } from './tocGenerator';
import { generatePackage } from './packageGenerator';
import { generateModules } from './moduleGenerator';
import { resolveIds } from './idResolver';
import { YamlModel, Syntax, YamlParameter, Root } from './interfaces/YamlModel';
import { TocItem } from './interfaces/TocItem';
import { UidMapping } from './interfaces/UidMapping';
import { RepoConfig } from './interfaces/RepoConfig';
import { yamlHeader } from './common/constants';
import { flags } from './common/flags';
import { ReferenceMapping } from './interfaces/ReferenceMapping';

let pjson = require('../package.json');

let path: string;
let outputPath: string;
let repoConfigPath: string;
program
.version(`v${pjson.version}`)
.description('A tool to convert the json format api file generated by TypeDoc to yaml format output files for docfx.')
.option('--hasModule', 'Add the option if the source repository contains module.')
.option('--disableAlphabetOrder', 'Add the option if you want to disable the alphabet order in output yaml.')
.option('--basePath [value]', 'Current base path to the repository.')
.option('--sourceUrl [value]', 'Define the source repository address.')
.option('--sourceBranch [value]', 'Define the branch of source repository.')
.arguments('<inputFile> <outputFolder> [repoConfigureFile]')
.action(function (input: string, output: string, repoConfig: string) {
    path = input;
    outputPath = output;
    repoConfigPath = repoConfig;
 })
.parse(process.argv);

if (!path || !outputPath) {
    console.log('Error: The input file path and output folder path is not specified!');
    program.help();
}

let repoConfig: RepoConfig;
if (repoConfigPath && program.basePath) {
    if (fs.existsSync(repoConfigPath)) {
        let temp = JSON.parse(fs.readFileSync(repoConfigPath).toString());
        repoConfig = {
            repo: temp.repo,
            branch: temp.branch,
            basePath: program.basePath
        };
    } else {
        console.log(`Error: repository config file path {${repoConfigPath}} doesn't exit!`);
        program.help();
    }
}

if (!repoConfig && program.sourceUrl && program.sourceBranch && program.basePath) {
    repoConfig = {
        repo: program.sourceUrl,
        branch: program.sourceBranch,
        basePath: program.basePath
    };
}

if (program.hasModule) {
    flags.hasModule = true;
}

if (program.disableAlphabetOrder) {
    flags.enableAlphabetOrder = false;
}

let json = null;
if (fs.existsSync(path)) {
    let dataStr = fs.readFileSync(path).toString();
    json = JSON.parse(dataStr);
} else {
    console.error('Api doc file ' + path + ' doesn\'t exist.');
    program.help();
}

let rootElements: YamlModel[] = [];
let uidMapping: UidMapping = {};
let referenceMappings: ReferenceMapping[] = [];
if (json) {
    traverse(json, '', rootElements, null, uidMapping, repoConfig);
}

if (rootElements && rootElements.length) {
    rootElements.forEach(rootElement => {
        let referenceMapping = {};
        resolveIds(rootElement, uidMapping, referenceMapping);
        referenceMappings.push(referenceMapping);
    });

    let functionsMapping = groupOrphanFunctions(rootElements);

    let flattenElements = rootElements.map((rootElement, index) => {
        if (rootElement.uid.indexOf('constructor') >= 0) {
            return [];
        }

        return postTransform(rootElement, referenceMappings[index]);
    }).reduce(function(a, b) {
        return a.concat(b);
    }, []);

    console.log('Yaml dump start.');
    fs.ensureDir(outputPath);
    flattenElements.forEach(transfomredClass => {
        transfomredClass = JSON.parse(JSON.stringify(transfomredClass));
        let filename = transfomredClass.items[0].uid.replace(`${transfomredClass.items[0].package}.`, '');
        filename = filename.split('(')[0];
        console.log(`Dump ${outputPath}/${filename}.yml`);
        fs.writeFileSync(`${outputPath}/${filename}.yml`, `${yamlHeader}\n${serializer.safeDump(transfomredClass)}`);
    });
    console.log('Yaml dump end.');

    let yamlModels: YamlModel[] = [];
    flattenElements.forEach(element => {
        yamlModels.push(element.items[0]);
    });

    let packageIndex = generatePackage(yamlModels);
    insertFunctionToIndex(packageIndex, functionsMapping['ParentToPackage']);
    packageIndex = JSON.parse(JSON.stringify(packageIndex));
    fs.writeFileSync(`${outputPath}/index.yml`, `${yamlHeader}\n${serializer.safeDump(packageIndex)}`);
    console.log('Package index genrated.');

    let toc = generateToc(yamlModels, flattenElements[0].items[0].package);
    toc = JSON.parse(JSON.stringify(toc));
    fs.writeFileSync(`${outputPath}/toc.yml`, serializer.safeDump(toc));
    console.log('Toc genrated.');

    if (flags.hasModule) {
        let moduleIndexes = generateModules(toc[0].items);
        moduleIndexes.forEach(moduleIndex => {
            if (moduleIndex.items && moduleIndex.items.length) {
                insertFunctionToIndex(moduleIndex, functionsMapping[moduleIndex.items[0].name]);
                moduleIndex = JSON.parse(JSON.stringify(moduleIndex));
                fs.writeFileSync(`${outputPath}/${moduleIndex.items[0].uid}.yml`, `${yamlHeader}\n${serializer.safeDump(moduleIndex)}`);
            }

        });
        console.log('Module indexes generated.');
    }
}
