"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var path = require("path");
var fs = require("fs");
var request = require("request");
var glob = require("glob");
var mustache_1 = require("mustache");
var utility_1 = require("./utility");
var fs_1 = require("./utility/fs");
var command_1 = require("@2fd/command");
var pack = require(path.resolve(__dirname, '../package.json'));
var GraphQLDocumentor = (function (_super) {
    __extends(GraphQLDocumentor, _super);
    function GraphQLDocumentor() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.description = pack.description + ' v' + pack.version;
        _this.params = new command_1.NoParams();
        _this.flags = [
            new command_1.ValueFlag('configFile', ['-c', '--config'], 'Configuration file [./package.json].', String, './package.json'),
            new command_1.ValueFlag('endpoint', ['-e', '--endpoint'], 'Graphql http endpoint ["https://domain.com/graphql"].'),
            new command_1.ListValueFlag('headers', ['-x', '--header'], 'HTTP header for request (use with --endpoint). ["Authorization=Token cb8795e7"].'),
            new command_1.ListValueFlag('queries', ['-q', '--query'], 'HTTP querystring for request (use with --endpoint) ["token=cb8795e7"].'),
            new command_1.ValueFlag('schemaFile', ['-s', '--schema'], 'Graphql Schema file ["./schema.json"].'),
            new command_1.ListValueFlag('plugins', ['-p', '--plugin'], 'Use plugins [default=graphdoc/plugins/default].'),
            new command_1.ValueFlag('template', ['-t', '--template'], 'Use template [default=graphdoc/template/slds].'),
            new command_1.ValueFlag('output', ['-o', '--output'], 'Output directory.'),
            new command_1.ValueFlag('data', ['-d', '--data'], 'Inject custom data.', JSON.parse, {}),
            new command_1.ValueFlag('baseUrl', ['-b', '--base-url'], 'Base url for templates.'),
            new command_1.BooleanFlag('force', ['-f', '--force'], 'Delete outputDirectory if exists.'),
            new command_1.BooleanFlag('verbose', ['-v', '--verbose'], 'Output more information.'),
            new command_1.BooleanFlag('version', ['-V', '--version'], 'Show graphdoc version.'),
        ];
        return _this;
    }
    GraphQLDocumentor.prototype.action = function (input, out) {
        var _this = this;
        var output = new utility_1.Output(out, input.flags);
        var graphdocPackage = pack;
        var projectPackage;
        var schema;
        var plugins;
        var partials;
        var assets;
        if (input.flags.version)
            return output.out.log('graphdoc v%s', graphdocPackage.version);
        function renderFile(type) {
            return utility_1.createData(projectPackage, graphdocPackage, plugins, type)
                .then(function (data) {
                var file = type ? utility_1.getFilenameOf(type) : 'index.html';
                var filepath = path.resolve(projectPackage.graphdoc.output, file);
                var relativeFilepath = path.relative(process.cwd(), filepath);
                output.info('render', relativeFilepath);
                return fs_1.writeFile(filepath, mustache_1.render(partials.index, data, partials));
            });
        }
        // Load project info
        return this.getProjectPackage(input)
            .then(function (config) {
            projectPackage = config;
        })
            .then(function () { return _this.getSchema(projectPackage); })
            .then(function (introspection) {
            schema = introspection;
        })
            .then(function () {
            projectPackage.graphdoc.plugins
                .forEach(function (plugin) { return output.info('use plugin', plugin); });
            return _this.getPlugins(projectPackage.graphdoc.plugins);
        })
            .then(function (pluginContructors) {
            plugins = pluginContructors
                .map(function (Plugin) { return new Plugin(schema, projectPackage, graphdocPackage); });
        })
            .then(function () { return utility_1.Plugin.collectAssets(plugins); })
            .then(function (pluginAssets) {
            assets = pluginAssets;
            assets
                .forEach(function (asset) { return output.info('use asset', asset); });
        })
            .then(function () { return output.info('output directory', projectPackage.graphdoc.output); })
            .then(function () { return _this.ensureOutputDirectory(projectPackage.graphdoc.output, projectPackage.graphdoc.force); })
            .then(function () { return fs_1.createBuildDirectory(projectPackage.graphdoc.output, projectPackage.graphdoc.template, assets); })
            .then(function () { return _this.getTemplatePartials(projectPackage.graphdoc.template); })
            .then(function (p) {
            partials = p;
        })
            .then(function () { return renderFile(); })
            .then(function () { return []
            .concat(schema.types || [])
            .concat(schema.directives || []); })
            .then(function (types) { return Promise
            .all(types.map(function (t) { return renderFile(t); })); })
            .then(function (files) {
            output.ok('complete', String(files.length + 1) + ' files generated.');
        })
            .catch(function (err) { return output.error(err); });
    };
    GraphQLDocumentor.prototype.ensureOutputDirectory = function (dir, force) {
        try {
            var stats = fs.statSync(dir);
            if (!stats.isDirectory())
                return Promise.reject(new Error('Unexpected output: ' + dir + ' is not a directory.'));
            if (!force)
                return Promise.reject(new Error(dir + ' already exists (delete it or use the --force flag)'));
            return fs_1.removeBuildDirectory(dir);
        }
        catch (err) {
            return err.code === 'ENOENT' ?
                Promise.resolve() :
                Promise.reject(err);
        }
    };
    GraphQLDocumentor.prototype.getTemplatePartials = function (templateDir) {
        return new Promise(function (resolve, reject) {
            glob('**/*.mustache', { cwd: templateDir }, function (err, files) {
                return err ? reject(err) : resolve(files);
            });
        })
            .then(function (files) {
            return Promise.all(files.map(function (file) { return fs_1.readFile(path.resolve(templateDir, file), 'utf8'); }))
                .then(function (content) {
                var partials = {};
                files
                    .forEach(function (file, i) {
                    var name = path.basename(file, '.mustache');
                    partials[name] = content[i];
                });
                return partials;
            });
        })
            .then(function (partials) {
            if (partials.index)
                return partials;
            return Promise.reject(new Error('The index partial is missing (file ' +
                path.resolve(templateDir, 'index.mustache') + ' not found).'));
        });
    };
    GraphQLDocumentor.prototype.getProjectPackage = function (input) {
        var packageJSON;
        try {
            packageJSON = require(path.resolve(input.flags.configFile));
        }
        catch (err) {
            packageJSON = {};
        }
        packageJSON.graphdoc = Object.assign(packageJSON.graphdoc || {}, input.flags);
        if (packageJSON.graphdoc.data) {
            var data = packageJSON.graphdoc.data;
            packageJSON.graphdoc = Object.assign(data, packageJSON.graphdoc);
        }
        if (packageJSON.graphdoc.plugins.length === 0)
            packageJSON.graphdoc.plugins = ['graphdoc/plugins/default'];
        packageJSON.graphdoc.baseUrl = path.resolve('/', packageJSON.graphdoc.baseUrl || '/');
        packageJSON.graphdoc.template = fs_1.resolve(packageJSON.graphdoc.template || 'graphdoc/template/slds');
        packageJSON.graphdoc.output = path.resolve(packageJSON.graphdoc.output);
        packageJSON.graphdoc.version = pack.version;
        if (!packageJSON.graphdoc.output)
            return Promise.reject(new Error('Flag output (-o, --output) is required'));
        return Promise.resolve(packageJSON);
    };
    GraphQLDocumentor.prototype.getPlugins = function (paths) {
        return paths
            .map(function (path) { return fs_1.resolve(path); })
            .map(function (path) { return require(path).default; });
    };
    GraphQLDocumentor.prototype.getSchema = function (projectPackage) {
        if (projectPackage.graphdoc.schemaFile) {
            try {
                var schemaPath = path.resolve(projectPackage.graphdoc.schemaFile);
                var introspection = require(schemaPath);
                return Promise.resolve(introspection.data.__schema);
            }
            catch (err) {
                return Promise.reject(err);
            }
        }
        else if (projectPackage.graphdoc.endpoint) {
            var options_1 = {
                url: projectPackage.graphdoc.endpoint,
                method: 'POST',
                json: true,
                body: { query: utility_1.query }
            };
            options_1.headers = projectPackage.graphdoc.headers.reduce(function (result, header) {
                var _a = header.split(': ', 2), name = _a[0], value = _a[1];
                result[name] = value;
                return result;
            }, {});
            options_1.qs = projectPackage.graphdoc.queries.reduce(function (result, query) {
                var _a = query.split('=', 2), name = _a[0], value = _a[1];
                result[name] = value;
                return result;
            }, {});
            return new Promise(function (resolve, reject) {
                request(options_1, function (err, _, introspection) { return err ?
                    reject(err) : resolve(introspection.data.__schema); });
            });
        }
        else {
            return Promise.reject(new Error('Endpoint (--endpoint, -e) or Schema File (--schema, -s) are require.'));
        }
    };
    return GraphQLDocumentor;
}(command_1.Command));
exports.GraphQLDocumentor = GraphQLDocumentor;
