import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { alert } from '../../../../base/browser/ui/aria/aria.js';
import { raceCancellation, Barrier, DeferredPromise, Queue } from '../../../../base/common/async.js';
import { CancellationTokenSource } from '../../../../base/common/cancellation.js';
import { toErrorMessage } from '../../../../base/common/errorMessage.js';
import { onUnexpectedError } from '../../../../base/common/errors.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { Lazy } from '../../../../base/common/lazy.js';
import { DisposableStore, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
import { MovingAverage } from '../../../../base/common/numbers.js';
import { StopWatch } from '../../../../base/common/stopwatch.js';
import { assertType } from '../../../../base/common/types.js';
import { IBulkEditService } from '../../../../editor/browser/services/bulkEditService.js';
import { Position } from '../../../../editor/common/core/position.js';
import { Range } from '../../../../editor/common/core/range.js';
import { Selection } from '../../../../editor/common/core/selection.js';
import { TextEdit } from '../../../../editor/common/languages.js';
import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js';
import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/inlineCompletionsController.js';
import { localizeWithPath } from '../../../../nls.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
import '../../../../../../../override/vs/platform/dialogs/common/dialogs.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { ILogService } from '../../../../platform/log/common/log.js';
import { IChatWidgetService } from '../../chat/browser/chat.js';
import { ChatAgentLocation, IChatAgentService } from '../../chat/common/chatAgents.js';
import { ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, chatAgentLeader } from '../../chat/common/chatParserTypes.js';
import { IChatService } from '../../chat/common/chatService.js';
import { IInlineChatSavingService } from './inlineChatSavingService.js';
import { SessionPrompt, ReplyResponse, EmptyResponse, ErrorResponse, Session } from './inlineChatSession.js';
import { IInlineChatSessionService } from './inlineChatSessionService.js';
import { LiveStrategy, PreviewStrategy } from './inlineChatStrategies.js';
import { InlineChatZoneWidget } from './inlineChatZoneWidget.js';
import { INLINE_CHAT_ID, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING } from '../common/inlineChat.js';
import { ICommandService } from '../../../../platform/commands/common/commands.js';
import { InlineChatContentWidget } from './inlineChatContentWidget.js';
import { MessageController } from '../../../../editor/contrib/message/browser/messageController.js';
import { tail } from '../../../../base/common/arrays.js';
import { InlineChatError } from './inlineChatSessionServiceImpl.js';
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';
import { ChatInputPart } from '../../chat/browser/chatInputPart.js';
import { OffsetRange } from '../../../../editor/common/core/offsetRange.js';
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
class InlineChatRunOptions {
    static isInteractiveEditorOptions(options) {
        const { initialSelection, initialRange, message, autoSend, position, existingSession } = options;
        if (typeof message !== 'undefined' && typeof message !== 'string'
            || typeof autoSend !== 'undefined' && typeof autoSend !== 'boolean'
            || typeof initialRange !== 'undefined' && !Range.isIRange(initialRange)
            || typeof initialSelection !== 'undefined' && !Selection.isISelection(initialSelection)
            || typeof position !== 'undefined' && !Position.isIPosition(position)
            || typeof existingSession !== 'undefined' && !(existingSession instanceof Session)) {
            return false;
        }
        return true;
    }
}
let InlineChatController = class InlineChatController {
    static get(editor) {
        return editor.getContribution(INLINE_CHAT_ID);
    }
    constructor(_editor, _instaService, _inlineChatSessionService, _inlineChatSavingService, _editorWorkerService, _logService, _configurationService, _dialogService, contextKeyService, _chatAgentService, _chatService, _bulkEditService, _commandService, _languageFeatureService, _chatWidgetService) {
        this._editor = _editor;
        this._instaService = _instaService;
        this._inlineChatSessionService = _inlineChatSessionService;
        this._inlineChatSavingService = _inlineChatSavingService;
        this._editorWorkerService = _editorWorkerService;
        this._logService = _logService;
        this._configurationService = _configurationService;
        this._dialogService = _dialogService;
        this._chatAgentService = _chatAgentService;
        this._chatService = _chatService;
        this._bulkEditService = _bulkEditService;
        this._commandService = _commandService;
        this._languageFeatureService = _languageFeatureService;
        this._chatWidgetService = _chatWidgetService;
        this._isDisposed = false;
        this._store = ( new DisposableStore());
        this._messages = this._store.add(( new Emitter()));
        this._onWillStartSession = this._store.add(( new Emitter()));
        this.onWillStartSession = this._onWillStartSession.event;
        this.onDidAcceptInput = Event.filter(this._messages.event, m => m === 32 , this._store);
        this.onDidCancelInput = Event.filter(this._messages.event, m => m === 16  || m === 2 , this._store);
        this._sessionStore = this._store.add(( new DisposableStore()));
        this._stashedSession = this._store.add(( new MutableDisposable()));
        this._nextAttempt = 0;
        this._nextWithIntentDetection = true;
        this._forcedPlaceholder = undefined;
        this._ctxVisible = CTX_INLINE_CHAT_VISIBLE.bindTo(contextKeyService);
        this._ctxDidEdit = CTX_INLINE_CHAT_DID_EDIT.bindTo(contextKeyService);
        this._ctxUserDidEdit = CTX_INLINE_CHAT_USER_DID_EDIT.bindTo(contextKeyService);
        this._ctxResponseTypes = CTX_INLINE_CHAT_RESPONSE_TYPES.bindTo(contextKeyService);
        this._ctxLastFeedbackKind = CTX_INLINE_CHAT_LAST_FEEDBACK.bindTo(contextKeyService);
        this._ctxSupportIssueReporting = CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING.bindTo(contextKeyService);
        this._input = ( new Lazy(
            () => this._store.add(_instaService.createInstance(InlineChatContentWidget, this._editor))
        ));
        this._zone = ( new Lazy(
            () => this._store.add(_instaService.createInstance(InlineChatZoneWidget, this._editor))
        ));
        this._store.add(this._editor.onDidChangeModel(async (e) => {
            if (this._session || !e.newModelUrl) {
                return;
            }
            const existingSession = this._inlineChatSessionService.getSession(this._editor, e.newModelUrl);
            if (!existingSession) {
                return;
            }
            this._log('session RESUMING after model change', e);
            await this.run({ existingSession });
        }));
        this._store.add(this._inlineChatSessionService.onDidEndSession(e => {
            if (e.session === this._session && e.endedByExternalCause) {
                this._log('session ENDED by external cause');
                this._session = undefined;
                this._strategy?.cancel();
                this._resetWidget();
                this.cancelSession();
            }
        }));
        this._store.add(this._inlineChatSessionService.onDidMoveSession(async (e) => {
            if (e.editor === this._editor) {
                this._log('session RESUMING after move', e);
                await this.run({ existingSession: e.session });
            }
        }));
        this._log('NEW controller');
    }
    dispose() {
        if (this._currentRun) {
            this._messages.fire(((this._session?.lastExchange ? 4  : 2) ));
        }
        this._store.dispose();
        this._isDisposed = true;
        this._log('DISPOSED controller');
    }
    _log(message, ...more) {
        if (message instanceof Error) {
            this._logService.error(message, ...more);
        }
        else {
            this._logService.trace(`[IE] (editor:${this._editor.getId()})${message}`, ...more);
        }
    }
    getMessage() {
        return this._zone.value.widget.responseContent;
    }
    getId() {
        return INLINE_CHAT_ID;
    }
    _getMode() {
        return this._configurationService.getValue("inlineChat.mode" );
    }
    getWidgetPosition() {
        return this._zone.value.position;
    }
    async run(options = {}) {
        try {
            this.finishExistingSession();
            if (this._currentRun) {
                await this._currentRun;
            }
            if (options.initialSelection) {
                this._editor.setSelection(options.initialSelection);
            }
            this._stashedSession.clear();
            this._onWillStartSession.fire();
            this._currentRun = this._nextState("CREATE_SESSION" , options);
            await this._currentRun;
        }
        catch (error) {
            onUnexpectedError(error);
            if (this._session) {
                this._inlineChatSessionService.releaseSession(this._session);
            }
            this["PAUSE" ]();
        }
        finally {
            this._currentRun = undefined;
        }
    }
    async _nextState(state, options) {
        let nextState = state;
        while (nextState && !this._isDisposed) {
            this._log('setState to ', nextState);
            nextState = await this[nextState](options);
        }
    }
    async ["CREATE_SESSION" ](options) {
        assertType(this._session === undefined);
        assertType(this._editor.hasModel());
        let session = options.existingSession;
        let initPosition;
        if (options.position) {
            initPosition = Position.lift(options.position).delta(-1);
            delete options.position;
        }
        const widgetPosition = this._showWidget(true, initPosition);
        let errorMessage = ( localizeWithPath(
            'vs/workbench/contrib/inlineChat/browser/inlineChatController',
            'create.fail',
            "Failed to start editor chat"
        ));
        if (!session) {
            const createSessionCts = ( new CancellationTokenSource());
            const msgListener = Event.once(this._messages.event)(m => {
                this._log('state=_createSession) message received', m);
                if (m === 32 ) {
                    options.autoSend = true;
                    this._zone.value.widget.updateProgress(true);
                    this._zone.value.widget.updateInfo(( localizeWithPath(
                        'vs/workbench/contrib/inlineChat/browser/inlineChatController',
                        'welcome.2',
                        "Getting ready..."
                    )));
                }
                else {
                    createSessionCts.cancel();
                }
            });
            try {
                session = await this._inlineChatSessionService.createSession(this._editor, { editMode: this._getMode(), wholeRange: options.initialRange }, createSessionCts.token);
            }
            catch (error) {
                if (error instanceof InlineChatError || error?.name === InlineChatError.code) {
                    errorMessage = error.message;
                }
            }
            createSessionCts.dispose();
            msgListener.dispose();
            if (createSessionCts.token.isCancellationRequested) {
                if (session) {
                    this._inlineChatSessionService.releaseSession(session);
                }
                return "CANCEL" ;
            }
        }
        delete options.initialRange;
        delete options.existingSession;
        if (!session) {
            MessageController.get(this._editor)?.showMessage(errorMessage, widgetPosition);
            this._log('Failed to start editor chat');
            return "CANCEL" ;
        }
        switch (session.editMode) {
            case "preview" :
                this._strategy = this._instaService.createInstance(PreviewStrategy, session, this._editor, this._zone.value);
                break;
            case "live" :
            default:
                this._strategy = this._instaService.createInstance(LiveStrategy, session, this._editor, this._zone.value);
                break;
        }
        if (session.session.input) {
            options.message = session.session.input;
        }
        this._session = session;
        return "INIT_UI" ;
    }
    async ["INIT_UI" ](options) {
        assertType(this._session);
        assertType(this._strategy);
        InlineCompletionsController.get(this._editor)?.hide();
        this._sessionStore.clear();
        const wholeRangeDecoration = this._editor.createDecorationsCollection();
        const updateWholeRangeDecoration = () => {
            const newDecorations = this._strategy?.getWholeRangeDecoration() ?? [];
            wholeRangeDecoration.set(newDecorations);
        };
        this._sessionStore.add(toDisposable(() => wholeRangeDecoration.clear()));
        this._sessionStore.add(this._session.wholeRange.onDidChange(updateWholeRangeDecoration));
        updateWholeRangeDecoration();
        this._sessionStore.add(this._input.value.onDidBlur(() => this.cancelSession()));
        this._input.value.setSession(this._session);
        this._updatePlaceholder();
        const message = this._session.session.message ?? ( localizeWithPath(
            'vs/workbench/contrib/inlineChat/browser/inlineChatController',
            'welcome.1',
            "AI-generated code may be incorrect"
        ));
        this._zone.value.widget.updateInfo(message);
        this._showWidget(!this._session.lastExchange);
        this._sessionStore.add(this._editor.onDidChangeModel((e) => {
            const msg = this._session?.lastExchange
                ? 4
                : 2 ;
            this._log('model changed, pause or cancel session', msg, e);
            this._messages.fire(msg);
        }));
        const altVersionNow = this._editor.getModel()?.getAlternativeVersionId();
        this._sessionStore.add(this._editor.onDidChangeModelContent(e => {
            if (!this._session?.hunkData.ignoreTextModelNChanges) {
                this._ctxUserDidEdit.set(altVersionNow !== this._editor.getModel()?.getAlternativeVersionId());
            }
            if (this._session?.hunkData.ignoreTextModelNChanges || this._strategy?.hasFocus()) {
                return;
            }
            const wholeRange = this._session.wholeRange;
            let shouldFinishSession = false;
            if (this._configurationService.getValue("inlineChat.finishOnType" )) {
                for (const { range } of e.changes) {
                    shouldFinishSession = !Range.areIntersectingOrTouching(range, wholeRange.value);
                }
            }
            this._session.recordExternalEditOccurred(shouldFinishSession);
            if (shouldFinishSession) {
                this._log('text changed outside of whole range, FINISH session');
                this.finishExistingSession();
            }
        }));
        this._sessionStore.add(this._session.chatModel.onDidChange(e => {
            if (e.kind === 'addRequest' && e.request.response) {
                this._zone.value.widget.updateProgress(true);
                const listener = e.request.response.onDidChange(() => {
                    if (e.request.response?.isCanceled || e.request.response?.isComplete) {
                        this._zone.value.widget.updateProgress(false);
                        listener.dispose();
                    }
                });
            }
        }));
        this._ctxSupportIssueReporting.set(this._session.provider.supportIssueReporting ?? false);
        this._sessionStore.add(this._languageFeatureService.completionProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, hasAccessToAllModels: true }, {
            _debugDisplayName: 'inline chat commands',
            triggerCharacters: ['/'],
            provideCompletionItems: (model, position, context, token) => {
                if (position.lineNumber !== 1) {
                    return undefined;
                }
                if (!this._session || !this._session.session.slashCommands) {
                    return undefined;
                }
                const widget = this._chatWidgetService.getWidgetByInputUri(model.uri);
                if (widget !== this._zone.value.widget.chatWidget && widget !== this._input.value.chatWidget) {
                    return undefined;
                }
                const result = { suggestions: [], incomplete: false };
                for (const command of this._session.session.slashCommands) {
                    const withSlash = `/${command.command}`;
                    result.suggestions.push({
                        label: { label: withSlash, description: command.detail ?? '' },
                        kind: 18 ,
                        insertText: withSlash,
                        range: Range.fromPositions(( new Position(1, 1)), position),
                        command: command.executeImmediately ? { id: 'workbench.action.chat.acceptInput', title: withSlash } : undefined
                    });
                }
                return result;
            }
        }));
        const updateSlashDecorations = (collection, model) => {
            const newDecorations = [];
            for (const command of (this._session?.session.slashCommands ?? []).sort((a, b) => b.command.length - a.command.length)) {
                const withSlash = `/${command.command}`;
                const firstLine = model.getLineContent(1);
                if (firstLine.startsWith(withSlash)) {
                    newDecorations.push({
                        range: ( new Range(1, 1, 1, withSlash.length + 1)),
                        options: {
                            description: 'inline-chat-slash-command',
                            inlineClassName: 'inline-chat-slash-command',
                            after: {
                                content: ' '
                            }
                        }
                    });
                    if (firstLine.trim() === `/${command.command}`) {
                        newDecorations.push({
                            range: ( new Range(1, withSlash.length, 1, withSlash.length)),
                            options: {
                                description: 'inline-chat-slash-command-detail',
                                after: {
                                    content: `${command.detail}`,
                                    inlineClassName: 'inline-chat-slash-command-detail'
                                }
                            }
                        });
                    }
                    break;
                }
            }
            collection.set(newDecorations);
        };
        const inputInputEditor = this._input.value.chatWidget.inputEditor;
        const zoneInputEditor = this._zone.value.widget.chatWidget.inputEditor;
        const inputDecorations = inputInputEditor.createDecorationsCollection();
        const zoneDecorations = zoneInputEditor.createDecorationsCollection();
        this._sessionStore.add(inputInputEditor.onDidChangeModelContent(() => updateSlashDecorations(inputDecorations, inputInputEditor.getModel())));
        this._sessionStore.add(zoneInputEditor.onDidChangeModelContent(() => updateSlashDecorations(zoneDecorations, zoneInputEditor.getModel())));
        this._sessionStore.add(toDisposable(() => {
            inputDecorations.clear();
            zoneDecorations.clear();
        }));
        if (!this._session.lastExchange) {
            return "WAIT_FOR_INPUT" ;
        }
        else if (options.isUnstashed) {
            delete options.isUnstashed;
            return "APPLY_RESPONSE" ;
        }
        else {
            return "SHOW_RESPONSE" ;
        }
    }
    async ["WAIT_FOR_INPUT" ](options) {
        assertType(this._session);
        assertType(this._strategy);
        this._updatePlaceholder();
        if (options.message) {
            this.updateInput(options.message);
            alert(options.message);
            delete options.message;
            this._showWidget(false);
        }
        let message = 0 ;
        let request;
        const barrier = ( new Barrier());
        const store = ( new DisposableStore());
        store.add(this._session.chatModel.onDidChange(e => {
            if (e.kind === 'addRequest') {
                request = e.request;
                message = 32 ;
                barrier.open();
            }
        }));
        store.add(this._strategy.onDidAccept(() => this.acceptSession()));
        store.add(this._strategy.onDidDiscard(() => this.cancelSession()));
        store.add(Event.once(this._messages.event)(m => {
            this._log('state=_waitForInput) message received', m);
            message = m;
            barrier.open();
        }));
        if (options.autoSend) {
            delete options.autoSend;
            this._showWidget(false);
            this._zone.value.widget.chatWidget.acceptInput();
        }
        await barrier.wait();
        store.dispose();
        if (message & ((16  | 2) )) {
            return "CANCEL" ;
        }
        if (message & 4 ) {
            return "PAUSE" ;
        }
        if (message & 1 ) {
            this._zone.value.widget.selectAll(false);
            return "DONE" ;
        }
        if (!request?.message.text) {
            return "WAIT_FOR_INPUT" ;
        }
        const input = request.message.text;
        this._zone.value.widget.value = input;
        let slashCommandLike = request.message.parts.find(part => part instanceof ChatRequestAgentSubcommandPart || part instanceof ChatRequestSlashCommandPart);
        const refer = this._session.session.slashCommands?.some(value => {
            if (value.refer) {
                if (slashCommandLike?.text === `/${value.command}`) {
                    return true;
                }
                if (request?.message.text.startsWith(`/${value.command}`)) {
                    slashCommandLike = ( new ChatRequestSlashCommandPart(( new OffsetRange(0, 1)), ( new Range(1, 1, 1, 1)), { command: value.command, detail: value.detail ?? '' }));
                    return true;
                }
            }
            return false;
        });
        if (refer && slashCommandLike && !this._session.lastExchange) {
            this._log('[IE] seeing refer command, continuing outside editor', this._session.provider.extensionId);
            this._chatService.cancelCurrentRequestForSession(request.session.sessionId);
            this._editor.setSelection(this._session.wholeRange.value);
            let massagedInput = input;
            const withoutSubCommandLeader = slashCommandLike.text.slice(1);
            for (const agent of this._chatAgentService.getActivatedAgents()) {
                if (agent.locations.includes(ChatAgentLocation.Panel)) {
                    const commands = agent.slashCommands;
                    if (commands.find((command) => withoutSubCommandLeader.startsWith(command.name))) {
                        massagedInput = `${chatAgentLeader}${agent.name} ${slashCommandLike.text}`;
                        break;
                    }
                }
            }
            await this._instaService.invokeFunction(sendRequest, massagedInput);
            return "DONE" ;
        }
        this._session.addInput(( new SessionPrompt(input, this._nextAttempt, this._nextWithIntentDetection)));
        this._nextAttempt = 0;
        this._nextWithIntentDetection = true;
        return "SHOW_REQUEST" ;
    }
    async ["SHOW_REQUEST" ](options) {
        assertType(this._session);
        assertType(this._session.lastInput);
        const request = tail(this._session.chatModel.getRequests());
        assertType(request);
        assertType(request.response);
        this._showWidget(false);
        this._zone.value.widget.value = request.message.text;
        this._zone.value.widget.selectAll(false);
        this._zone.value.widget.updateInfo('');
        const { response } = request;
        const responsePromise = ( new DeferredPromise());
        const store = ( new DisposableStore());
        const progressiveEditsCts = store.add(( new CancellationTokenSource()));
        const progressiveEditsAvgDuration = ( new MovingAverage());
        const progressiveEditsClock = StopWatch.create();
        const progressiveEditsQueue = ( new Queue());
        let lastLength = 0;
        let message = 0 ;
        store.add(Event.once(this._messages.event)(m => {
            this._log('state=_makeRequest) message received', m);
            this._chatService.cancelCurrentRequestForSession(request.session.sessionId);
            message = m;
        }));
        store.add(this._zone.value.widget.chatWidget.inputEditor.onDidChangeModelContent(() => {
            this._chatService.cancelCurrentRequestForSession(request.session.sessionId);
        }));
        store.add(response.onDidChange(() => {
            if (response.isCanceled) {
                progressiveEditsCts.cancel();
                responsePromise.complete();
                return;
            }
            if (response.isComplete) {
                responsePromise.complete();
                return;
            }
            const edits = response.edits.get(this._session.textModelN.uri) ?? [];
            const newEdits = edits.slice(lastLength);
            if (newEdits.length === 0) {
                return;
            }
            lastLength = edits.length;
            progressiveEditsAvgDuration.update(progressiveEditsClock.elapsed());
            progressiveEditsClock.reset();
            progressiveEditsQueue.queue(async () => {
                const startThen = this._session.wholeRange.value.getStartPosition();
                await this._makeChanges(newEdits, { duration: progressiveEditsAvgDuration.value, token: progressiveEditsCts.token });
                const startNow = this._session.wholeRange.value.getStartPosition();
                if (!startNow.equals(startThen) || !this._zone.value.position?.equals(startNow)) {
                    this._showWidget(false, startNow.delta(-1));
                }
            });
        }));
        await responsePromise.p;
        await progressiveEditsQueue.whenIdle();
        store.dispose();
        const diff = await this._editorWorkerService.computeDiff(this._session.textModel0.uri, this._session.textModelN.uri, { computeMoves: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, ignoreTrimWhitespace: false }, 'advanced');
        this._session.wholeRange.fixup(diff?.changes ?? []);
        await this._session.hunkData.recompute();
        this._zone.value.widget.updateToolbar(true);
        if (message & 2 ) {
            return "CANCEL" ;
        }
        else if (message & 4 ) {
            return "PAUSE" ;
        }
        else if (message & 1 ) {
            return "DONE" ;
        }
        else {
            return "APPLY_RESPONSE" ;
        }
    }
    async ["APPLY_RESPONSE" ]() {
        assertType(this._session);
        assertType(this._strategy);
        const { response } = this._session.lastExchange;
        if (response instanceof ReplyResponse && response.workspaceEdit) {
            this._bulkEditService.apply(response.workspaceEdit, { showPreview: true });
            return "CANCEL" ;
        }
        return "SHOW_RESPONSE" ;
    }
    async ["SHOW_RESPONSE" ]() {
        assertType(this._session);
        assertType(this._strategy);
        const { response } = this._session.lastExchange;
        let responseTypes;
        for (const { response } of this._session.exchanges) {
            const thisType = response instanceof ReplyResponse
                ? response.responseType
                : undefined;
            if (responseTypes === undefined) {
                responseTypes = thisType;
            }
            else if (responseTypes !== thisType) {
                responseTypes = "mixed" ;
                break;
            }
        }
        this._ctxResponseTypes.set(responseTypes);
        this._ctxDidEdit.set(this._session.hasChangedText);
        let newPosition;
        if (response instanceof EmptyResponse) {
            const status = ( localizeWithPath(
                'vs/workbench/contrib/inlineChat/browser/inlineChatController',
                'empty',
                "No results, please refine your input and try again"
            ));
            this._zone.value.widget.updateStatus(status, { classes: ['warn'] });
            return "WAIT_FOR_INPUT" ;
        }
        else if (response instanceof ErrorResponse) {
            if (!response.isCancellation) {
                this._zone.value.widget.updateStatus(response.message, { classes: ['error'] });
            }
        }
        else if (response instanceof ReplyResponse) {
            this._zone.value.widget.updateStatus('');
            this._zone.value.widget.updateToolbar(true);
            newPosition = await this._strategy.renderChanges(response);
            if (this._session.provider.provideFollowups) {
                const followupCts = ( new CancellationTokenSource());
                const msgListener = Event.once(this._messages.event)(() => {
                    followupCts.cancel();
                });
                const followupTask = this._session.provider.provideFollowups(this._session.session, response.raw, followupCts.token);
                this._log('followup request started', this._session.provider.extensionId, this._session.session, response.raw);
                raceCancellation(Promise.resolve(followupTask), followupCts.token).then(followupReply => {
                    if (followupReply && this._session) {
                        this._log('followup request received', this._session.provider.extensionId, this._session.session, followupReply);
                        this._zone.value.widget.updateFollowUps(followupReply, followup => {
                            if (followup.kind === 'reply') {
                                this.updateInput(followup.message);
                                this.acceptInput();
                            }
                            else {
                                this._commandService.executeCommand(followup.commandId, ...(followup.args ?? []));
                            }
                        });
                    }
                }).finally(() => {
                    msgListener.dispose();
                    followupCts.dispose();
                });
            }
        }
        this._showWidget(false, newPosition);
        return "WAIT_FOR_INPUT" ;
    }
    async ["PAUSE" ]() {
        this._resetWidget();
        this._strategy?.dispose?.();
        this._session = undefined;
    }
    async ["DONE" ]() {
        assertType(this._session);
        assertType(this._strategy);
        this._sessionStore.clear();
        try {
            await this._strategy.apply();
        }
        catch (err) {
            this._dialogService.error(( localizeWithPath(
                'vs/workbench/contrib/inlineChat/browser/inlineChatController',
                'err.apply',
                "Failed to apply changes.",
                toErrorMessage(err)
            )));
            this._log('FAILED to apply changes');
            this._log(err);
        }
        this._inlineChatSessionService.releaseSession(this._session);
        this._resetWidget();
        this._strategy?.dispose();
        this._strategy = undefined;
        this._session = undefined;
    }
    async ["CANCEL" ]() {
        if (this._session) {
            assertType(this._strategy);
            this._sessionStore.clear();
            const shouldStash = !this._session.isUnstashed && !!this._session.lastExchange && this._session.hunkData.size === this._session.hunkData.pending;
            let undoCancelEdits = [];
            try {
                undoCancelEdits = this._strategy.cancel();
            }
            catch (err) {
                this._dialogService.error(( localizeWithPath(
                    'vs/workbench/contrib/inlineChat/browser/inlineChatController',
                    'err.discard',
                    "Failed to discard changes.",
                    toErrorMessage(err)
                )));
                this._log('FAILED to discard changes');
                this._log(err);
            }
            this._stashedSession.clear();
            if (shouldStash) {
                this._stashedSession.value = this._inlineChatSessionService.stashSession(this._session, this._editor, undoCancelEdits);
            }
            else {
                this._inlineChatSessionService.releaseSession(this._session);
            }
        }
        this._resetWidget();
        this._strategy?.dispose();
        this._strategy = undefined;
        this._session = undefined;
    }
    _showWidget(initialRender = false, position) {
        assertType(this._editor.hasModel());
        let widgetPosition;
        if (position) {
            widgetPosition = position;
        }
        else if (this._zone.rawValue?.position) {
            if (this._zone.rawValue.position.lineNumber === 1) {
                widgetPosition = this._zone.rawValue.position.delta(-1);
            }
            else {
                widgetPosition = this._zone.rawValue.position;
            }
        }
        else {
            widgetPosition = this._editor.getSelection().getStartPosition().delta(-1);
        }
        if (this._session && !position && (this._session.hasChangedText || this._session.lastExchange)) {
            widgetPosition = this._session.wholeRange.value.getStartPosition().delta(-1);
        }
        if (this._zone.rawValue?.position) {
            this._zone.value.updatePositionAndHeight(widgetPosition);
        }
        else if (initialRender) {
            const selection = this._editor.getSelection();
            widgetPosition = selection.getStartPosition();
            this._input.value.show(widgetPosition);
        }
        else {
            this._input.value.hide();
            this._zone.value.show(widgetPosition);
            if (this._session && this._zone.value.widget.chatWidget.viewModel?.model !== this._session.chatModel) {
                this._zone.value.widget.setChatModel(this._session.chatModel);
            }
        }
        if (this._session && this._zone.rawValue) {
            this._zone.rawValue.updateBackgroundColor(widgetPosition, this._session.wholeRange.value);
        }
        this._ctxVisible.set(true);
        return widgetPosition;
    }
    _resetWidget() {
        this._sessionStore.clear();
        this._ctxVisible.reset();
        this._ctxDidEdit.reset();
        this._ctxUserDidEdit.reset();
        this._ctxLastFeedbackKind.reset();
        this._ctxSupportIssueReporting.reset();
        this._input.rawValue?.hide();
        this._zone.rawValue?.hide();
        if (this._editor.hasWidgetFocus()) {
            this._editor.focus();
        }
    }
    async _makeChanges(edits, opts) {
        assertType(this._session);
        assertType(this._strategy);
        const moreMinimalEdits = await this._editorWorkerService.computeMoreMinimalEdits(this._session.textModelN.uri, edits);
        this._log('edits from PROVIDER and after making them MORE MINIMAL', this._session.provider.extensionId, edits, moreMinimalEdits);
        if (moreMinimalEdits?.length === 0) {
            return;
        }
        const actualEdits = !opts && moreMinimalEdits ? moreMinimalEdits : edits;
        const editOperations = ( actualEdits.map(TextEdit.asEditOperation));
        const editsObserver = {
            start: () => this._session.hunkData.ignoreTextModelNChanges = true,
            stop: () => this._session.hunkData.ignoreTextModelNChanges = false,
        };
        this._inlineChatSavingService.markChanged(this._session);
        this._session.wholeRange.trackEdits(editOperations);
        if (opts) {
            await this._strategy.makeProgressiveChanges(editOperations, editsObserver, opts);
        }
        else {
            await this._strategy.makeChanges(editOperations, editsObserver);
        }
        this._ctxDidEdit.set(this._session.hasChangedText);
    }
    _updatePlaceholder() {
        this._zone.value.widget.placeholder = this._getPlaceholderText();
    }
    _getPlaceholderText() {
        return this._forcedPlaceholder ?? this._session?.session.placeholder ?? '';
    }
    showSaveHint() {
        const status = ( localizeWithPath(
            'vs/workbench/contrib/inlineChat/browser/inlineChatController',
            'savehint',
            "Accept or discard changes to continue saving"
        ));
        this._zone.value.widget.updateStatus(status, { classes: ['warn'] });
    }
    setPlaceholder(text) {
        this._forcedPlaceholder = text;
        this._updatePlaceholder();
    }
    resetPlaceholder() {
        this._forcedPlaceholder = undefined;
        this._updatePlaceholder();
    }
    acceptInput() {
        if (this._input.value.isVisible) {
            this._input.value.chatWidget.acceptInput();
        }
        else {
            this._zone.value.widget.chatWidget.acceptInput();
        }
    }
    updateInput(text, selectAll = true) {
        this._input.value.chatWidget.setInput(text);
        this._zone.value.widget.chatWidget.setInput(text);
        if (selectAll) {
            const newSelection = ( new Selection(1, 1, Number.MAX_SAFE_INTEGER, 1));
            this._input.value.chatWidget.inputEditor.setSelection(newSelection);
            this._zone.value.widget.chatWidget.inputEditor.setSelection(newSelection);
        }
    }
    getInput() {
        return this._input.value.isVisible
            ? this._input.value.value
            : this._zone.value.widget.value;
    }
    async rerun(opts) {
        if (this._session?.lastExchange && this._strategy) {
            const { lastExchange } = this._session;
            const request = tail(this._session.chatModel.getRequests());
            if (!request || !request.response?.isComplete) {
                return;
            }
            this._session.chatModel.removeRequest(request.id);
            if (lastExchange.response instanceof ReplyResponse) {
                try {
                    this._session.hunkData.ignoreTextModelNChanges = true;
                    await this._strategy.undoChanges(lastExchange.response.modelAltVersionId);
                }
                finally {
                    this._session.hunkData.ignoreTextModelNChanges = false;
                }
            }
            if (opts.retry) {
                this._nextAttempt = lastExchange.prompt.attempt + 1;
            }
            if (opts.withoutIntentDetection) {
                this._nextWithIntentDetection = false;
            }
            this._zone.value.widget.chatWidget.acceptInput(request.message.text);
        }
    }
    cancelCurrentRequest() {
        this._messages.fire(16  | 8 );
    }
    arrowOut(up) {
        if (this._zone.value.position && this._editor.hasModel()) {
            const { column } = this._editor.getPosition();
            const { lineNumber } = this._zone.value.position;
            const newLine = up ? lineNumber : lineNumber + 1;
            this._editor.setPosition({ lineNumber: newLine, column });
            this._editor.focus();
        }
    }
    focus() {
        this._zone.value.widget.focus();
    }
    hasFocus() {
        return this._zone.value.widget.hasFocus();
    }
    moveHunk(next) {
        this.focus();
        this._strategy?.move?.(next);
    }
    viewInChat() {
        if (this._session?.lastExchange?.response instanceof ReplyResponse) {
            this._instaService.invokeFunction(showMessageResponse, this._session.lastExchange.prompt.value, this._session.lastExchange.response.mdContent.value);
        }
    }
    toggleDiff() {
        this._strategy?.toggleDiff?.();
    }
    feedbackLast(kind) {
        if (this._session?.lastExchange && this._session.lastExchange.response instanceof ReplyResponse) {
            this._session.provider.handleInlineChatResponseFeedback?.(this._session.session, this._session.lastExchange.response.raw, kind);
            switch (kind) {
                case 1 :
                    this._ctxLastFeedbackKind.set('helpful');
                    break;
                case 0 :
                    this._ctxLastFeedbackKind.set('unhelpful');
                    break;
            }
            this._zone.value.widget.updateStatus('Thank you for your feedback!', { resetAfter: 1250 });
        }
    }
    createSnapshot() {
        if (this._session && !this._session.textModel0.equalsTextBuffer(this._session.textModelN.getTextBuffer())) {
            this._session.createSnapshot();
        }
    }
    acceptSession() {
        if (this._session?.lastExchange && this._session.lastExchange.response instanceof ReplyResponse) {
            this._session.provider.handleInlineChatResponseFeedback?.(this._session.session, this._session.lastExchange.response.raw, 3 );
        }
        this._messages.fire(1 );
    }
    acceptHunk() {
        return this._strategy?.acceptHunk();
    }
    discardHunk() {
        return this._strategy?.discardHunk();
    }
    async cancelSession() {
        let result;
        if (this._session) {
            const diff = await this._editorWorkerService.computeDiff(this._session.textModel0.uri, this._session.textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced');
            result = this._session.asChangedText(diff?.changes ?? []);
            if (this._session.lastExchange && this._session.lastExchange.response instanceof ReplyResponse) {
                this._session.provider.handleInlineChatResponseFeedback?.(this._session.session, this._session.lastExchange.response.raw, 2 );
            }
        }
        this._messages.fire(2 );
        return result;
    }
    finishExistingSession() {
        if (this._session) {
            if (this._session.editMode === "preview" ) {
                this._log('finishing existing session, using CANCEL', this._session.editMode);
                this.cancelSession();
            }
            else {
                this._log('finishing existing session, using APPLY', this._session.editMode);
                this.acceptSession();
            }
        }
    }
    unstashLastSession() {
        const result = this._stashedSession.value?.unstash();
        if (result) {
            this._inlineChatSavingService.markChanged(result);
        }
        return result;
    }
    joinCurrentRun() {
        return this._currentRun;
    }
};
InlineChatController = ( __decorate([
    ( __param(1, IInstantiationService)),
    ( __param(2, IInlineChatSessionService)),
    ( __param(3, IInlineChatSavingService)),
    ( __param(4, IEditorWorkerService)),
    ( __param(5, ILogService)),
    ( __param(6, IConfigurationService)),
    ( __param(7, IDialogService)),
    ( __param(8, IContextKeyService)),
    ( __param(9, IChatAgentService)),
    ( __param(10, IChatService)),
    ( __param(11, IBulkEditService)),
    ( __param(12, ICommandService)),
    ( __param(13, ILanguageFeaturesService)),
    ( __param(14, IChatWidgetService))
], InlineChatController));
async function showMessageResponse(accessor, query, response) {
    const chatService = accessor.get(IChatService);
    const chatAgentService = accessor.get(IChatAgentService);
    const agent = chatAgentService.getActivatedAgents().find(agent => agent.locations.includes(ChatAgentLocation.Panel) && agent.isDefault);
    if (!agent) {
        return;
    }
    const chatWidgetService = accessor.get(IChatWidgetService);
    const widget = await chatWidgetService.revealViewForProvider(agent.name);
    if (widget && widget.viewModel) {
        chatService.addCompleteRequest(widget.viewModel.sessionId, query, undefined, { message: response });
        widget.focusLastMessage();
    }
}
async function sendRequest(accessor, query) {
    const widgetService = accessor.get(IChatWidgetService);
    const chatAgentService = accessor.get(IChatAgentService);
    const agent = chatAgentService.getActivatedAgents().find(agent => agent.locations.includes(ChatAgentLocation.Panel) && agent.isDefault);
    if (!agent) {
        return;
    }
    const widget = await widgetService.revealViewForProvider(agent.name);
    if (!widget) {
        return;
    }
    widget.focusInput();
    widget.acceptInput(query);
}
export { InlineChatController, InlineChatRunOptions };
