import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { findLast } from '../../../../base/common/arraysFind.js';
import { timeout } from '../../../../base/common/async.js';
import { CancellationToken } from '../../../../base/common/cancellation.js';
import { Emitter } from '../../../../base/common/event.js';
import { Iterable } from '../../../../base/common/iterator.js';
import { toDisposable } from '../../../../base/common/lifecycle.js';
import { revive } from '../../../../base/common/marshalling.js';
import { observableValue } from '../../../../base/common/observableInternal/base.js';
import { equalsIgnoreCase } from '../../../../base/common/strings.js';
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.service.js';
import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
import { ILogService } from '../../../../platform/log/common/log.service.js';
import { IProductService } from '../../../../platform/product/common/productService.service.js';
import { asJson } from '../../../../platform/request/common/request.js';
import { IRequestService } from '../../../../platform/request/common/request.service.js';
import { StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
import { IStorageService } from '../../../../platform/storage/common/storage.service.js';
import { CONTEXT_CHAT_ENABLED } from './chatContextKeys.js';
var ChatAgentNameService_1;
var ChatAgentLocation;
( (function(ChatAgentLocation) {
    ChatAgentLocation["Panel"] = "panel";
    ChatAgentLocation["Terminal"] = "terminal";
    ChatAgentLocation["Notebook"] = "notebook";
    ChatAgentLocation["Editor"] = "editor";
})(ChatAgentLocation || (ChatAgentLocation = {})));
( (function(ChatAgentLocation) {
    function fromRaw(value) {
        switch (value) {
            case 'panel': return ChatAgentLocation.Panel;
            case 'terminal': return ChatAgentLocation.Terminal;
            case 'notebook': return ChatAgentLocation.Notebook;
            case 'editor': return ChatAgentLocation.Editor;
        }
        return ChatAgentLocation.Panel;
    }
    ChatAgentLocation.fromRaw = fromRaw;
})(ChatAgentLocation || (ChatAgentLocation = {})));
let ChatAgentService = class ChatAgentService {
    static { this.AGENT_LEADER = '@'; }
    constructor(contextKeyService) {
        this.contextKeyService = contextKeyService;
        this._agents = ( new Map());
        this._onDidChangeAgents = ( new Emitter());
        this.onDidChangeAgents = this._onDidChangeAgents.event;
        this._agentCompletionProviders = ( new Map());
        this._hasDefaultAgent = CONTEXT_CHAT_ENABLED.bindTo(this.contextKeyService);
    }
    registerAgent(id, data) {
        const existingAgent = this.getAgent(id);
        if (existingAgent) {
            throw ( new Error(`Agent already registered: ${JSON.stringify(id)}`));
        }
        const that = this;
        const commands = data.slashCommands;
        data = {
            ...data,
            get slashCommands() {
                return commands.filter(c => !c.when || that.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(c.when)));
            }
        };
        const entry = { data };
        this._agents.set(id, entry);
        return toDisposable(() => {
            this._agents.delete(id);
            this._onDidChangeAgents.fire(undefined);
        });
    }
    registerAgentImplementation(id, agentImpl) {
        const entry = this._agents.get(id);
        if (!entry) {
            throw ( new Error(`Unknown agent: ${JSON.stringify(id)}`));
        }
        if (entry.impl) {
            throw ( new Error(`Agent already has implementation: ${JSON.stringify(id)}`));
        }
        if (entry.data.isDefault) {
            this._hasDefaultAgent.set(true);
        }
        entry.impl = agentImpl;
        this._onDidChangeAgents.fire(( new MergedChatAgent(entry.data, agentImpl)));
        return toDisposable(() => {
            entry.impl = undefined;
            this._onDidChangeAgents.fire(undefined);
            if (entry.data.isDefault) {
                this._hasDefaultAgent.set(false);
            }
        });
    }
    registerDynamicAgent(data, agentImpl) {
        data.isDynamic = true;
        const agent = { data, impl: agentImpl };
        this._agents.set(data.id, agent);
        this._onDidChangeAgents.fire(( new MergedChatAgent(data, agentImpl)));
        return toDisposable(() => {
            this._agents.delete(data.id);
            this._onDidChangeAgents.fire(undefined);
        });
    }
    registerAgentCompletionProvider(id, provider) {
        this._agentCompletionProviders.set(id, provider);
        return {
            dispose: () => { this._agentCompletionProviders.delete(id); }
        };
    }
    async getAgentCompletionItems(id, query, token) {
        return (await this._agentCompletionProviders.get(id)?.(query, token)) ?? [];
    }
    updateAgent(id, updateMetadata) {
        const agent = this._agents.get(id);
        if (!agent?.impl) {
            throw ( new Error(`No activated agent with id ${JSON.stringify(id)} registered`));
        }
        agent.data.metadata = { ...agent.data.metadata, ...updateMetadata };
        this._onDidChangeAgents.fire(( new MergedChatAgent(agent.data, agent.impl)));
    }
    getDefaultAgent(location) {
        return findLast(this.getActivatedAgents(), a => !!a.isDefault && a.locations.includes(location));
    }
    getContributedDefaultAgent(location) {
        return this.getAgents().find(a => !!a.isDefault && a.locations.includes(location));
    }
    getSecondaryAgent() {
        return Iterable.find(( this._agents.values()), a => !!a.data.metadata.isSecondary)?.data;
    }
    getAgent(id) {
        if (!this._agentIsEnabled(id)) {
            return;
        }
        return this._agents.get(id)?.data;
    }
    _agentIsEnabled(id) {
        const entry = this._agents.get(id);
        return !entry?.data.when || this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(entry.data.when));
    }
    getAgentByFullyQualifiedId(id) {
        const agent = Iterable.find(( this._agents.values()), a => getFullyQualifiedId(a.data) === id)?.data;
        if (agent && !this._agentIsEnabled(agent.id)) {
            return;
        }
        return agent;
    }
    getAgents() {
        return ( Array.from(( this._agents.values()))
            .map(entry => entry.data))
            .filter(a => this._agentIsEnabled(a.id));
    }
    getActivatedAgents() {
        return ( Array.from(( this._agents.values()))
            .filter(a => !!a.impl)
            .filter(a => this._agentIsEnabled(a.data.id))
            .map(a => ( new MergedChatAgent(a.data, a.impl))));
    }
    getAgentsByName(name) {
        return this.getAgents().filter(a => a.name === name);
    }
    agentHasDupeName(id) {
        const agent = this.getAgent(id);
        if (!agent) {
            return false;
        }
        return this.getAgentsByName(agent.name)
            .filter(a => a.extensionId.value !== agent.extensionId.value).length > 0;
    }
    async invokeAgent(id, request, progress, history, token) {
        const data = this._agents.get(id);
        if (!data?.impl) {
            throw ( new Error(`No activated agent with id "${id}"`));
        }
        return await data.impl.invoke(request, progress, history, token);
    }
    async getFollowups(id, request, result, history, token) {
        const data = this._agents.get(id);
        if (!data?.impl) {
            throw ( new Error(`No activated agent with id "${id}"`));
        }
        if (!data.impl?.provideFollowups) {
            return [];
        }
        return data.impl.provideFollowups(request, result, history, token);
    }
};
ChatAgentService = ( __decorate([
    ( __param(0, IContextKeyService))
], ChatAgentService));
class MergedChatAgent {
    constructor(data, impl) {
        this.data = data;
        this.impl = impl;
    }
    get id() { return this.data.id; }
    get name() { return this.data.name ?? ''; }
    get fullName() { return this.data.fullName ?? ''; }
    get description() { return this.data.description ?? ''; }
    get extensionId() { return this.data.extensionId; }
    get extensionPublisherId() { return this.data.extensionPublisherId; }
    get extensionPublisherDisplayName() { return this.data.publisherDisplayName; }
    get extensionDisplayName() { return this.data.extensionDisplayName; }
    get isDefault() { return this.data.isDefault; }
    get metadata() { return this.data.metadata; }
    get slashCommands() { return this.data.slashCommands; }
    get defaultImplicitVariables() { return this.data.defaultImplicitVariables; }
    get locations() { return this.data.locations; }
    async invoke(request, progress, history, token) {
        return this.impl.invoke(request, progress, history, token);
    }
    async provideFollowups(request, result, history, token) {
        if (this.impl.provideFollowups) {
            return this.impl.provideFollowups(request, result, history, token);
        }
        return [];
    }
    provideWelcomeMessage(location, token) {
        if (this.impl.provideWelcomeMessage) {
            return this.impl.provideWelcomeMessage(location, token);
        }
        return undefined;
    }
    provideSampleQuestions(location, token) {
        if (this.impl.provideSampleQuestions) {
            return this.impl.provideSampleQuestions(location, token);
        }
        return undefined;
    }
}
let ChatAgentNameService = class ChatAgentNameService {
    static { ChatAgentNameService_1 = this; }
    static { this.StorageKey = 'chat.participantNameRegistry'; }
    constructor(productService, requestService, logService, storageService) {
        this.requestService = requestService;
        this.logService = logService;
        this.storageService = storageService;
        this.registry = observableValue(this, Object.create(null));
        this.disposed = false;
        if (!productService.chatParticipantRegistry) {
            return;
        }
        this.url = productService.chatParticipantRegistry;
        const raw = storageService.get(ChatAgentNameService_1.StorageKey, StorageScope.APPLICATION);
        try {
            this.registry.set(JSON.parse(raw ?? '{}'), undefined);
        }
        catch (err) {
            storageService.remove(ChatAgentNameService_1.StorageKey, StorageScope.APPLICATION);
        }
        this.refresh();
    }
    refresh() {
        if (this.disposed) {
            return;
        }
        this.update()
            .catch(err => this.logService.warn('Failed to fetch chat participant registry', err))
            .then(() => timeout(5 * 60 * 1000))
            .then(() => this.refresh());
    }
    async update() {
        const context = await this.requestService.request({ type: 'GET', url: this.url }, CancellationToken.None);
        if (context.res.statusCode !== 200) {
            throw ( new Error('Could not get extensions report.'));
        }
        const result = await asJson(context);
        if (!result || result.version !== 1) {
            throw ( new Error('Unexpected chat participant registry response.'));
        }
        const registry = result.restrictedChatParticipants;
        this.registry.set(registry, undefined);
        this.storageService.store(ChatAgentNameService_1.StorageKey, JSON.stringify(registry), StorageScope.APPLICATION, StorageTarget.MACHINE);
    }
    getAgentNameRestriction(chatAgentData) {
        const nameAllowed = this.checkAgentNameRestriction(chatAgentData.name, chatAgentData).get();
        const fullNameAllowed = !chatAgentData.fullName || this.checkAgentNameRestriction(chatAgentData.fullName.replace(/\s/g, ''), chatAgentData).get();
        return nameAllowed && fullNameAllowed;
    }
    checkAgentNameRestriction(name, chatAgentData) {
        const allowList = ( this.registry.map(registry => registry[name.toLowerCase()]));
        return ( allowList.map(allowList => {
            if (!allowList) {
                return true;
            }
            return ( allowList.some(
                id => equalsIgnoreCase(id, id.includes('.') ? chatAgentData.extensionId.value : chatAgentData.extensionPublisherId)
            ));
        }));
    }
    dispose() {
        this.disposed = true;
    }
};
ChatAgentNameService = ChatAgentNameService_1 = ( __decorate([
    ( __param(0, IProductService)),
    ( __param(1, IRequestService)),
    ( __param(2, ILogService)),
    ( __param(3, IStorageService))
], ChatAgentNameService));
function getFullyQualifiedId(chatAgentData) {
    return `${chatAgentData.extensionId.value}.${chatAgentData.id}`;
}
function reviveSerializedAgent(raw) {
    const agent = 'name' in raw ?
        raw :
        {
            ...raw,
            name: raw.id,
        };
    if (!('extensionPublisherId' in agent)) {
        agent.extensionPublisherId = agent.extensionPublisher ?? '';
    }
    if (!('extensionDisplayName' in agent)) {
        agent.extensionDisplayName = '';
    }
    if (!('extensionId' in agent)) {
        agent.extensionId = ( new ExtensionIdentifier(''));
    }
    return revive(agent);
}
export { ChatAgentLocation, ChatAgentNameService, ChatAgentService, MergedChatAgent, getFullyQualifiedId, reviveSerializedAgent };
