/*---------------------------------------------------------
 * Copyright (C) Microsoft Corporation. All rights reserved.
 *--------------------------------------------------------*/
import { existsSync, promises as fs } from 'fs';
import { dirname, isAbsolute, join } from 'path';
import { pathToFileURL } from 'url';
import { CliExpectedError } from './error.mjs';
import { mustResolve } from './resolver.mjs';
import { ensureArray } from './util.mjs';
const configFileRules = {
    json: (path) => fs.readFile(path, 'utf8').then(JSON.parse),
    js: (path) => import(pathToFileURL(path).toString()),
    mjs: (path) => import(pathToFileURL(path).toString()),
};
/** Loads the default config based on the process working directory. */
export async function loadDefaultConfigFile() {
    const base = '.vscode-test';
    let dir = process.cwd();
    while (true) {
        for (const ext of Object.keys(configFileRules)) {
            const candidate = join(dir, `${base}.${ext}`);
            if (existsSync(candidate)) {
                return tryLoadConfigFile(candidate);
            }
        }
        const next = dirname(dir);
        if (next === dir) {
            break;
        }
        dir = next;
    }
    throw new CliExpectedError(`Could not find a ${base} file in this directory or any parent. You can specify one with the --config option.`);
}
/** Loads a specific config file by the path, throwing if loading fails. */
export async function tryLoadConfigFile(path) {
    const ext = path.split('.').pop();
    if (!configFileRules.hasOwnProperty(ext)) {
        throw new CliExpectedError(`I don't know how to load the extension '${ext}'. We can load: ${Object.keys(configFileRules).join(', ')}`);
    }
    try {
        let loaded = await configFileRules[ext](path);
        if ('default' in loaded) {
            // handle default es module exports
            loaded = loaded.default;
        }
        // allow returned promises to resolve:
        loaded = await loaded;
        if (typeof loaded === 'object' && 'tests' in loaded) {
            return await ResolvedTestConfiguration.load(loaded, path);
        }
        return await ResolvedTestConfiguration.load({ tests: ensureArray(loaded) }, path);
    }
    catch (e) {
        throw new CliExpectedError(`Could not read config file ${path}: ${e.stack || e}`);
    }
}
export class ResolvedTestConfiguration {
    path;
    tests;
    coverage;
    /** Directory name the configuration file resides in. */
    dir;
    static async load(config, path) {
        // Resolve all mocha `require` locations relative to the configuration file,
        // since these are otherwise relative to the runner which is opaque to the user.
        const dir = dirname(path);
        for (const test of config.tests) {
            if (test.mocha?.require) {
                test.mocha.require = await Promise.all(ensureArray(test.mocha.require).map((f) => mustResolve(dir, f)));
            }
        }
        return new ResolvedTestConfiguration(config, path);
    }
    constructor(config, path) {
        this.path = path;
        this.coverage = config.coverage;
        this.tests = config.tests;
        this.dir = dirname(path);
    }
    /**
     * Gets the resolved extension development path for the test configuration.
     */
    extensionDevelopmentPath(test) {
        return ensureArray(test.extensionDevelopmentPath?.slice() || this.dir).map((p) => isAbsolute(p) ? p : join(this.dir, p));
    }
}
