// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import { InternalError } from '@rushstack/node-core-library';
import { Colorize } from '@rushstack/terminal';
import { OperationStatus } from './OperationStatus';
const PLUGIN_NAME = 'OperationResultSummarizerPlugin';
/**
 * Format "======" lines for a shell window with classic 80 columns
 */
const ASCII_HEADER_WIDTH = 79;
/**
 * Phased command plugin that emits a summary of build results to the console.
 */
export class OperationResultSummarizerPlugin {
    constructor(terminal) {
        this._terminal = terminal;
    }
    apply(hooks) {
        hooks.afterExecuteOperations.tap(PLUGIN_NAME, (result, context) => {
            _printOperationStatus(this._terminal, result);
        });
    }
}
/**
 * Prints out a report of the status of each project
 * @internal
 */
export function _printOperationStatus(terminal, result) {
    const { operationResults } = result;
    const operationsByStatus = new Map();
    for (const record of operationResults) {
        if (record[1].silent) {
            // Don't report silenced operations
            continue;
        }
        const { status } = record[1];
        switch (status) {
            // These are the sections that we will report below
            case OperationStatus.Skipped:
            case OperationStatus.FromCache:
            case OperationStatus.Success:
            case OperationStatus.SuccessWithWarning:
            case OperationStatus.Blocked:
            case OperationStatus.Failure:
            case OperationStatus.NoOp:
            case OperationStatus.Aborted:
                break;
            default:
                // This should never happen
                throw new InternalError(`Unexpected operation status: ${status}`);
        }
        const collection = operationsByStatus.get(status);
        if (collection) {
            collection.push(record);
        }
        else {
            operationsByStatus.set(status, [record]);
        }
    }
    // Skip a few lines before we start the summary
    terminal.writeLine('\n\n');
    // These are ordered so that the most interesting statuses appear last:
    writeCondensedSummary(terminal, OperationStatus.Skipped, operationsByStatus, Colorize.green, 'These operations were already up to date:');
    writeCondensedSummary(terminal, OperationStatus.NoOp, operationsByStatus, Colorize.gray, 'These operations did not define any work:');
    writeCondensedSummary(terminal, OperationStatus.FromCache, operationsByStatus, Colorize.green, 'These operations were restored from the build cache:');
    writeCondensedSummary(terminal, OperationStatus.Success, operationsByStatus, Colorize.green, 'These operations completed successfully:');
    writeDetailedSummary(terminal, OperationStatus.SuccessWithWarning, operationsByStatus, Colorize.yellow, 'WARNING');
    writeCondensedSummary(terminal, OperationStatus.Aborted, operationsByStatus, Colorize.white, 'These operations were aborted:');
    writeCondensedSummary(terminal, OperationStatus.Blocked, operationsByStatus, Colorize.white, 'These operations were blocked by dependencies that failed:');
    writeDetailedSummary(terminal, OperationStatus.Failure, operationsByStatus, Colorize.red);
    terminal.writeLine('');
    switch (result.status) {
        case OperationStatus.Failure:
            terminal.writeErrorLine('Operations failed.\n');
            break;
        case OperationStatus.SuccessWithWarning:
            terminal.writeWarningLine('Operations succeeded with warnings.\n');
            break;
    }
}
function writeCondensedSummary(terminal, status, operationsByStatus, headingColor, preamble) {
    var _a, _b, _c;
    // Example:
    //
    // ==[ BLOCKED: 4 projects ]==============================================================
    //
    // These projects were blocked by dependencies that failed:
    //   @scope/name
    //   e
    //   k
    const operations = operationsByStatus.get(status);
    if (!operations || operations.length === 0) {
        return;
    }
    writeSummaryHeader(terminal, status, operations, headingColor);
    terminal.writeLine(preamble);
    let longestTaskName = 0;
    for (const [operation] of operations) {
        const nameLength = operation.name.length;
        if (nameLength > longestTaskName) {
            longestTaskName = nameLength;
        }
    }
    for (const [operation, operationResult] of operations) {
        const { _operationMetadataManager: operationMetadataManager } = operationResult;
        const stopwatch = (_a = operationMetadataManager === null || operationMetadataManager === void 0 ? void 0 : operationMetadataManager.tryRestoreStopwatch(operationResult.stopwatch)) !== null && _a !== void 0 ? _a : operationResult.stopwatch;
        if (stopwatch.duration !== 0 &&
            operation.runner.reportTiming &&
            operationResult.status !== OperationStatus.Skipped) {
            const time = stopwatch.toString();
            const padding = ' '.repeat(longestTaskName - (operation.name || '').length);
            const cacheString = (operationMetadataManager === null || operationMetadataManager === void 0 ? void 0 : operationMetadataManager.wasCobuilt)
                ? ` (restore ${(((_b = operationResult.stopwatch.endTime) !== null && _b !== void 0 ? _b : 0) - ((_c = operationResult.stopwatch.startTime) !== null && _c !== void 0 ? _c : 0)).toFixed(1)}ms)`
                : '';
            terminal.writeLine(`  ${operation.name}${padding}    ${time}${cacheString}`);
        }
        else {
            terminal.writeLine(`  ${operation.name}`);
        }
    }
    terminal.writeLine('');
}
function writeDetailedSummary(terminal, status, operationsByStatus, headingColor, shortStatusName) {
    // Example:
    //
    // ==[ SUCCESS WITH WARNINGS: 2 projects ]================================
    //
    // --[ WARNINGS: f ]------------------------------------[ 5.07 seconds ]--
    //
    // [eslint] Warning: src/logic/operations/OperationsExecutionManager.ts:393:3 ...
    var _a;
    const operations = operationsByStatus.get(status);
    if (!operations || operations.length === 0) {
        return;
    }
    writeSummaryHeader(terminal, status, operations, headingColor);
    if (shortStatusName === undefined) {
        shortStatusName = status;
    }
    for (const [operation, operationResult] of operations) {
        const { _operationMetadataManager: operationMetadataManager } = operationResult;
        // Format a header like this
        //
        // --[ WARNINGS: f ]------------------------------------[ 5.07 seconds ]--
        // leftPart: "--[ WARNINGS: f "
        const subheadingText = `${shortStatusName}: ${operation.name}`;
        const leftPartLength = 4 + subheadingText.length + 1;
        // rightPart: " 5.07 seconds ]--"
        const stopwatch = (_a = operationMetadataManager === null || operationMetadataManager === void 0 ? void 0 : operationMetadataManager.tryRestoreStopwatch(operationResult.stopwatch)) !== null && _a !== void 0 ? _a : operationResult.stopwatch;
        const time = stopwatch.toString();
        const rightPartLength = 1 + time.length + 1 + 3;
        // middlePart: "]----------------------["
        const twoBracketsLength = 2;
        const middlePartLengthMinusTwoBrackets = Math.max(ASCII_HEADER_WIDTH - (leftPartLength + rightPartLength + twoBracketsLength), 0);
        terminal.writeLine(`${Colorize.gray('--[')} ${headingColor(subheadingText)} ${Colorize.gray(`]${'-'.repeat(middlePartLengthMinusTwoBrackets)}[`)} ${Colorize.white(time)} ${Colorize.gray(']--')}\n`);
        const details = operationResult.stdioSummarizer.getReport();
        if (details) {
            // Don't write a newline, because the report will always end with a newline
            terminal.write(details);
        }
        terminal.writeLine('');
    }
}
function writeSummaryHeader(terminal, status, operations, headingColor) {
    // Format a header like this
    //
    // ==[ FAILED: 2 operations ]================================================
    // "2 operations"
    const projectsText = `${operations.length}${operations.length === 1 ? ' operation' : ' operations'}`;
    const headingText = `${status}: ${projectsText}`;
    // leftPart: "==[ FAILED: 2 operations "
    const leftPartLength = 3 + 1 + headingText.length + 1;
    const rightPartLengthMinusBracket = Math.max(ASCII_HEADER_WIDTH - (leftPartLength + 1), 0);
    // rightPart: "]======================"
    terminal.writeLine(`${Colorize.gray('==[')} ${headingColor(headingText)} ${Colorize.gray(`]${'='.repeat(rightPartLengthMinusBracket)}`)}\n`);
}
//# sourceMappingURL=OperationResultSummarizerPlugin.js.map