/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import * as lsp from 'vscode-languageserver';
import { toTextDocumentEdit } from '../protocol-translation.js';
import * as errorCodes from '../utils/errorCodes.js';
import * as fixNames from '../utils/fixNames.js';
import { CodeActionKind } from '../utils/types.js';
import { Range } from '../utils/typeConverters.js';
async function buildIndividualFixes(fixes, client, file, documents, diagnostics) {
    const edits = [];
    for (const diagnostic of diagnostics) {
        for (const { codes, fixName } of fixes) {
            if (!codes.has(diagnostic.code)) {
                continue;
            }
            const args = {
                ...Range.toFileRangeRequestArgs(file, diagnostic.range),
                errorCodes: [+diagnostic.code],
            };
            const response = await client.request("getCodeFixes" /* CommandTypes.GetCodeFixes */, args);
            if (response.type !== 'response') {
                continue;
            }
            const fix = response.body?.find(fix => fix.fixName === fixName);
            if (fix) {
                edits.push(...fix.changes.map(change => toTextDocumentEdit(change, documents)));
                break;
            }
        }
    }
    return edits;
}
async function buildCombinedFix(fixes, client, file, documents, diagnostics) {
    const edits = [];
    for (const diagnostic of diagnostics) {
        for (const { codes, fixName } of fixes) {
            if (!codes.has(diagnostic.code)) {
                continue;
            }
            const args = {
                ...Range.toFileRangeRequestArgs(file, diagnostic.range),
                errorCodes: [+diagnostic.code],
            };
            const response = await client.request("getCodeFixes" /* CommandTypes.GetCodeFixes */, args);
            if (response.type !== 'response' || !response.body?.length) {
                continue;
            }
            const fix = response.body?.find(fix => fix.fixName === fixName);
            if (!fix) {
                continue;
            }
            if (!fix.fixId) {
                edits.push(...fix.changes.map(change => toTextDocumentEdit(change, documents)));
                return edits;
            }
            const combinedArgs = {
                scope: {
                    type: 'file',
                    args: { file },
                },
                fixId: fix.fixId,
            };
            const combinedResponse = await client.request("getCombinedCodeFix" /* CommandTypes.GetCombinedCodeFix */, combinedArgs);
            if (combinedResponse.type !== 'response' || !combinedResponse.body) {
                return edits;
            }
            edits.push(...combinedResponse.body.changes.map(change => toTextDocumentEdit(change, documents)));
            return edits;
        }
    }
    return edits;
}
// #region Source Actions
class SourceAction {
}
class SourceFixAll extends SourceAction {
    constructor() {
        super(...arguments);
        this.title = 'Fix all';
    }
    async build(client, file, documents, diagnostics) {
        const edits = [];
        edits.push(...await buildIndividualFixes([
            { codes: errorCodes.incorrectlyImplementsInterface, fixName: fixNames.classIncorrectlyImplementsInterface },
            { codes: errorCodes.asyncOnlyAllowedInAsyncFunctions, fixName: fixNames.awaitInSyncFunction },
        ], client, file, documents, diagnostics));
        edits.push(...await buildCombinedFix([
            { codes: errorCodes.unreachableCode, fixName: fixNames.unreachableCode },
        ], client, file, documents, diagnostics));
        if (!edits.length) {
            return null;
        }
        return lsp.CodeAction.create(this.title, { documentChanges: edits }, SourceFixAll.kind.value);
    }
}
SourceFixAll.kind = CodeActionKind.SourceFixAllTs;
class SourceRemoveUnused extends SourceAction {
    constructor() {
        super(...arguments);
        this.title = 'Remove all unused code';
    }
    async build(client, file, documents, diagnostics) {
        const edits = await buildCombinedFix([
            { codes: errorCodes.variableDeclaredButNeverUsed, fixName: fixNames.unusedIdentifier },
        ], client, file, documents, diagnostics);
        if (!edits.length) {
            return null;
        }
        return lsp.CodeAction.create(this.title, { documentChanges: edits }, SourceRemoveUnused.kind.value);
    }
}
SourceRemoveUnused.kind = CodeActionKind.SourceRemoveUnusedTs;
class SourceAddMissingImports extends SourceAction {
    constructor() {
        super(...arguments);
        this.title = 'Add all missing imports';
    }
    async build(client, file, documents, diagnostics) {
        const edits = await buildCombinedFix([
            { codes: errorCodes.cannotFindName, fixName: fixNames.fixImport },
        ], client, file, documents, diagnostics);
        if (!edits.length) {
            return null;
        }
        return lsp.CodeAction.create(this.title, { documentChanges: edits }, SourceAddMissingImports.kind.value);
    }
}
SourceAddMissingImports.kind = CodeActionKind.SourceAddMissingImportsTs;
//#endregion
export class TypeScriptAutoFixProvider {
    constructor(client) {
        this.client = client;
    }
    static get kinds() {
        return TypeScriptAutoFixProvider.kindProviders.map(provider => provider.kind);
    }
    async provideCodeActions(kinds, file, diagnostics, documents) {
        const results = [];
        for (const provider of TypeScriptAutoFixProvider.kindProviders) {
            if (kinds.some(kind => kind.contains(provider.kind))) {
                results.push((new provider).build(this.client, file, documents, diagnostics));
            }
        }
        return (await Promise.all(results)).flatMap(result => result || []);
    }
}
TypeScriptAutoFixProvider.kindProviders = [
    SourceFixAll,
    SourceRemoveUnused,
    SourceAddMissingImports,
];
//# sourceMappingURL=fix-all.js.map