/**
 * Copyright (c) Microsoft Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { TeleReporterReceiver, TeleSuite } from './teleReceiver';
import { statusEx } from './testTree';
export class TeleSuiteUpdater {
    rootSuite;
    config;
    loadErrors = [];
    progress = {
        total: 0,
        passed: 0,
        failed: 0,
        skipped: 0,
    };
    _receiver;
    _lastRunReceiver;
    _lastRunTestCount = 0;
    _options;
    _testResultsSnapshot;
    constructor(options) {
        this._receiver = new TeleReporterReceiver(this._createReporter(), {
            mergeProjects: true,
            mergeTestCases: true,
            resolvePath: createPathResolve(options.pathSeparator),
            clearPreviousResultsWhenTestBegins: true,
        });
        this._options = options;
    }
    _createReporter() {
        return {
            version: () => 'v2',
            onConfigure: (config) => {
                this.config = config;
                // TeleReportReceiver is merging everything into a single suite, so when we
                // run one test, we still get many tests via rootSuite.allTests().length.
                // To work around that, have a dedicated per-run receiver that will only have
                // suite for a single test run, and hence will have correct total.
                this._lastRunReceiver = new TeleReporterReceiver({
                    version: () => 'v2',
                    onBegin: (suite) => {
                        this._lastRunTestCount = suite.allTests().length;
                        this._lastRunReceiver = undefined;
                    }
                }, {
                    mergeProjects: true,
                    mergeTestCases: false,
                    resolvePath: createPathResolve(this._options.pathSeparator),
                });
                void this._lastRunReceiver.dispatch({ method: 'onConfigure', params: { config } });
            },
            onBegin: (suite) => {
                if (!this.rootSuite)
                    this.rootSuite = suite;
                // As soon as new test tree is built add previous results, before calling onUpdate
                // to avoid flashing empty results in the UI.
                if (this._testResultsSnapshot) {
                    for (const test of this.rootSuite.allTests())
                        test.results = this._testResultsSnapshot?.get(test.id) || test.results;
                    this._testResultsSnapshot = undefined;
                }
                this.progress.total = this._lastRunTestCount;
                this.progress.passed = 0;
                this.progress.failed = 0;
                this.progress.skipped = 0;
                this._options.onUpdate(true);
            },
            onEnd: () => {
                this._options.onUpdate(true);
            },
            onTestBegin: (test, testResult) => {
                testResult[statusEx] = 'running';
                this._options.onUpdate();
            },
            onTestEnd: (test, testResult) => {
                if (test.outcome() === 'skipped')
                    ++this.progress.skipped;
                else if (test.outcome() === 'unexpected')
                    ++this.progress.failed;
                else
                    ++this.progress.passed;
                testResult[statusEx] = testResult.status;
                this._options.onUpdate();
            },
            onError: (error) => this._handleOnError(error),
            printsToStdio: () => false,
        };
    }
    processGlobalReport(report) {
        const receiver = new TeleReporterReceiver({
            version: () => 'v2',
            onConfigure: (c) => {
                this.config = c;
            },
            onError: (error) => this._handleOnError(error)
        });
        for (const message of report)
            void receiver.dispatch(message);
    }
    processListReport(report) {
        // Save test results and reset all projects, the results will be restored after
        // new project structure is built.
        const tests = this.rootSuite?.allTests() || [];
        this._testResultsSnapshot = new Map(tests.map(test => [test.id, test.results]));
        this._receiver.reset();
        for (const message of report)
            void this._receiver.dispatch(message);
    }
    processTestReportEvent(message) {
        // The order of receiver dispatches matters here, we want to assign `lastRunTestCount`
        // before we use it.
        this._lastRunReceiver?.dispatch(message)?.catch(() => { });
        this._receiver.dispatch(message)?.catch(() => { });
    }
    _handleOnError(error) {
        this.loadErrors.push(error);
        this._options.onError?.(error);
        this._options.onUpdate();
    }
    asModel() {
        return {
            rootSuite: this.rootSuite || new TeleSuite('', 'root'),
            config: this.config,
            loadErrors: this.loadErrors,
            progress: this.progress,
        };
    }
}
function createPathResolve(pathSeparator) {
    return (rootDir, relativePath) => {
        const segments = [];
        for (const segment of [...rootDir.split(pathSeparator), ...relativePath.split(pathSeparator)]) {
            // Careful with "C:\\" on win32 and "/root" on posix.
            const isAfterDrive = pathSeparator === '\\' && segments.length === 1 && segments[0].endsWith(':');
            const isFirst = !segments.length;
            if (!segment && !isFirst && !isAfterDrive)
                continue;
            if (segment === '.')
                continue;
            if (segment === '..') {
                segments.pop();
                continue;
            }
            segments.push(segment);
        }
        return segments.join(pathSeparator);
    };
}
//# sourceMappingURL=teleSuiteUpdater.js.map