"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const chalk_1 = require("chalk");
const most = require("most");
const most_last_1 = require("most-last");
const os = require("os");
const prettyBytes = require("pretty-bytes");
const R = require("ramda");
const semver = require("semver");
const constants_1 = require("./constants");
const pkgsDiff_1 = require("./pkgsDiff");
const reportError_1 = require("./reportError");
const BIG_TARBALL_SIZE = 1024 * 1024 * 5; // 5 MB
const addedSign = chalk_1.default.green('+');
const removedSign = chalk_1.default.red('-');
const linkSign = chalk_1.default.magentaBright('#');
const hlValue = chalk_1.default.blue;
const hlPkgId = chalk_1.default['whiteBright'];
function default_1(log$, isRecursive, cmd, widthArg, appendOnly, throttleProgress) {
    const width = widthArg || process.stdout.columns || 80;
    const outputs = [];
    const resolutionDone$ = isRecursive
        ? most.never()
        : log$.stage
            .filter((log) => log.message === 'resolution_done')
            .constant(true)
            .take(1)
            .startWith(false);
    const resolvingContentLog$ = log$.progress
        .filter((log) => log.status === 'resolving_content')
        .scan(R.inc, 0)
        .skip(1)
        .until(most_last_1.last(resolutionDone$));
    if (!isRecursive) {
        const alreadyUpToDate$ = most.of(most_last_1.last(resolvingContentLog$
            .take(1)
            .constant(false)
            .startWith(true))
            .filter(R.equals(true))
            .constant({
            fixed: false,
            msg: 'Already up-to-date',
        }));
        outputs.push(alreadyUpToDate$);
    }
    if (!appendOnly) {
        const fedtchedLog$ = log$.progress
            .filter((log) => log.status === 'fetched')
            .scan(R.inc, 0);
        const foundInStoreLog$ = log$.progress
            .filter((log) => log.status === 'found_in_store')
            .scan(R.inc, 0);
        function createStatusMessage(resolving, fetched, foundInStore, resolutionDone) {
            const msg = `Resolving: total ${hlValue(resolving.toString())}, reused ${hlValue(foundInStore.toString())}, downloaded ${hlValue(fetched.toString())}`;
            if (resolving === foundInStore + fetched && resolutionDone) {
                return {
                    fixed: false,
                    msg: `${msg}, done`,
                };
            }
            return {
                fixed: true,
                msg,
            };
        }
        if (!isRecursive && typeof throttleProgress === 'number' && throttleProgress > 0) {
            const importingDone$ = log$.stage.filter((log) => log.message === 'importing_done').multicast();
            const resolutionStarted$ = log$.stage.filter((log) => log.message === 'resolution_started');
            const commandDone$ = log$.cli.filter((log) => log['message'] === 'command_done');
            // Reporting is done every `throttleProgress` milliseconds
            // and once all packages are fetched.
            const sampler = most.merge(most.periodic(throttleProgress).since(resolutionStarted$).until(most.merge(importingDone$, commandDone$)), importingDone$);
            const progress = most.sample(createStatusMessage, sampler, resolvingContentLog$, fedtchedLog$, foundInStoreLog$, resolutionDone$);
            outputs.push(most.of(progress));
        }
        else {
            const progress = most.combine(createStatusMessage, resolvingContentLog$, fedtchedLog$, foundInStoreLog$, isRecursive ? most.of(false) : resolutionDone$);
            outputs.push(most.of(progress));
        }
        const tarballsProgressOutput$ = log$.progress
            .filter((log) => log.status === 'fetching_started' &&
            typeof log.size === 'number' && log.size >= BIG_TARBALL_SIZE)
            .map((startedLog) => {
            const size = prettyBytes(startedLog['size']);
            return log$.progress
                .filter((log) => log.status === 'fetching_progress' && log.pkgId === startedLog['pkgId'])
                .map((log) => log['downloaded'])
                .startWith(0)
                .map((downloadedRaw) => {
                const done = startedLog['size'] === downloadedRaw;
                const downloaded = prettyBytes(downloadedRaw);
                return {
                    fixed: !done,
                    msg: `Downloading ${hlPkgId(startedLog['pkgId'])}: ${hlValue(downloaded)}/${hlValue(size)}${done ? ', done' : ''}`,
                };
            });
        });
        outputs.push(tarballsProgressOutput$);
        const lifecycleMessages = {};
        const lifecycleOutput$ = most.of(log$.lifecycle
            .map((log) => {
            const key = `${log.script}:${log.pkgId}`;
            lifecycleMessages[key] = formatLifecycle(log);
            return R.values(lifecycleMessages).join(constants_1.EOL);
        })
            .map((msg) => ({ msg })));
        outputs.push(lifecycleOutput$);
    }
    else {
        const lifecycleMessages = {};
        const lifecycleOutput$ = most.of(log$.lifecycle
            .map((log) => ({ msg: formatLifecycle(log) })));
        outputs.push(lifecycleOutput$);
    }
    if (!isRecursive) {
        const pkgsDiff$ = pkgsDiff_1.default(log$);
        const summaryLog$ = log$.summary
            .take(1);
        const summaryOutput$ = most.combine((pkgsDiff) => {
            let msg = '';
            for (const depType of ['prod', 'optional', 'dev']) {
                const diffs = R.values(pkgsDiff[depType]);
                if (diffs.length) {
                    msg += constants_1.EOL;
                    msg += chalk_1.default.blue(`${pkgsDiff_1.propertyByDependencyType[depType]}:`);
                    msg += constants_1.EOL;
                    msg += printDiffs(diffs);
                    msg += constants_1.EOL;
                }
            }
            return { msg };
        }, pkgsDiff$, summaryLog$)
            .take(1)
            .map(most.of);
        outputs.push(summaryOutput$);
        const deprecationOutput$ = log$.deprecation
            .filter((log) => log.depth === 0)
            .map((log) => {
            return {
                msg: formatWarn(`${chalk_1.default.red('deprecated')} ${log.pkgName}@${log.pkgVersion}: ${log.deprecated}`),
            };
        })
            .map(most.of);
        outputs.push(deprecationOutput$);
    }
    if (!isRecursive) {
        outputs.push(most.fromPromise(log$.stats
            .take((cmd === 'install' || cmd === 'update') ? 2 : 1)
            .reduce((acc, log) => {
            if (typeof log['added'] === 'number') {
                acc['added'] = log['added'];
            }
            else if (typeof log['removed'] === 'number') {
                acc['removed'] = log['removed'];
            }
            return acc;
        }, {}))
            .map((stats) => {
            if (!stats['removed'] && !stats['added']) {
                return most.empty();
            }
            let addSigns = (stats['added'] || 0);
            let removeSigns = (stats['removed'] || 0);
            const changes = addSigns + removeSigns;
            if (changes > width) {
                if (!addSigns) {
                    addSigns = 0;
                    removeSigns = width;
                }
                else if (!removeSigns) {
                    addSigns = width;
                    removeSigns = 0;
                }
                else {
                    const p = width / changes;
                    addSigns = Math.min(Math.max(Math.floor(addSigns * p), 1), width - 1);
                    removeSigns = width - addSigns;
                }
            }
            let msg = 'Packages:';
            if (stats['removed']) {
                msg += ' ' + chalk_1.default.red(`-${stats['removed']}`);
            }
            if (stats['added']) {
                msg += ' ' + chalk_1.default.green(`+${stats['added']}`);
            }
            msg += constants_1.EOL + R.repeat(removedSign, removeSigns).join('') + R.repeat(addedSign, addSigns).join('');
            return most.of({ msg });
        }));
        const installCheckOutput$ = log$.installCheck
            .map(formatInstallCheck)
            .filter(Boolean)
            .map((msg) => ({ msg }))
            .map(most.of);
        outputs.push(installCheckOutput$);
        const registryOutput$ = log$.registry
            .filter((log) => log.level === 'warn')
            .map((log) => ({ msg: formatWarn(log.message) }))
            .map(most.of);
        outputs.push(registryOutput$);
        const miscOutput$ = most.merge(log$.link, log$.other)
            .map((obj) => {
            if (obj.level === 'debug')
                return;
            if (obj.level === 'warn') {
                return formatWarn(obj['message']);
            }
            if (obj.level === 'error') {
                return reportError_1.default(obj);
            }
            return obj['message'];
        })
            .map((msg) => ({ msg }))
            .map(most.of);
        outputs.push(miscOutput$);
    }
    else {
        const miscOutput$ = log$.other
            .filter((obj) => obj.level === 'error')
            .map((obj) => {
            if (obj['message']['prefix']) {
                return obj['message']['prefix'] + ':' + os.EOL + reportError_1.default(obj);
            }
            return reportError_1.default(obj);
        })
            .map((msg) => ({ msg }))
            .map(most.of);
        outputs.push(miscOutput$);
    }
    return outputs;
}
exports.default = default_1;
function printDiffs(pkgsDiff) {
    // Sorts by alphabet then by removed/added
    // + ava 0.10.0
    // - chalk 1.0.0
    // + chalk 2.0.0
    pkgsDiff.sort((a, b) => (a.name.localeCompare(b.name) * 10 + (Number(!b.added) - Number(!a.added))));
    const msg = pkgsDiff.map((pkg) => {
        let result = pkg.added
            ? addedSign
            : pkg.linked
                ? linkSign
                : removedSign;
        if (!pkg.realName || pkg.name === pkg.realName) {
            result += ` ${pkg.name}`;
        }
        else {
            result += ` ${pkg.name} <- ${pkg.realName}`;
        }
        if (pkg.version) {
            result += ` ${chalk_1.default.grey(pkg.version)}`;
            if (pkg.latest && semver.lt(pkg.version, pkg.latest)) {
                result += ` ${chalk_1.default.grey(`(${pkg.latest} is available)`)}`;
            }
        }
        if (pkg.deprecated) {
            result += ` ${chalk_1.default.red('deprecated')}`;
        }
        if (pkg.linked) {
            result += ` ${chalk_1.default.magentaBright('linked from')} ${chalk_1.default.grey(pkg.from || '???')}`;
        }
        return result;
    }).join(constants_1.EOL);
    return msg;
}
function formatLifecycle(logObj) {
    const prefix = `Running ${hlValue(logObj.script)} for ${hlPkgId(logObj.pkgId)}`;
    if (logObj['exitCode'] === 0) {
        return `${prefix}, done`;
    }
    const line = formatLine(logObj);
    if (logObj.level === 'error') {
        return `${prefix}! ${line}`;
    }
    return `${prefix}: ${line}`;
}
function formatLine(logObj) {
    if (typeof logObj['exitCode'] === 'number')
        return chalk_1.default.red(`Exited with ${logObj['exitCode']}`);
    const color = logObj.level === 'error' ? chalk_1.default.red : chalk_1.default.gray;
    return color(logObj['line']);
}
function formatInstallCheck(logObj) {
    switch (logObj.code) {
        case 'EBADPLATFORM':
            return formatWarn(`Unsupported system. Skipping dependency ${logObj.pkgId}`);
        case 'ENOTSUP':
            return logObj.toString();
        default:
            return;
    }
}
function formatWarn(message) {
    // The \u2009 is the "thin space" unicode character
    // It is used instead of ' ' because chalk (as of version 2.1.0)
    // trims whitespace at the beginning
    return `${chalk_1.default.bgYellow.black('\u2009WARN\u2009')} ${message}`;
}
//# sourceMappingURL=reporterForClient.js.map