
import { encodeBase64 } from '../../../../../../base/common/buffer.js';
import '../../../../../../base/common/observableInternal/index.js';
import { ThemeIcon } from '../../../../../../base/common/themables.js';
import { localize } from '../../../../../../nls.js';
import { ToolConfirmKind, IChatToolInvocation } from '../../chatService/chatService.js';
import { isToolResultOutputDetails } from '../../tools/languageModelToolsService.js';
import { observableValue } from '../../../../../../base/common/observableInternal/observables/observableValue.js';

class ChatToolInvocation {
    get toolSpecificData() {
        return this._toolSpecificData;
    }
    set toolSpecificData(value) {
        this._toolSpecificData = value;
        this._toolSpecificDataKind.set(value?.kind, undefined);
    }
    get state() {
        return this._state;
    }
    static createStreaming(options) {
        return ( new ChatToolInvocation(
            undefined,
            options.toolData,
            options.toolCallId,
            options.subagentInvocationId,
            undefined,
            {
                startInStreaming: true
            },
            options.chatRequestId
        ));
    }
    static createCancelled(options, parameters, reason, reasonMessage) {
        return ( new ChatToolInvocation(
            undefined,
            options.toolData,
            options.toolCallId,
            options.subagentInvocationId,
            parameters,
            {
                startInCancelled: true,
                cancelReason: reason,
                cancelReasonMessage: reasonMessage
            },
            options.chatRequestId
        ));
    }
    constructor(
        preparedInvocation,
        toolData,
        toolCallId,
        subAgentInvocationId,
        parameters,
        startOptions = {},
        chatRequestId
    ) {
        this.toolCallId = toolCallId;
        this.kind = "toolInvocation";
        this.isAttachedToThinking = false;
        this._toolSpecificDataKind = observableValue(this, undefined);
        this.toolSpecificDataKind = this._toolSpecificDataKind;
        this._progress = observableValue(this, {
            progress: 0
        });
        this._partialInput = observableValue(this, undefined);
        this._streamingMessage = observableValue(this, undefined);
        let defaultMessage = "";
        if (startOptions.startInStreaming) {
            defaultMessage = toolData.displayName;
        } else if (startOptions.startInCancelled) {
            defaultMessage = startOptions.cancelReasonMessage ?? ( localize(7975, "Tool \"{0}\" was denied", toolData.displayName));
        }
        this.invocationMessage = preparedInvocation?.invocationMessage ?? defaultMessage;
        this.pastTenseMessage = preparedInvocation?.pastTenseMessage;
        this.originMessage = preparedInvocation?.originMessage;
        this.confirmationMessages = preparedInvocation?.confirmationMessages;
        this.presentation = preparedInvocation?.presentation;
        this.toolSpecificData = preparedInvocation?.toolSpecificData;
        this.toolId = toolData.id;
        this.icon = preparedInvocation?.icon ?? (toolData.icon && ThemeIcon.isThemeIcon(toolData.icon) ? toolData.icon : undefined);
        this.source = toolData.source;
        this.subAgentInvocationId = subAgentInvocationId;
        this.parameters = parameters;
        this.chatRequestId = chatRequestId;
        if (startOptions.startInCancelled) {
            this._state = observableValue(this, {
                type: IChatToolInvocation.StateKind.Cancelled,
                reason: startOptions.cancelReason ?? ToolConfirmKind.Denied,
                reasonMessage: startOptions.cancelReasonMessage,
                parameters: this.parameters,
                confirmationMessages: this.confirmationMessages
            });
        } else if (startOptions.startInStreaming) {
            this._state = observableValue(this, {
                type: IChatToolInvocation.StateKind.Streaming,
                partialInput: this._partialInput,
                streamingMessage: this._streamingMessage
            });
        } else if (!this.confirmationMessages?.title) {
            this._state = observableValue(this, {
                type: IChatToolInvocation.StateKind.Executing,
                confirmed: {
                    type: ToolConfirmKind.ConfirmationNotNeeded,
                    reason: this.confirmationMessages?.confirmationNotNeededReason
                },
                progress: this._progress,
                parameters: this.parameters,
                confirmationMessages: this.confirmationMessages
            });
        } else {
            this._state = observableValue(this, {
                type: IChatToolInvocation.StateKind.WaitingForConfirmation,
                parameters: this.parameters,
                confirmationMessages: this.confirmationMessages,
                confirm: reason => {
                    if (reason.type === ToolConfirmKind.Denied || reason.type === ToolConfirmKind.Skipped) {
                        this._state.set({
                            type: IChatToolInvocation.StateKind.Cancelled,
                            reason: reason.type,
                            parameters: this.parameters,
                            confirmationMessages: this.confirmationMessages
                        }, undefined);
                    } else {
                        this._state.set({
                            type: IChatToolInvocation.StateKind.Executing,
                            confirmed: reason,
                            progress: this._progress,
                            parameters: this.parameters,
                            confirmationMessages: this.confirmationMessages
                        }, undefined);
                    }
                }
            });
        }
    }
    updatePartialInput(input) {
        if (this._state.get().type !== IChatToolInvocation.StateKind.Streaming) {
            return;
        }
        this._partialInput.set(input, undefined);
    }
    updateStreamingMessage(message) {
        const state = this._state.get();
        if (state.type !== IChatToolInvocation.StateKind.Streaming) {
            return;
        }
        this._streamingMessage.set(message, undefined);
    }
    notifyToolSpecificDataChanged() {
        const current = this._state.get();
        this._state.set({
            ...current
        }, undefined);
    }
    cancelFromStreaming(reason, reasonMessage) {
        const currentState = this._state.get();
        if (currentState.type !== IChatToolInvocation.StateKind.Streaming) {
            return false;
        }
        this._state.set({
            type: IChatToolInvocation.StateKind.Cancelled,
            reason: reason,
            reasonMessage: reasonMessage,
            parameters: this.parameters,
            confirmationMessages: this.confirmationMessages
        }, undefined);
        return true;
    }
    transitionFromStreaming(preparedInvocation, parameters, autoConfirmed) {
        const currentState = this._state.get();
        if (currentState.type !== IChatToolInvocation.StateKind.Streaming) {
            return;
        }
        const lastStreamingMessage = this._streamingMessage.get();
        if (lastStreamingMessage && !preparedInvocation?.invocationMessage) {
            this.invocationMessage = lastStreamingMessage;
        }
        this.parameters = parameters;
        if (preparedInvocation) {
            if (preparedInvocation.invocationMessage) {
                this.invocationMessage = preparedInvocation.invocationMessage;
            }
            this.pastTenseMessage = preparedInvocation.pastTenseMessage;
            this.confirmationMessages = preparedInvocation.confirmationMessages;
            this.presentation = preparedInvocation.presentation;
            this.toolSpecificData = preparedInvocation.toolSpecificData;
        }
        const confirm = reason => {
            if (reason.type === ToolConfirmKind.Denied || reason.type === ToolConfirmKind.Skipped) {
                this._state.set({
                    type: IChatToolInvocation.StateKind.Cancelled,
                    reason: reason.type,
                    parameters: this.parameters,
                    confirmationMessages: this.confirmationMessages
                }, undefined);
            } else {
                this._state.set({
                    type: IChatToolInvocation.StateKind.Executing,
                    confirmed: reason,
                    progress: this._progress,
                    parameters: this.parameters,
                    confirmationMessages: this.confirmationMessages
                }, undefined);
            }
        };
        if (autoConfirmed) {
            confirm(autoConfirmed);
        } else if (!this.confirmationMessages?.title) {
            this._state.set({
                type: IChatToolInvocation.StateKind.Executing,
                confirmed: {
                    type: ToolConfirmKind.ConfirmationNotNeeded,
                    reason: this.confirmationMessages?.confirmationNotNeededReason
                },
                progress: this._progress,
                parameters: this.parameters,
                confirmationMessages: this.confirmationMessages
            }, undefined);
        } else {
            this._state.set({
                type: IChatToolInvocation.StateKind.WaitingForConfirmation,
                parameters: this.parameters,
                confirmationMessages: this.confirmationMessages,
                confirm
            }, undefined);
        }
    }
    _setCompleted(result, postConfirmed) {
        if (postConfirmed && (postConfirmed.type === ToolConfirmKind.Denied || postConfirmed.type === ToolConfirmKind.Skipped)) {
            this._state.set({
                type: IChatToolInvocation.StateKind.Cancelled,
                reason: postConfirmed.type,
                parameters: this.parameters,
                confirmationMessages: this.confirmationMessages
            }, undefined);
            return;
        }
        this._state.set({
            type: IChatToolInvocation.StateKind.Completed,
            confirmed: IChatToolInvocation.executionConfirmedOrDenied(this) || {
                type: ToolConfirmKind.ConfirmationNotNeeded
            },
            resultDetails: result?.toolResultDetails,
            postConfirmed,
            contentForModel: result?.content || [],
            parameters: this.parameters,
            confirmationMessages: this.confirmationMessages
        }, undefined);
    }
    async didExecuteTool(result, final, checkIfResultAutoApproved) {
        if (result?.toolResultMessage) {
            this.pastTenseMessage = result.toolResultMessage;
        } else if (this._progress.get().message) {
            this.pastTenseMessage = this._progress.get().message;
        }
        if (this.confirmationMessages?.confirmResults && !result?.toolResultError && result?.confirmResults !== false && !final) {
            const autoApproved = await checkIfResultAutoApproved?.();
            if (autoApproved) {
                this._setCompleted(result, autoApproved);
            } else {
                this._state.set({
                    type: IChatToolInvocation.StateKind.WaitingForPostApproval,
                    confirmed: IChatToolInvocation.executionConfirmedOrDenied(this) || {
                        type: ToolConfirmKind.ConfirmationNotNeeded
                    },
                    resultDetails: result?.toolResultDetails,
                    contentForModel: result?.content || [],
                    confirm: reason => this._setCompleted(result, reason),
                    parameters: this.parameters,
                    confirmationMessages: this.confirmationMessages
                }, undefined);
            }
        } else {
            this._setCompleted(result);
        }
        return this._state.get();
    }
    acceptProgress(step) {
        const prev = this._progress.get();
        this._progress.set({
            progress: step.progress || prev.progress || 0,
            message: step.message
        }, undefined);
    }
    toJSON() {
        const waitingForPostApproval = this.state.get().type === IChatToolInvocation.StateKind.WaitingForPostApproval;
        const details = waitingForPostApproval ? undefined : IChatToolInvocation.resultDetails(this);
        return {
            kind: "toolInvocationSerialized",
            presentation: this.presentation,
            invocationMessage: this.invocationMessage,
            pastTenseMessage: this.pastTenseMessage,
            originMessage: this.originMessage,
            isConfirmed: waitingForPostApproval ? {
                type: ToolConfirmKind.Skipped
            } : IChatToolInvocation.executionConfirmedOrDenied(this),
            isComplete: true,
            source: this.source,
            resultDetails: isToolResultOutputDetails(details) ? {
                output: {
                    type: "data",
                    mimeType: details.output.mimeType,
                    base64Data: encodeBase64(details.output.value)
                }
            } : details,
            toolSpecificData: this.toolSpecificData,
            toolCallId: this.toolCallId,
            toolId: this.toolId,
            subAgentInvocationId: this.subAgentInvocationId,
            generatedTitle: this.generatedTitle
        };
    }
}

export { ChatToolInvocation };
