"use strict";
var command_run_1 = require('./command.run');
var task_resolve_1 = require('./task.resolve');
var Rx = require('rx');
var reporter_resolve_1 = require("./reporter.resolve");
var task_utils_1 = require("./task.utils");
var fs_1 = require("fs");
var file = require("./file.utils");
var debug = require("debug")("cb:command:docs");
;
(function (DocsErrorTypes) {
    DocsErrorTypes[DocsErrorTypes["DocsInputFileNotFound"] = "DocsInputFileNotFound"] = "DocsInputFileNotFound";
    DocsErrorTypes[DocsErrorTypes["DocsOutputFileExists"] = "DocsOutputFileExists"] = "DocsOutputFileExists";
})(exports.DocsErrorTypes || (exports.DocsErrorTypes = {}));
var DocsErrorTypes = exports.DocsErrorTypes;
exports.docStartComment = '<!--crossbow-docs-start-->';
exports.docEndComment = '<!--crossbow-docs-end-->';
exports.hasRegExp = /<!--crossbow-docs-start-->([\s\S]+?)?<!--crossbow-docs-end-->/g;
exports.hasExistingComments = function (string) { return exports.hasRegExp.test(string); };
exports.readmeRegExp = /readme\.(md|markdown)$/i;
function execute(trigger) {
    var input = trigger.input, config = trigger.config, reporter = trigger.reporter;
    /**
     * Resolve all top-level tasks as these are the ones
     * that will used in the docs
     * @type {Tasks}
     */
    var tasks = task_resolve_1.resolveTasks(Object.keys(input.tasks).filter(task_utils_1.isPublicTask), trigger);
    /**
     * If there were 0 tasks, exit with error
     */
    if (tasks.all.length === 0) {
        reporter({ type: reporter_resolve_1.ReportTypes.NoTasksAvailable });
        return Rx.Observable.just({
            setup: {
                tasks: tasks,
                errors: []
            }
        });
    }
    debug("Amount of tasks to consider " + tasks.all.length);
    /**
     * If any tasks were invalid, refuse to generate docs
     * and prompt to run tasks command (for the full error output)
     */
    if (tasks.invalid.length) {
        debug("Tasks were invalid, so skipping doc generation completely");
        reporter({ type: reporter_resolve_1.ReportTypes.DocsInvalidTasksSimple });
        return Rx.Observable.just({
            setup: {
                tasks: tasks,
                errors: []
            }
        });
    }
    var markdown = getMarkdown(tasks.valid);
    /**
     * If the user provided the --file flag,
     * try to load that file & either wedge in the new docs or
     * append them
     */
    if (config.file) {
        return Rx.Observable.just({ setup: handleFileFlag(tasks, markdown, trigger) });
    }
    /**
     * If a user provides the 'output' flag, it means they want a new file creating
     */
    if (config.output) {
        return Rx.Observable.just({ setup: handleOutputFlag(tasks, markdown, trigger) });
    }
    /**
     * If both --flag and --output were NOT given, look for a readme in this current
     * directory
     * @type {Array}
     */
    var existingReadmeFiles = fs_1.readdirSync(process.cwd())
        .filter(function (x) { return exports.readmeRegExp.test(x); })
        .reduce(function (acc, item) { return acc.concat(file.readFilesFromDiskWithContent([item], config.cwd)); }, []);
    if (existingReadmeFiles.length) {
        var output_1 = existingReadmeFiles.map(function (x) { return getFileOutput(x, markdown); });
        reportAddToDocs(output_1, trigger);
        return Rx.Observable.just({
            setup: {
                errors: [],
                tasks: tasks,
                markdown: markdown,
                output: output_1
            }
        });
    }
    /**
     * At this point:
     * 1. NO --file flag given
     * 2. NO --output flag given
     * 3. NO existing readme files in cwd
     *
     * so, create a new one :)
     */
    var output = [{
            file: file.getStubFile('readme.md', config.cwd),
            content: markdown
        }];
    reportAddToDocs(output, trigger);
    return Rx.Observable.just({
        setup: {
            errors: [],
            tasks: tasks,
            markdown: markdown,
            output: output
        }
    });
}
/**
 * When adding docs to a file
 * 1. if file content does not exist, use raw markdown
 * 2. if docs exist, replace them
 * 3. if 1 & 2 fail, append
 */
function getFileOutput(file, markdown) {
    /**
     * If there's no exisitng file content, just use the markdown
     */
    if (!file.content) {
        debug(file.relative + " DOES NOT have any content");
        return { file: file, content: markdown };
    }
    /**
     * If there's existing docs, wedge.
     */
    if (exports.hasExistingComments(file.content)) {
        debug(file.relative + " has the comments already in the file, so will replace");
        var replaced = file.content.replace(exports.hasRegExp, markdown);
        return {
            file: file,
            content: replaced
        };
    }
    debug(file.relative + " DOES NOT have the comments, so will append to the end of the file");
    return {
        file: file,
        content: file.content + '\n' + markdown
    };
}
/**
 * When the --output flag was given
 *
 * eg:
 *
 *      $ crossbow docs --output newfile.md
 */
function handleOutputFlag(tasks, markdown, trigger) {
    var config = trigger.config, reporter = trigger.reporter;
    var maybe = file.readFilesFromDiskWithContent([config.output], config.cwd);
    var available = maybe
        .filter(function (x) { return x.errors.length > 0; })
        .filter(function (x) { return x.errors[0].type === task_utils_1.InputErrorTypes.FileNotFound; });
    if (!available.length) {
        var error = { type: DocsErrorTypes.DocsOutputFileExists, file: maybe[0] };
        if (!config.handoff) {
            reporter({ type: reporter_resolve_1.ReportTypes.DocsOutputFileExists, data: { error: error } });
        }
        return {
            errors: [error],
            tasks: tasks,
            markdown: markdown,
            output: []
        };
    }
    var output = [{
            file: maybe[0],
            content: markdown
        }];
    // Now we can report about writing to disk
    reportAddToDocs(output, trigger);
    return {
        errors: [],
        tasks: tasks,
        markdown: markdown,
        output: output
    };
}
/**
 * When the --file flag was given.
 *
 * eg:
 *
 *      $ crossbow docs --file readme.md
 */
function handleFileFlag(tasks, markdown, trigger) {
    var config = trigger.config, reporter = trigger.reporter;
    /**
     * Try to read the file from disk with content appended
     * @type {file.ExternalFileContent[]}
     */
    var maybes = file.readFilesFromDiskWithContent([config.file], config.cwd);
    var withErrors = maybes
        .filter(function (x) { return x.errors.length > 0; })
        .map(function (x) {
        return {
            type: DocsErrorTypes.DocsInputFileNotFound,
            file: x
        };
    });
    /**
     * If the --file flag produced an error,
     * eg: --file shane.md -> but shane.md did not exist
     */
    if (withErrors.length) {
        /**
         * If we're not handing off, report the error
         */
        if (!config.handoff) {
            reporter({ type: reporter_resolve_1.ReportTypes.DocsInputFileNotFound, data: { error: withErrors[0] } });
        }
        return {
            errors: withErrors,
            tasks: tasks,
            markdown: markdown,
            output: []
        };
    }
    /**
     * At this point we have files to work with so we
     * either append the docs or insert them between existing
     * comments
     * @type {{content: string, file: file.ExternalFileContent}[]}
     */
    var output = maybes.map(function (x) { return getFileOutput(x, markdown); });
    /**
     * Now write to file
     */
    reportAddToDocs(output, trigger);
    /**
     * Always return everything gathered
     */
    return {
        errors: [],
        tasks: tasks,
        markdown: markdown,
        output: output
    };
}
function getMarkdown(tasks) {
    /**
     * Create the header for the markdown table
     * @type {string|string[]}
     */
    var tasksHeader = ["## Crossbow tasks\n\nThe following tasks have been defined by this project's Crossbow configuration.\nRun any of them in the following way\n \n```shell\n$ crossbow run <taskname>\n```"];
    var tableHeader = '|Task name|Description|\n|---|---|';
    /**
     * Create the body for the table with taskname + description
     * @type {string[]}
     */
    var body = tasks.map(function (x) {
        var name = "|<pre>`" + x.baseTaskName + "`</pre>";
        var desc = (function () {
            if (x.description)
                return task_utils_1.removeNewlines(x.description);
            if (x.tasks.length) {
                return ['**Alias for:**'].concat(x.tasks.map(function (x) { return ("- `" + task_utils_1.removeNewlines(x.baseTaskName) + "`"); })).join('<br>');
            }
        })() + '|';
        return [name, desc].join('|');
    }).join('\n');
    /**
     * Join the lines with a \n for correct formatting in markdown
     * @type {string}
     */
    return [exports.docStartComment, tasksHeader, tableHeader, body, exports.docEndComment].join('\n');
}
function reportAddToDocs(output, trigger) {
    var config = trigger.config;
    if (!config.handoff) {
        output.forEach(function (x) {
            trigger.reporter({
                type: reporter_resolve_1.ReportTypes.DocsAddedToFile,
                data: {
                    file: x.file
                }
            });
        });
    }
}
function handleIncomingDocsCommand(cli, input, config, reporter) {
    return execute({
        cli: cli,
        input: input,
        config: config,
        reporter: reporter,
        type: command_run_1.TriggerTypes.command
    });
}
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = handleIncomingDocsCommand;
//# sourceMappingURL=command.docs.js.map