import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { asArray, firstOrDefault } from '../../../../base/common/arrays.js';
import { DeferredPromise } from '../../../../base/common/async.js';
import { Emitter } from '../../../../base/common/event.js';
import { isMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { ResourceMap } from '../../../../base/common/map.js';
import { revive } from '../../../../base/common/marshalling.js';
import { basename } from '../../../../base/common/resources.js';
import { URI, isUriComponents } from '../../../../base/common/uri.js';
import { generateUuid } from '../../../../base/common/uuid.js';
import { OffsetRange } from '../../../../editor/common/core/offsetRange.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { ILogService } from '../../../../platform/log/common/log.service.js';
import { ChatAgentLocation } from './chatAgents.js';
import { IChatAgentService } from './chatAgents.service.js';
import { getPromptText, reviveParsedChatRequest, ChatRequestTextPart } from './chatParserTypes.js';
import { isIUsedContext } from './chatService.js';
var ChatModel_1, ChatWelcomeMessageModel_1;
class ChatRequestModel {
    static { this.nextId = 0; }
    get id() {
        return this._id;
    }
    get username() {
        return this.session.requesterUsername;
    }
    get avatarIconUri() {
        return this.session.requesterAvatarIconUri;
    }
    get variableData() {
        return this._variableData;
    }
    set variableData(v) {
        this._variableData = v;
    }
    constructor(session, message, _variableData) {
        this.session = session;
        this.message = message;
        this._variableData = _variableData;
        this._id = 'request_' + ChatRequestModel.nextId++;
    }
}
class Response {
    get onDidChangeValue() {
        return this._onDidChangeValue.event;
    }
    get value() {
        return this._responseParts;
    }
    constructor(value) {
        this._onDidChangeValue = ( new Emitter());
        this._responseParts = ( asArray(value).map((v) => (isMarkdownString(v) ?
            { content: v, kind: 'markdownContent' } :
            'kind' in v ? v : { kind: 'treeData', treeData: v })));
        this._updateRepr(true);
    }
    asString() {
        return this._responseRepr;
    }
    clear() {
        this._responseParts = [];
        this._updateRepr(true);
    }
    updateContent(progress, quiet) {
        if (progress.kind === 'content' || progress.kind === 'markdownContent') {
            const responsePartLength = this._responseParts.length - 1;
            const lastResponsePart = this._responseParts[responsePartLength];
            if (!lastResponsePart || lastResponsePart.kind !== 'markdownContent') {
                if (progress.kind === 'content') {
                    this._responseParts.push({ content: ( new MarkdownString(progress.content)), kind: 'markdownContent' });
                }
                else {
                    this._responseParts.push(progress);
                }
            }
            else if (progress.kind === 'markdownContent') {
                const lastPartEnabledCommands = typeof lastResponsePart.content.isTrusted === 'object' ?
                    lastResponsePart.content.isTrusted.enabledCommands :
                    [];
                const thisPartEnabledCommands = typeof progress.content.isTrusted === 'object' ?
                    progress.content.isTrusted.enabledCommands :
                    [];
                const enabledCommands = [...lastPartEnabledCommands, ...thisPartEnabledCommands];
                this._responseParts[responsePartLength] = { content: ( new MarkdownString(
                    lastResponsePart.content.value + progress.content.value,
                    { isTrusted: { enabledCommands } }
                )), kind: 'markdownContent' };
            }
            else {
                this._responseParts[responsePartLength] = { content: ( new MarkdownString(
                    lastResponsePart.content.value + progress.content,
                    lastResponsePart.content
                )), kind: 'markdownContent' };
            }
            this._updateRepr(quiet);
        }
        else {
            this._responseParts.push(progress);
            this._updateRepr(quiet);
        }
    }
    _updateRepr(quiet) {
        this._responseRepr = ( this._responseParts.map(part => {
            if (part.kind === 'treeData') {
                return '';
            }
            else if (part.kind === 'inlineReference') {
                return basename('uri' in part.inlineReference ? part.inlineReference.uri : part.inlineReference);
            }
            else if (part.kind === 'command') {
                return part.command.title;
            }
            else {
                return part.content.value;
            }
        })).join('\n\n');
        if (!quiet) {
            this._onDidChangeValue.fire();
        }
    }
}
class ChatResponseModel extends Disposable {
    static { this.nextId = 0; }
    get id() {
        return this._id;
    }
    get isComplete() {
        return this._isComplete;
    }
    get isCanceled() {
        return this._isCanceled;
    }
    get vote() {
        return this._vote;
    }
    get followups() {
        return this._followups;
    }
    get response() {
        return this._response;
    }
    get edits() {
        return this._edits;
    }
    get result() {
        return this._result;
    }
    get providerId() {
        return this.session.providerId;
    }
    get username() {
        return this.session.responderUsername;
    }
    get avatarIcon() {
        return this.session.responderAvatarIcon;
    }
    get agent() {
        return this._agent;
    }
    get slashCommand() {
        return this._slashCommand;
    }
    get agentOrSlashCommandDetected() {
        return this._agentOrSlashCommandDetected ?? false;
    }
    get usedContext() {
        return this._usedContext;
    }
    get contentReferences() {
        return this._contentReferences;
    }
    get progressMessages() {
        return this._progressMessages;
    }
    get isStale() {
        return this._isStale;
    }
    constructor(_response, session, _agent, _slashCommand, requestId, _isComplete = false, _isCanceled = false, _vote, _result, followups) {
        super();
        this.session = session;
        this._agent = _agent;
        this._slashCommand = _slashCommand;
        this.requestId = requestId;
        this._isComplete = _isComplete;
        this._isCanceled = _isCanceled;
        this._vote = _vote;
        this._result = _result;
        this._onDidChange = this._register(( new Emitter()));
        this.onDidChange = this._onDidChange.event;
        this._contentReferences = [];
        this._progressMessages = [];
        this._isStale = false;
        this._isStale = Array.isArray(_response) && (_response.length !== 0 || isMarkdownString(_response) && _response.value.length !== 0);
        this._followups = followups ? [...followups] : undefined;
        this._response = ( new Response(_response));
        this._edits = ( new ResourceMap());
        this._register(this._response.onDidChangeValue(() => this._onDidChange.fire()));
        this._id = 'response_' + ChatResponseModel.nextId++;
    }
    updateContent(responsePart, quiet) {
        this._response.updateContent(responsePart, quiet);
    }
    updateTextEdits(uri, edits) {
        const array = this._edits.get(uri);
        if (!array) {
            this._edits.set(uri, edits);
        }
        else {
            array.push(...edits);
        }
        this._onDidChange.fire();
    }
    applyReference(progress) {
        if (progress.kind === 'usedContext') {
            this._usedContext = progress;
        }
        else if (progress.kind === 'reference') {
            this._contentReferences.push(progress);
            this._onDidChange.fire();
        }
    }
    setAgent(agent, slashCommand) {
        this._agent = agent;
        this._slashCommand = slashCommand;
        this._agentOrSlashCommandDetected = true;
        this._onDidChange.fire();
    }
    setResult(result) {
        this._result = result;
        this._onDidChange.fire();
    }
    complete() {
        if (this._result?.errorDetails?.responseIsRedacted) {
            this._response.clear();
        }
        this._isComplete = true;
        this._onDidChange.fire();
    }
    cancel() {
        this._isComplete = true;
        this._isCanceled = true;
        this._onDidChange.fire();
    }
    setFollowups(followups) {
        this._followups = followups;
        this._onDidChange.fire();
    }
    setVote(vote) {
        this._vote = vote;
        this._onDidChange.fire();
    }
}
function isExportableSessionData(obj) {
    const data = obj;
    return typeof data === 'object' &&
        typeof data.providerId === 'string' &&
        typeof data.requesterUsername === 'string';
}
function isSerializableSessionData(obj) {
    const data = obj;
    return isExportableSessionData(obj) &&
        typeof data.creationDate === 'number' &&
        typeof data.sessionId === 'string' &&
        obj.requests.every((request) => !request.usedContext  || isIUsedContext(request.usedContext));
}
var ChatModelInitState;
( (function(ChatModelInitState) {
    ChatModelInitState[ChatModelInitState["Created"] = 0] = "Created";
    ChatModelInitState[ChatModelInitState["Initializing"] = 1] = "Initializing";
    ChatModelInitState[ChatModelInitState["Initialized"] = 2] = "Initialized";
})(ChatModelInitState || (ChatModelInitState = {})));
let ChatModel = ChatModel_1 = class ChatModel extends Disposable {
    static getDefaultTitle(requests) {
        const firstRequestMessage = firstOrDefault(requests)?.message ?? '';
        const message = typeof firstRequestMessage === 'string' ?
            firstRequestMessage :
            firstRequestMessage.text;
        return message.split('\n')[0].substring(0, 50);
    }
    get session() {
        return this._session;
    }
    get welcomeMessage() {
        return this._welcomeMessage;
    }
    get sessionId() {
        return this._sessionId;
    }
    get requestInProgress() {
        const lastRequest = this._requests[this._requests.length - 1];
        return !!lastRequest && !!lastRequest.response && !lastRequest.response.isComplete;
    }
    get creationDate() {
        return this._creationDate;
    }
    get _defaultAgent() {
        return this.chatAgentService.getDefaultAgent(ChatAgentLocation.Panel);
    }
    get requesterUsername() {
        return (this._defaultAgent ?
            this._defaultAgent.metadata.requester?.name :
            this.initialData?.requesterUsername) ?? '';
    }
    get responderUsername() {
        return (this._defaultAgent ?
            this._defaultAgent.metadata.fullName :
            this.initialData?.responderUsername) ?? '';
    }
    get requesterAvatarIconUri() {
        return this._defaultAgent ?
            this._defaultAgent.metadata.requester?.icon :
            this._initialRequesterAvatarIconUri;
    }
    get responderAvatarIcon() {
        return this._defaultAgent ?
            this._defaultAgent?.metadata.themeIcon :
            this._initialResponderAvatarIconUri;
    }
    get initState() {
        return this._initState;
    }
    get isImported() {
        return this._isImported;
    }
    get title() {
        return ChatModel_1.getDefaultTitle(this._requests);
    }
    constructor(providerId, initialData, logService, chatAgentService, instantiationService) {
        super();
        this.providerId = providerId;
        this.initialData = initialData;
        this.logService = logService;
        this.chatAgentService = chatAgentService;
        this.instantiationService = instantiationService;
        this._onDidDispose = this._register(( new Emitter()));
        this.onDidDispose = this._onDidDispose.event;
        this._onDidChange = this._register(( new Emitter()));
        this.onDidChange = this._onDidChange.event;
        this._initState = ChatModelInitState.Created;
        this._isInitializedDeferred = ( new DeferredPromise());
        this._isImported = false;
        this._isImported = (!!initialData && !isSerializableSessionData(initialData)) || (initialData?.isImported ?? false);
        this._sessionId = (isSerializableSessionData(initialData) && initialData.sessionId) || generateUuid();
        this._requests = initialData ? this._deserialize(initialData) : [];
        this._creationDate = (isSerializableSessionData(initialData) && initialData.creationDate) || Date.now();
        this._initialRequesterAvatarIconUri = initialData?.requesterAvatarIconUri && URI.revive(initialData.requesterAvatarIconUri);
        this._initialResponderAvatarIconUri = isUriComponents(initialData?.responderAvatarIconUri) ? URI.revive(initialData.responderAvatarIconUri) : initialData?.responderAvatarIconUri;
    }
    _deserialize(obj) {
        const requests = obj.requests;
        if (!Array.isArray(requests)) {
            this.logService.error(`Ignoring malformed session data: ${JSON.stringify(obj)}`);
            return [];
        }
        if (obj.welcomeMessage) {
            const content = ( obj.welcomeMessage.map(item => typeof item === 'string' ? ( new MarkdownString(item)) : item));
            this._welcomeMessage = this.instantiationService.createInstance(ChatWelcomeMessageModel, content, []);
        }
        try {
            return ( requests.map((raw) => {
                const parsedRequest = typeof raw.message === 'string'
                    ? this.getParsedRequestFromString(raw.message)
                    : reviveParsedChatRequest(raw.message);
                const variableData = raw.variableData && Array.isArray(raw.variableData.variables)
                    ? raw.variableData :
                    { variables: [] };
                const request = ( new ChatRequestModel(this, parsedRequest, variableData));
                if (raw.response || raw.result || raw.responseErrorDetails) {
                    const agent = (raw.agent && 'metadata' in raw.agent) ?
                        this.reviveSerializedAgent(raw.agent) : undefined;
                    const result = 'responseErrorDetails' in raw ?
                        { errorDetails: raw.responseErrorDetails } : raw.result;
                    request.response = ( new ChatResponseModel(raw.response ?? [( new MarkdownString(raw.response))], this, agent, raw.slashCommand, request.id, true, raw.isCanceled, raw.vote, result, raw.followups));
                    if (raw.usedContext) {
                        request.response.applyReference(revive(raw.usedContext));
                    }
                    if (raw.contentReferences) {
                        raw.contentReferences.forEach(r => request.response.applyReference(revive(r)));
                    }
                }
                return request;
            }));
        }
        catch (error) {
            this.logService.error('Failed to parse chat data', error);
            return [];
        }
    }
    reviveSerializedAgent(raw) {
        const agent = 'name' in raw ?
            raw :
            {
                ...raw,
                name: raw.id,
            };
        return revive(agent);
    }
    getParsedRequestFromString(message) {
        const parts = [( new ChatRequestTextPart(( new OffsetRange(0, message.length)), { startColumn: 1, startLineNumber: 1, endColumn: 1, endLineNumber: 1 }, message))];
        return {
            text: message,
            parts
        };
    }
    startInitialize() {
        if (this.initState !== ChatModelInitState.Created) {
            throw ( new Error(
                `ChatModel is in the wrong state for startInitialize: ${ChatModelInitState[this.initState]}`
            ));
        }
        this._initState = ChatModelInitState.Initializing;
    }
    deinitialize() {
        this._session = undefined;
        this._initState = ChatModelInitState.Created;
        this._isInitializedDeferred = ( new DeferredPromise());
    }
    initialize(session, welcomeMessage) {
        if (this.initState !== ChatModelInitState.Initializing) {
            throw ( new Error(
                `ChatModel is in the wrong state for initialize: ${ChatModelInitState[this.initState]}`
            ));
        }
        this._initState = ChatModelInitState.Initialized;
        this._session = session;
        if (!this._welcomeMessage) {
            this._welcomeMessage = welcomeMessage;
        }
        this._isInitializedDeferred.complete();
        this._onDidChange.fire({ kind: 'initialize' });
    }
    setInitializationError(error) {
        if (this.initState !== ChatModelInitState.Initializing) {
            throw ( new Error(
                `ChatModel is in the wrong state for setInitializationError: ${ChatModelInitState[this.initState]}`
            ));
        }
        if (!this._isInitializedDeferred.isSettled) {
            this._isInitializedDeferred.error(error);
        }
    }
    waitForInitialization() {
        return this._isInitializedDeferred.p;
    }
    getRequests() {
        return this._requests;
    }
    addRequest(message, variableData, chatAgent, slashCommand) {
        if (!this._session) {
            throw ( new Error('addRequest: No session'));
        }
        const request = ( new ChatRequestModel(this, message, variableData));
        request.response = ( new ChatResponseModel([], this, chatAgent, slashCommand, request.id));
        this._requests.push(request);
        this._onDidChange.fire({ kind: 'addRequest', request });
        return request;
    }
    acceptResponseProgress(request, progress, quiet) {
        if (!this._session) {
            throw ( new Error('acceptResponseProgress: No session'));
        }
        if (!request.response) {
            request.response = ( new ChatResponseModel([], this, undefined, undefined, request.id));
        }
        if (request.response.isComplete) {
            throw ( new Error('acceptResponseProgress: Adding progress to a completed response'));
        }
        if (progress.kind === 'vulnerability') {
            request.response.updateContent({ kind: 'markdownVuln', content: { value: progress.content }, vulnerabilities: progress.vulnerabilities }, quiet);
        }
        else if (progress.kind === 'content' || progress.kind === 'markdownContent' || progress.kind === 'treeData' || progress.kind === 'inlineReference' || progress.kind === 'markdownVuln' || progress.kind === 'progressMessage' || progress.kind === 'command') {
            request.response.updateContent(progress, quiet);
        }
        else if (progress.kind === 'usedContext' || progress.kind === 'reference') {
            request.response.applyReference(progress);
        }
        else if (progress.kind === 'agentDetection') {
            const agent = this.chatAgentService.getAgent(progress.agentId);
            if (agent) {
                request.response.setAgent(agent, progress.command);
            }
        }
        else if (progress.kind === 'textEdit') {
            request.response.updateTextEdits(progress.uri, progress.edits);
        }
        else {
            this.logService.error(`Couldn't handle progress: ${JSON.stringify(progress)}`);
        }
    }
    removeRequest(id) {
        const index = this._requests.findIndex(request => request.id === id);
        const request = this._requests[index];
        if (index !== -1) {
            this._onDidChange.fire({ kind: 'removeRequest', requestId: request.id, responseId: request.response?.id });
            this._requests.splice(index, 1);
            request.response?.dispose();
        }
    }
    cancelRequest(request) {
        if (request.response) {
            request.response.cancel();
        }
    }
    setResponse(request, result) {
        if (!this._session) {
            throw ( new Error('completeResponse: No session'));
        }
        if (!request.response) {
            request.response = ( new ChatResponseModel([], this, undefined, undefined, request.id));
        }
        request.response.setResult(result);
    }
    completeResponse(request) {
        if (!request.response) {
            throw ( new Error('Call setResponse before completeResponse'));
        }
        request.response.complete();
    }
    setFollowups(request, followups) {
        if (!request.response) {
            return;
        }
        request.response.setFollowups(followups);
    }
    setResponseModel(request, response) {
        request.response = response;
        this._onDidChange.fire({ kind: 'addResponse', response });
    }
    toExport() {
        return {
            requesterUsername: this.requesterUsername,
            requesterAvatarIconUri: this.requesterAvatarIconUri,
            responderUsername: this.responderUsername,
            responderAvatarIconUri: this.responderAvatarIcon,
            welcomeMessage: this._welcomeMessage?.content.map(c => {
                if (Array.isArray(c)) {
                    return c;
                }
                else {
                    return c.value;
                }
            }),
            requests: ( this._requests.map((r) => {
                const message = {
                    ...r.message,
                    parts: ( r.message.parts.map(p => p && 'toJSON' in p ? p.toJSON() : p))
                };
                return {
                    message,
                    variableData: r.variableData,
                    response: r.response ?
                        ( r.response.response.value.map(item => {
                            if (item.kind === 'treeData') {
                                return item.treeData;
                            }
                            else if (item.kind === 'markdownContent') {
                                return item.content;
                            }
                            else {
                                return item;
                            }
                        }))
                        : undefined,
                    result: r.response?.result,
                    followups: r.response?.followups,
                    isCanceled: r.response?.isCanceled,
                    vote: r.response?.vote,
                    agent: r.response?.agent ?
                        { id: r.response.agent.id, name: r.response.agent.name, description: r.response.agent.description, extensionId: r.response.agent.extensionId, metadata: r.response.agent.metadata, slashCommands: [], locations: r.response.agent.locations, isDefault: r.response.agent.isDefault }
                        : undefined,
                    slashCommand: r.response?.slashCommand,
                    usedContext: r.response?.usedContext,
                    contentReferences: r.response?.contentReferences
                };
            })),
            providerId: this.providerId,
        };
    }
    toJSON() {
        return {
            ...this.toExport(),
            sessionId: this.sessionId,
            creationDate: this._creationDate,
            isImported: this._isImported
        };
    }
    dispose() {
        this._session?.dispose?.();
        this._requests.forEach(r => r.response?.dispose());
        this._onDidDispose.fire();
        super.dispose();
    }
};
ChatModel = ChatModel_1 = ( __decorate([
    ( __param(2, ILogService)),
    ( __param(3, IChatAgentService)),
    ( __param(4, IInstantiationService))
], ChatModel));
let ChatWelcomeMessageModel = class ChatWelcomeMessageModel {
    static { ChatWelcomeMessageModel_1 = this; }
    static { this.nextId = 0; }
    get id() {
        return this._id;
    }
    constructor(content, sampleQuestions, chatAgentService) {
        this.content = content;
        this.sampleQuestions = sampleQuestions;
        this.chatAgentService = chatAgentService;
        this._id = 'welcome_' + ChatWelcomeMessageModel_1.nextId++;
    }
    get username() {
        return this.chatAgentService.getDefaultAgent(ChatAgentLocation.Panel)?.metadata.fullName ?? '';
    }
    get avatarIcon() {
        return this.chatAgentService.getDefaultAgent(ChatAgentLocation.Panel)?.metadata.themeIcon;
    }
};
ChatWelcomeMessageModel = ChatWelcomeMessageModel_1 = ( __decorate([
    ( __param(2, IChatAgentService))
], ChatWelcomeMessageModel));
function getHistoryEntriesFromModel(model) {
    const history = [];
    for (const request of model.getRequests()) {
        if (!request.response) {
            continue;
        }
        const promptTextResult = getPromptText(request.message);
        const historyRequest = {
            sessionId: model.sessionId,
            requestId: request.id,
            agentId: request.response.agent?.id ?? '',
            message: promptTextResult.message,
            command: request.response.slashCommand?.name,
            variables: updateRanges(request.variableData, promptTextResult.diff),
            location: ChatAgentLocation.Panel
        };
        history.push({ request: historyRequest, response: request.response.response.value, result: request.response.result ?? {} });
    }
    return history;
}
function updateRanges(variableData, diff) {
    return {
        variables: ( variableData.variables.map(v => ({
            ...v,
            range: v.range && {
                start: v.range.start - diff,
                endExclusive: v.range.endExclusive - diff
            }
        })))
    };
}
export { ChatModel, ChatModelInitState, ChatRequestModel, ChatResponseModel, ChatWelcomeMessageModel, Response, getHistoryEntriesFromModel, isExportableSessionData, isSerializableSessionData, updateRanges };
