
import { normalizeDriveLetter } from '../../../../base/common/labels.js';
import { basename, sep, dirname, extname, relative } from '../../../../base/common/path.js';
import { isWindows } from '../../../../base/common/platform.js';
import { cwd } from '../../../../base/common/process.js';
import { isUndefinedOrNull, isObject, isString } from '../../../../base/common/types.js';
import { localize } from '../../../../nls.js';
import { allVariableKinds, VariableError, VariableKind } from './configurationResolver.js';
import { ConfigurationResolverExpression } from './configurationResolverExpression.js';

class AbstractVariableResolverService {
    constructor(_context, _labelService, _userHomePromise, _envVariablesPromise) {
        this._contributedVariables = ( new Map());
        this.resolvableVariables = ( new Set(allVariableKinds));
        this._context = _context;
        this._labelService = _labelService;
        this._userHomePromise = _userHomePromise;
        if (_envVariablesPromise) {
            this._envVariablesPromise = _envVariablesPromise.then(envVariables => {
                return this.prepareEnv(envVariables);
            });
        }
    }
    prepareEnv(envVariables) {
        if (isWindows) {
            const ev = Object.create(null);
            ( Object.keys(envVariables)).forEach(key => {
                ev[key.toLowerCase()] = envVariables[key];
            });
            return ev;
        }
        return envVariables;
    }
    async resolveWithEnvironment(environment, folder, value) {
        const expr = ConfigurationResolverExpression.parse(value);
        const env = {
            env: this.prepareEnv(environment),
            userHome: undefined
        };
        for (const replacement of expr.unresolved()) {
            const resolvedValue = await this.evaluateSingleVariable(env, replacement, folder?.uri);
            if (resolvedValue !== undefined) {
                expr.resolve(replacement, resolvedValue);
            }
        }
        return expr.toObject();
    }
    async resolveAsync(folder, config) {
        const expr = ConfigurationResolverExpression.parse(config);
        const environment = {
            env: await this._envVariablesPromise,
            userHome: await this._userHomePromise
        };
        for (const replacement of expr.unresolved()) {
            const resolvedValue = await this.evaluateSingleVariable(environment, replacement, folder?.uri);
            if (resolvedValue !== undefined) {
                expr.resolve(replacement, resolvedValue);
            }
        }
        return expr.toObject();
    }
    resolveWithInteractionReplace(folder, config) {
        throw ( new Error('resolveWithInteractionReplace not implemented.'));
    }
    resolveWithInteraction(folder, config) {
        throw ( new Error('resolveWithInteraction not implemented.'));
    }
    contributeVariable(variable, resolution) {
        if (( this._contributedVariables.has(variable))) {
            throw ( new Error('Variable ' + variable + ' is contributed twice.'));
        }
        else {
            this.resolvableVariables.add(variable);
            this._contributedVariables.set(variable, resolution);
        }
    }
    fsPath(displayUri) {
        return this._labelService ? this._labelService.getUriLabel(displayUri, { noPrefix: true }) : displayUri.fsPath;
    }
    async evaluateSingleVariable(environment, replacement, folderUri, commandValueMapping) {
        const { name: variable, arg: argument } = replacement;
        const getFilePath = (variableKind) => {
            const filePath = this._context.getFilePath();
            if (filePath) {
                return normalizeDriveLetter(filePath);
            }
            throw ( new VariableError(variableKind, ( localize(
                11994,
                "Variable {0} can not be resolved. Please open an editor.",
                replacement.id
            ))));
        };
        const getFolderPathForFile = (variableKind) => {
            const filePath = getFilePath(variableKind);
            if (this._context.getWorkspaceFolderPathForFile) {
                const folderPath = this._context.getWorkspaceFolderPathForFile();
                if (folderPath) {
                    return normalizeDriveLetter(folderPath);
                }
            }
            throw ( new VariableError(variableKind, ( localize(
                11995,
                "Variable {0}: can not find workspace folder of '{1}'.",
                replacement.id,
                basename(filePath)
            ))));
        };
        const getFolderUri = (variableKind) => {
            if (argument) {
                const folder = this._context.getFolderUri(argument);
                if (folder) {
                    return folder;
                }
                throw ( new VariableError(variableKind, ( localize(
                    11996,
                    "Variable {0} can not be resolved. No such folder '{1}'.",
                    variableKind,
                    argument
                ))));
            }
            if (folderUri) {
                return folderUri;
            }
            if (this._context.getWorkspaceFolderCount() > 1) {
                throw ( new VariableError(variableKind, ( localize(
                    11997,
                    "Variable {0} can not be resolved in a multi folder workspace. Scope this variable using ':' and a workspace folder name.",
                    variableKind
                ))));
            }
            throw ( new VariableError(variableKind, ( localize(
                11998,
                "Variable {0} can not be resolved. Please open a folder.",
                variableKind
            ))));
        };
        switch (variable) {
            case 'env':
                if (argument) {
                    if (environment.env) {
                        const env = environment.env[isWindows ? argument.toLowerCase() : argument];
                        if (isString(env)) {
                            return env;
                        }
                    }
                    return '';
                }
                throw ( new VariableError(VariableKind.Env, ( localize(
                    11999,
                    "Variable {0} can not be resolved because no environment variable name is given.",
                    replacement.id
                ))));
            case 'config':
                if (argument) {
                    const config = this._context.getConfigurationValue(folderUri, argument);
                    if (isUndefinedOrNull(config)) {
                        throw ( new VariableError(VariableKind.Config, ( localize(
                            12000,
                            "Variable {0} can not be resolved because setting '{1}' not found.",
                            replacement.id,
                            argument
                        ))));
                    }
                    if (isObject(config)) {
                        throw ( new VariableError(VariableKind.Config, ( localize(
                            12001,
                            "Variable {0} can not be resolved because '{1}' is a structured value.",
                            replacement.id,
                            argument
                        ))));
                    }
                    return config;
                }
                throw ( new VariableError(VariableKind.Config, ( localize(
                    12002,
                    "Variable {0} can not be resolved because no settings name is given.",
                    replacement.id
                ))));
            case 'command':
                return this.resolveFromMap(VariableKind.Command, replacement.id, argument, commandValueMapping, 'command');
            case 'input':
                return this.resolveFromMap(VariableKind.Input, replacement.id, argument, commandValueMapping, 'input');
            case 'extensionInstallFolder':
                if (argument) {
                    const ext = await this._context.getExtension(argument);
                    if (!ext) {
                        throw ( new VariableError(VariableKind.ExtensionInstallFolder, ( localize(
                            12003,
                            "Variable {0} can not be resolved because the extension {1} is not installed.",
                            replacement.id,
                            argument
                        ))));
                    }
                    return this.fsPath(ext.extensionLocation);
                }
                throw ( new VariableError(VariableKind.ExtensionInstallFolder, ( localize(
                    12004,
                    "Variable {0} can not be resolved because no extension name is given.",
                    replacement.id
                ))));
            default: {
                switch (variable) {
                    case 'workspaceRoot':
                    case 'workspaceFolder': {
                        const uri = getFolderUri(VariableKind.WorkspaceFolder);
                        return uri ? normalizeDriveLetter(this.fsPath(uri)) : undefined;
                    }
                    case 'cwd': {
                        if (!folderUri && !argument) {
                            return cwd();
                        }
                        const uri = getFolderUri(VariableKind.Cwd);
                        return uri ? normalizeDriveLetter(this.fsPath(uri)) : undefined;
                    }
                    case 'workspaceRootFolderName':
                    case 'workspaceFolderBasename': {
                        const uri = getFolderUri(VariableKind.WorkspaceFolderBasename);
                        return uri ? normalizeDriveLetter(basename(this.fsPath(uri))) : undefined;
                    }
                    case 'userHome':
                        if (environment.userHome) {
                            return environment.userHome;
                        }
                        throw ( new VariableError(VariableKind.UserHome, ( localize(
                            12005,
                            "Variable {0} can not be resolved. UserHome path is not defined",
                            replacement.id
                        ))));
                    case 'lineNumber': {
                        const lineNumber = this._context.getLineNumber();
                        if (lineNumber) {
                            return lineNumber;
                        }
                        throw ( new VariableError(VariableKind.LineNumber, ( localize(
                            12006,
                            "Variable {0} can not be resolved. Make sure to have a line selected in the active editor.",
                            replacement.id
                        ))));
                    }
                    case 'columnNumber': {
                        const columnNumber = this._context.getColumnNumber();
                        if (columnNumber) {
                            return columnNumber;
                        }
                        throw ( new Error(( localize(
                            12007,
                            "Variable {0} can not be resolved. Make sure to have a column selected in the active editor.",
                            replacement.id
                        ))));
                    }
                    case 'selectedText': {
                        const selectedText = this._context.getSelectedText();
                        if (selectedText) {
                            return selectedText;
                        }
                        throw ( new VariableError(VariableKind.SelectedText, ( localize(
                            12008,
                            "Variable {0} can not be resolved. Make sure to have some text selected in the active editor.",
                            replacement.id
                        ))));
                    }
                    case 'file':
                        return getFilePath(VariableKind.File);
                    case 'fileWorkspaceFolder':
                        return getFolderPathForFile(VariableKind.FileWorkspaceFolder);
                    case 'fileWorkspaceFolderBasename':
                        return basename(getFolderPathForFile(VariableKind.FileWorkspaceFolderBasename));
                    case 'relativeFile':
                        if (folderUri || argument) {
                            return relative(this.fsPath(getFolderUri(VariableKind.RelativeFile)), getFilePath(VariableKind.RelativeFile));
                        }
                        return getFilePath(VariableKind.RelativeFile);
                    case 'relativeFileDirname': {
                        const dirname$1 = dirname(getFilePath(VariableKind.RelativeFileDirname));
                        if (folderUri || argument) {
                            const relative$1 = relative(this.fsPath(getFolderUri(VariableKind.RelativeFileDirname)), dirname$1);
                            return relative$1.length === 0 ? '.' : relative$1;
                        }
                        return dirname$1;
                    }
                    case 'fileDirname':
                        return dirname(getFilePath(VariableKind.FileDirname));
                    case 'fileExtname':
                        return extname(getFilePath(VariableKind.FileExtname));
                    case 'fileBasename':
                        return basename(getFilePath(VariableKind.FileBasename));
                    case 'fileBasenameNoExtension': {
                        const basename$1 = basename(getFilePath(VariableKind.FileBasenameNoExtension));
                        return (basename$1.slice(0, basename$1.length - extname(basename$1).length));
                    }
                    case 'fileDirnameBasename':
                        return basename(dirname(getFilePath(VariableKind.FileDirnameBasename)));
                    case 'execPath': {
                        const ep = this._context.getExecPath();
                        if (ep) {
                            return ep;
                        }
                        return replacement.id;
                    }
                    case 'execInstallFolder': {
                        const ar = this._context.getAppRoot();
                        if (ar) {
                            return ar;
                        }
                        return replacement.id;
                    }
                    case 'pathSeparator':
                    case '/':
                        return sep;
                    default: {
                        try {
                            return this.resolveFromMap(VariableKind.Unknown, replacement.id, argument, commandValueMapping, undefined);
                        }
                        catch {
                            return replacement.id;
                        }
                    }
                }
            }
        }
    }
    resolveFromMap(variableKind, match, argument, commandValueMapping, prefix) {
        if (argument && commandValueMapping) {
            const v = (prefix === undefined) ? commandValueMapping[argument] : commandValueMapping[prefix + ':' + argument];
            if (typeof v === 'string') {
                return v;
            }
            throw ( new VariableError(variableKind, ( localize(
                12009,
                "Variable {0} can not be resolved because the command has no value.",
                match
            ))));
        }
        return match;
    }
}

export { AbstractVariableResolverService };
