// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
"use strict";

const child_process = require("child_process");
const fs = require("fs");
const log = require("fancy-log");
const colors = require("ansi-colors");
const path = require("path");
const PluginError = require("plugin-error");
const through = require("through2");

/**
 * Pretty logger using 'log'
 * @param {string} pluginName Name of the pluginName
 * @param {Object} file A gulp file to report on
 * @param {string} message The error message to display
 */
function logError(pluginName, file, message) {
    const sourcePath = path.relative(__dirname, file.path).replace(/..\//g, "''");
    log(`[${colors.cyan(pluginName)}] ${colors.red("error")} ${sourcePath}: ${message}`);
}

/**
 * Plugin to verify the Microsoft copyright notice is present
 */
function checkCopyright() {
    const pluginName = "check-copyright";
    let hadErrors = false;
    const copyrightNotice =
        "// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for details.";

    return through.obj(
        function (file, encoding, callback) {
            if (file.isBuffer()) {
                let fileContents = file.contents.toString(encoding);
                fileContents = fileContents.replace("\r\n", "\n");
                fileContents = fileContents.replace('"use strict";\n', "");
                fileContents = fileContents.replace(
                    'Object.defineProperty(exports, "__esModule", { value: true });\n',
                    "",
                );

                if (fileContents.indexOf(copyrightNotice) !== 0) {
                    logError(pluginName, file, "missing copyright notice");
                    hadErrors = true;
                }
            }

            callback(null, file);
        },
        function (callback) {
            if (hadErrors) {
                return this.emit("error", new PluginError(pluginName, "Failed copyright check"));
            }
            callback();
        },
    );
}

/**
 * Helper function to check if a file exists case sensitive
 * @param {string} filePath The path to check
 * @returns {boolean} If the path exists case sensitive
 */
function existsCaseSensitive(filePath) {
    if (fs.existsSync(filePath)) {
        const fileName = path.basename(filePath);
        return fs.readdirSync(path.dirname(filePath)).indexOf(fileName) !== -1;
    }

    return false;
}

function executeCommand(command, args, callback, opts) {
    const proc = child_process.spawn(
        command + (process.platform === "win32" ? ".cmd" : ""),
        args,
        Object.assign({}, opts, { shell: true }),
    );
    let errorSignaled = false;

    proc.stdout.on("data", data => {
        log(`${data}`);
    });

    proc.stderr.on("data", data => {
        log.error(`${data}`);
    });

    proc.on("error", error => {
        if (!errorSignaled) {
            callback(`An error occurred. ${error}`);
            errorSignaled = true;
        }
    });

    proc.on("exit", code => {
        if (code === 0) {
            callback();
        } else if (!errorSignaled) {
            callback(`Error code: ${code}`);
            errorSignaled = true;
        }
    });
}

async function withTimeout(promise, ms, { onTimeout, fallbackValue } = {}) {
    let timer;
    const guarded = promise
        .then(value => ({ kind: "ok", value }))
        .catch(error => ({ kind: "error", error }));

    const timed = new Promise(resolve => {
        timer = setTimeout(() => {
            if (onTimeout) onTimeout();
            resolve({ kind: "timeout", value: fallbackValue });
        }, ms);
    });

    const result = await Promise.race([guarded, timed]);
    clearTimeout(timer);

    if (result.kind === "error") throw result.error;
    if (result.kind === "timeout") return result.value;
    return result.value;
}

module.exports = {
    checkCopyright,
    executeCommand,
    withTimeout,
};
