import { __decorate, __param } from '../../../../../../../../external/tslib/tslib.es6.js';
import { localizeWithPath } from '../../../../../nls.js';
import { toErrorMessage } from '../../../../../base/common/errorMessage.js';
import { isEqual, basename } from '../../../../../base/common/resources.js';
import { Action } from '../../../../../base/common/actions.js';
import { URI } from '../../../../../base/common/uri.js';
import { FileOperationResult } from '../../../../../platform/files/common/files.js';
import { ITextFileService } from '../../../../services/textfile/common/textfiles.service.js';
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
import { Disposable, dispose } from '../../../../../base/common/lifecycle.js';
import { ITextModelService } from '../../../../../editor/common/services/resolverService.js';
import { ResourceMap } from '../../../../../base/common/map.js';
import { DiffEditorInput } from '../../../../common/editor/diffEditorInput.js';
import { RawContextKey } from '../../../../../platform/contextkey/common/contextkey.js';
import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.service.js';
import { TextFileContentProvider } from '../../common/files.js';
import { FileEditorInput } from './fileEditorInput.js';
import { SAVE_FILE_AS_LABEL } from '../fileConstants.js';
import '../../../../../platform/notification/common/notification.js';
import { INotificationService } from '../../../../../platform/notification/common/notification.service.js';
import { IOpenerService } from '../../../../../platform/opener/common/opener.service.js';
import { StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
import { IStorageService } from '../../../../../platform/storage/common/storage.service.js';
import { IProductService } from '../../../../../platform/product/common/productService.service.js';
import { Event } from '../../../../../base/common/event.js';
import { IEditorService } from '../../../../services/editor/common/editorService.service.js';
import { isWindows } from '../../../../../base/common/platform.js';
import { Schemas } from '../../../../../base/common/network.js';
import { IPreferencesService } from '../../../../services/preferences/common/preferences.service.js';
import { SaveReason, SideBySideEditor } from '../../../../common/editor.js';
import { hash } from '../../../../../base/common/hash.js';
import Severity$1 from '../../../../../base/common/severity.js';
const _moduleId = "vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler";
const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext';
const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution';
const LEARN_MORE_DIRTY_WRITE_IGNORE_KEY = 'learnMoreDirtyWriteError';
const conflictEditorHelp = ( localizeWithPath(
    _moduleId,
    0,
    "Use the actions in the editor tool bar to either undo your changes or overwrite the content of the file with your changes."
));
let TextFileSaveErrorHandler = class TextFileSaveErrorHandler extends Disposable {
    static { this.ID = 'workbench.contrib.textFileSaveErrorHandler'; }
    constructor(notificationService, textFileService, contextKeyService, editorService, textModelService, instantiationService, storageService) {
        super();
        this.notificationService = notificationService;
        this.textFileService = textFileService;
        this.contextKeyService = contextKeyService;
        this.editorService = editorService;
        this.instantiationService = instantiationService;
        this.storageService = storageService;
        this.messages = ( (new ResourceMap()));
        this.conflictResolutionContext = ( (new RawContextKey(CONFLICT_RESOLUTION_CONTEXT, false, true))).bindTo(this.contextKeyService);
        this.activeConflictResolutionResource = undefined;
        const provider = this._register(instantiationService.createInstance(TextFileContentProvider));
        this._register(textModelService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, provider));
        this.textFileService.files.saveErrorHandler = this;
        this.registerListeners();
    }
    registerListeners() {
        this._register(this.textFileService.files.onDidSave(e => this.onFileSavedOrReverted(e.model.resource)));
        this._register(this.textFileService.files.onDidRevert(model => this.onFileSavedOrReverted(model.resource)));
        this._register(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChanged()));
    }
    onActiveEditorChanged() {
        let isActiveEditorSaveConflictResolution = false;
        let activeConflictResolutionResource;
        const activeInput = this.editorService.activeEditor;
        if (activeInput instanceof DiffEditorInput) {
            const resource = activeInput.original.resource;
            if (resource?.scheme === CONFLICT_RESOLUTION_SCHEME) {
                isActiveEditorSaveConflictResolution = true;
                activeConflictResolutionResource = activeInput.modified.resource;
            }
        }
        this.conflictResolutionContext.set(isActiveEditorSaveConflictResolution);
        this.activeConflictResolutionResource = activeConflictResolutionResource;
    }
    onFileSavedOrReverted(resource) {
        const messageHandle = this.messages.get(resource);
        if (messageHandle) {
            messageHandle.close();
            this.messages.delete(resource);
        }
    }
    onSaveError(error, model, options) {
        const fileOperationError = error;
        const resource = model.resource;
        let message;
        const primaryActions = [];
        const secondaryActions = [];
        if (fileOperationError.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE) {
            if (this.activeConflictResolutionResource && isEqual(this.activeConflictResolutionResource, model.resource)) {
                if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, StorageScope.APPLICATION)) {
                    return;
                }
                message = conflictEditorHelp;
                primaryActions.push(this.instantiationService.createInstance(ResolveConflictLearnMoreAction));
                secondaryActions.push(this.instantiationService.createInstance(DoNotShowResolveConflictLearnMoreAction));
            }
            else {
                message = ( localizeWithPath(
                    _moduleId,
                    1,
                    "Failed to save '{0}': The content of the file is newer. Please compare your version with the file contents or overwrite the content of the file with your changes.",
                    basename(resource)
                ));
                primaryActions.push(this.instantiationService.createInstance(ResolveSaveConflictAction, model));
                primaryActions.push(this.instantiationService.createInstance(SaveModelIgnoreModifiedSinceAction, model, options));
                secondaryActions.push(this.instantiationService.createInstance(ConfigureSaveConflictAction));
            }
        }
        else {
            const isWriteLocked = fileOperationError.fileOperationResult === FileOperationResult.FILE_WRITE_LOCKED;
            const triedToUnlock = isWriteLocked && fileOperationError.options?.unlock;
            const isPermissionDenied = fileOperationError.fileOperationResult === FileOperationResult.FILE_PERMISSION_DENIED;
            const canSaveElevated = resource.scheme === Schemas.file;
            if (canSaveElevated && (isPermissionDenied || triedToUnlock)) {
                primaryActions.push(this.instantiationService.createInstance(SaveModelElevatedAction, model, options, !!triedToUnlock));
            }
            else if (isWriteLocked) {
                primaryActions.push(this.instantiationService.createInstance(UnlockModelAction, model, options));
            }
            else {
                primaryActions.push(this.instantiationService.createInstance(RetrySaveModelAction, model, options));
            }
            primaryActions.push(this.instantiationService.createInstance(SaveModelAsAction, model));
            primaryActions.push(this.instantiationService.createInstance(DiscardModelAction, model));
            if (isWriteLocked) {
                if (triedToUnlock && canSaveElevated) {
                    message = isWindows ? ( localizeWithPath(
                        _moduleId,
                        2,
                        "Failed to save '{0}': File is read-only. Select 'Overwrite as Admin' to retry as administrator.",
                        basename(resource)
                    )) : ( localizeWithPath(
                        _moduleId,
                        3,
                        "Failed to save '{0}': File is read-only. Select 'Overwrite as Sudo' to retry as superuser.",
                        basename(resource)
                    ));
                }
                else {
                    message = ( localizeWithPath(
                        _moduleId,
                        4,
                        "Failed to save '{0}': File is read-only. Select 'Overwrite' to attempt to make it writeable.",
                        basename(resource)
                    ));
                }
            }
            else if (canSaveElevated && isPermissionDenied) {
                message = isWindows ? ( localizeWithPath(
                    _moduleId,
                    5,
                    "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.",
                    basename(resource)
                )) : ( localizeWithPath(
                    _moduleId,
                    6,
                    "Failed to save '{0}': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.",
                    basename(resource)
                ));
            }
            else {
                message = ( localizeWithPath(
                    _moduleId,
                    7,
                    "Failed to save '{0}': {1}",
                    basename(resource),
                    toErrorMessage(error, false)
                ));
            }
        }
        const actions = { primary: primaryActions, secondary: secondaryActions };
        const handle = this.notificationService.notify({
            id: `${hash(( (model.resource.toString())))}`,
            severity: Severity$1.Error,
            message,
            actions
        });
        Event.once(handle.onDidClose)(() => { dispose(primaryActions); dispose(secondaryActions); });
        this.messages.set(model.resource, handle);
    }
    dispose() {
        super.dispose();
        this.messages.clear();
    }
};
TextFileSaveErrorHandler = ( (__decorate([
    ( (__param(0, INotificationService))),
    ( (__param(1, ITextFileService))),
    ( (__param(2, IContextKeyService))),
    ( (__param(3, IEditorService))),
    ( (__param(4, ITextModelService))),
    ( (__param(5, IInstantiationService))),
    ( (__param(6, IStorageService)))
], TextFileSaveErrorHandler)));
const pendingResolveSaveConflictMessages = [];
function clearPendingResolveSaveConflictMessages() {
    while (pendingResolveSaveConflictMessages.length > 0) {
        const item = pendingResolveSaveConflictMessages.pop();
        item?.close();
    }
}
let ResolveConflictLearnMoreAction = class ResolveConflictLearnMoreAction extends Action {
    constructor(openerService) {
        super('workbench.files.action.resolveConflictLearnMore', ( localizeWithPath(_moduleId, 8, "Learn More")));
        this.openerService = openerService;
    }
    async run() {
        await this.openerService.open(( (URI.parse('https://go.microsoft.com/fwlink/?linkid=868264'))));
    }
};
ResolveConflictLearnMoreAction = ( (__decorate([
    ( (__param(0, IOpenerService)))
], ResolveConflictLearnMoreAction)));
let DoNotShowResolveConflictLearnMoreAction = class DoNotShowResolveConflictLearnMoreAction extends Action {
    constructor(storageService) {
        super('workbench.files.action.resolveConflictLearnMoreDoNotShowAgain', ( localizeWithPath(_moduleId, 9, "Don't Show Again")));
        this.storageService = storageService;
    }
    async run(notification) {
        this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true, StorageScope.APPLICATION, StorageTarget.USER);
        notification.dispose();
    }
};
DoNotShowResolveConflictLearnMoreAction = ( (__decorate([
    ( (__param(0, IStorageService)))
], DoNotShowResolveConflictLearnMoreAction)));
let ResolveSaveConflictAction = class ResolveSaveConflictAction extends Action {
    constructor(model, editorService, notificationService, instantiationService, productService) {
        super('workbench.files.action.resolveConflict', ( localizeWithPath(_moduleId, 10, "Compare")));
        this.model = model;
        this.editorService = editorService;
        this.notificationService = notificationService;
        this.instantiationService = instantiationService;
        this.productService = productService;
    }
    async run() {
        if (!this.model.isDisposed()) {
            const resource = this.model.resource;
            const name = basename(resource);
            const editorLabel = ( localizeWithPath(
                _moduleId,
                11,
                "{0} (in file) ↔ {1} (in {2}) - Resolve save conflict",
                name,
                name,
                this.productService.nameLong
            ));
            await TextFileContentProvider.open(resource, CONFLICT_RESOLUTION_SCHEME, editorLabel, this.editorService, { pinned: true });
            const actions = { primary: [this.instantiationService.createInstance(ResolveConflictLearnMoreAction)] };
            const handle = this.notificationService.notify({
                id: `${hash(( (resource.toString())))}`,
                severity: Severity$1.Info,
                message: conflictEditorHelp,
                actions,
                neverShowAgain: { id: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, isSecondary: true }
            });
            Event.once(handle.onDidClose)(() => dispose(actions.primary));
            pendingResolveSaveConflictMessages.push(handle);
        }
    }
};
ResolveSaveConflictAction = ( (__decorate([
    ( (__param(1, IEditorService))),
    ( (__param(2, INotificationService))),
    ( (__param(3, IInstantiationService))),
    ( (__param(4, IProductService)))
], ResolveSaveConflictAction)));
class SaveModelElevatedAction extends Action {
    constructor(model, options, triedToUnlock) {
        super('workbench.files.action.saveModelElevated', triedToUnlock ? isWindows ? ( localizeWithPath(_moduleId, 12, "Overwrite as Admin...")) : ( localizeWithPath(_moduleId, 13, "Overwrite as Sudo...")) : isWindows ? ( localizeWithPath(_moduleId, 14, "Retry as Admin...")) : ( localizeWithPath(_moduleId, 15, "Retry as Sudo...")));
        this.model = model;
        this.options = options;
        this.triedToUnlock = triedToUnlock;
    }
    async run() {
        if (!this.model.isDisposed()) {
            await this.model.save({
                ...this.options,
                writeElevated: true,
                writeUnlock: this.triedToUnlock,
                reason: SaveReason.EXPLICIT
            });
        }
    }
}
class RetrySaveModelAction extends Action {
    constructor(model, options) {
        super('workbench.files.action.saveModel', ( localizeWithPath(_moduleId, 16, "Retry")));
        this.model = model;
        this.options = options;
    }
    async run() {
        if (!this.model.isDisposed()) {
            await this.model.save({ ...this.options, reason: SaveReason.EXPLICIT });
        }
    }
}
class DiscardModelAction extends Action {
    constructor(model) {
        super('workbench.files.action.discardModel', ( localizeWithPath(_moduleId, 17, "Discard")));
        this.model = model;
    }
    async run() {
        if (!this.model.isDisposed()) {
            await this.model.revert();
        }
    }
}
let SaveModelAsAction = class SaveModelAsAction extends Action {
    constructor(model, editorService) {
        super('workbench.files.action.saveModelAs', SAVE_FILE_AS_LABEL.value);
        this.model = model;
        this.editorService = editorService;
    }
    async run() {
        if (!this.model.isDisposed()) {
            const editor = this.findEditor();
            if (editor) {
                await this.editorService.save(editor, { saveAs: true, reason: SaveReason.EXPLICIT });
            }
        }
    }
    findEditor() {
        let preferredMatchingEditor;
        const editors = this.editorService.findEditors(this.model.resource, { supportSideBySide: SideBySideEditor.PRIMARY });
        for (const identifier of editors) {
            if (identifier.editor instanceof FileEditorInput) {
                preferredMatchingEditor = identifier;
                break;
            }
            else if (!preferredMatchingEditor) {
                preferredMatchingEditor = identifier;
            }
        }
        return preferredMatchingEditor;
    }
};
SaveModelAsAction = ( (__decorate([
    ( (__param(1, IEditorService)))
], SaveModelAsAction)));
class UnlockModelAction extends Action {
    constructor(model, options) {
        super('workbench.files.action.unlock', ( localizeWithPath(_moduleId, 18, "Overwrite")));
        this.model = model;
        this.options = options;
    }
    async run() {
        if (!this.model.isDisposed()) {
            await this.model.save({ ...this.options, writeUnlock: true, reason: SaveReason.EXPLICIT });
        }
    }
}
class SaveModelIgnoreModifiedSinceAction extends Action {
    constructor(model, options) {
        super('workbench.files.action.saveIgnoreModifiedSince', ( localizeWithPath(_moduleId, 18, "Overwrite")));
        this.model = model;
        this.options = options;
    }
    async run() {
        if (!this.model.isDisposed()) {
            await this.model.save({ ...this.options, ignoreModifiedSince: true, reason: SaveReason.EXPLICIT });
        }
    }
}
let ConfigureSaveConflictAction = class ConfigureSaveConflictAction extends Action {
    constructor(preferencesService) {
        super('workbench.files.action.configureSaveConflict', ( localizeWithPath(_moduleId, 19, "Configure")));
        this.preferencesService = preferencesService;
    }
    async run() {
        this.preferencesService.openSettings({ query: 'files.saveConflictResolution' });
    }
};
ConfigureSaveConflictAction = ( (__decorate([
    ( (__param(0, IPreferencesService)))
], ConfigureSaveConflictAction)));
const acceptLocalChangesCommand = (accessor, resource) => {
    return acceptOrRevertLocalChangesCommand(accessor, resource, true);
};
const revertLocalChangesCommand = (accessor, resource) => {
    return acceptOrRevertLocalChangesCommand(accessor, resource, false);
};
async function acceptOrRevertLocalChangesCommand(accessor, resource, accept) {
    const editorService = accessor.get(IEditorService);
    const editorPane = editorService.activeEditorPane;
    if (!editorPane) {
        return;
    }
    const editor = editorPane.input;
    const group = editorPane.group;
    clearPendingResolveSaveConflictMessages();
    if (accept) {
        const options = { ignoreModifiedSince: true, reason: SaveReason.EXPLICIT };
        await editorService.save({ editor, groupId: group.id }, options);
    }
    else {
        await editorService.revert({ editor, groupId: group.id });
    }
    await editorService.openEditor({ resource }, group);
    return group.closeEditor(editor);
}
export { CONFLICT_RESOLUTION_CONTEXT, CONFLICT_RESOLUTION_SCHEME, TextFileSaveErrorHandler, acceptLocalChangesCommand, revertLocalChangesCommand };
