// *****************************************************************************
// Copyright (C) 2024 EclipseSource GmbH.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import '../../src/browser/style/index.css';

import { ContainerModule } from '@theia/core/shared/inversify';
import { ChatAgent, ChatAgentRecommendationService } from '@theia/ai-chat/lib/common';
import { Agent, AIVariableContribution, bindToolProvider } from '@theia/ai-core/lib/common';
import { ArchitectAgent } from './architect-agent';
import { CoderAgent } from './coder-agent';
import { SummarizeSessionCommandContribution } from './summarize-session-command-contribution';
import {
    FileContentFunction,
    FileDiagnosticProvider,
    FindFilesByPattern,
    GetWorkspaceDirectoryStructure,
    GetWorkspaceFileList,
    WorkspaceFunctionScope
} from './workspace-functions';
import { WorkspaceSearchProvider } from './workspace-search-provider';
import {
    FrontendApplicationContribution,
    WidgetFactory,
    bindViewContribution,
    RemoteConnectionProvider,
    ServiceConnectionProvider
} from '@theia/core/lib/browser';
import { TaskListProvider, TaskRunnerProvider } from './workspace-task-provider';
import {
    LaunchListProvider,
    LaunchRunnerProvider,
    LaunchStopProvider,
} from './workspace-launch-provider';
import { WorkspacePreferencesSchema } from '../common/workspace-preferences';
import {
    ClearFileChanges,
    GetProposedFileState,
    ReplaceContentInFileFunctionHelper,
    SuggestFileReplacements,
    SuggestFileReplacements_Simple,
    SimpleSuggestFileReplacements,
    SuggestFileContent,
    WriteFileContent,
    WriteFileReplacements,
    WriteFileReplacements_Simple,
    SimpleWriteFileReplacements,
    FileChangeSetTitleProvider,
    DefaultFileChangeSetTitleProvider,
    ReplaceContentInFileFunctionHelperV2
} from './file-changeset-functions';
import { OrchestratorChatAgent } from '../common/orchestrator-chat-agent';
import { UniversalChatAgent } from '../common/universal-chat-agent';
import { AppTesterChatAgent } from './app-tester-chat-agent';
import { GitHubChatAgent } from './github-chat-agent';
import { CommandChatAgent } from '../common/command-chat-agents';
import { ListChatContext, ResolveChatContext, AddFileToChatContext } from './context-functions';
import { AIAgentConfigurationWidget } from './ai-configuration/agent-configuration-widget';
import { AIConfigurationSelectionService } from './ai-configuration/ai-configuration-service';
import { AIAgentConfigurationViewContribution } from './ai-configuration/ai-configuration-view-contribution';
import { AIConfigurationContainerWidget } from './ai-configuration/ai-configuration-widget';
import { AIVariableConfigurationWidget } from './ai-configuration/variable-configuration-widget';
import { ContextFilesVariableContribution } from '../common/context-files-variable';
import { AIToolsConfigurationWidget } from './ai-configuration/tools-configuration-widget';
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { TemplatePreferenceContribution } from './template-preference-contribution';
import { ChatWelcomeMessageProvider } from '@theia/ai-chat-ui/lib/browser/chat-tree-view';
import { IdeChatWelcomeMessageProvider } from './ide-chat-welcome-message-provider';
import { ChatSessionsWelcomeMessageProvider } from './chat-sessions-welcome-message-provider';
import { ChatSessionItemActionContribution, DefaultChatSessionItemActionContribution } from './chat-session-item-action-contribution';
import { AiAllowAllModeChatBanner } from './ai-allow-all-mode-chat-banner';
import { ChatBannerProvider } from '@theia/ai-chat-ui/lib/browser/chat-banner-provider';
import { DefaultChatAgentRecommendationService } from './default-chat-agent-recommendation-service';
import { AITokenUsageConfigurationWidget } from './ai-configuration/token-usage-configuration-widget';
import { AISkillsConfigurationWidget } from './ai-configuration/skills-configuration-widget';
import { TaskContextSummaryVariableContribution } from './task-background-summary-variable';
import { GitHubRepoVariableContribution } from './github-repo-variable-contribution';
import { TaskContextFileStorageService } from './task-context-file-storage-service';
import { TaskContextStorageService } from '@theia/ai-chat/lib/browser/task-context-service';
import { bindRootContributionProvider, CommandContribution, PreferenceContribution } from '@theia/core';
import { AIPromptFragmentsConfigurationWidget } from './ai-configuration/prompt-fragments-configuration-widget';
import { BrowserAutomation, browserAutomationPath } from '../common/browser-automation-protocol';
import { GitHubRepoService, githubRepoServicePath } from '../common/github-repo-protocol';
import { CloseBrowserProvider, IsBrowserRunningProvider, LaunchBrowserProvider, QueryDomProvider } from './app-tester-chat-functions';
import { GetSkillFileContent } from './skill-file-functions';
import { ModelAliasesConfigurationWidget } from './ai-configuration/model-aliases-configuration-widget';
import { aiIdePreferenceSchema } from '../common/ai-ide-preferences';
import { AIActivationService } from '@theia/ai-core/lib/browser';
import { AIIdeActivationServiceImpl } from './ai-ide-activation-service';
import { AiConfigurationPreferences } from '../common/ai-configuration-preferences';
import { WorkspaceRestrictionContribution } from '@theia/workspace/lib/browser/workspace-trust-service';
import { AIWorkspaceRestrictionContribution } from './ai-workspace-restriction-contribution';

import { ProjectInfoAgent } from './project-info-agent';
import { CreateSkillAgent } from './create-skill-agent';
import { SuggestTerminalCommand } from './ai-terminal-functions';
import { TodoWriteTool } from './todo-tool';
import { TodoToolRenderer } from './todo-tool-renderer';
import { UserInteractionTool } from './user-interaction-tool';
import { UserInteractionToolRenderer } from './user-interaction-tool-renderer';
import { ChatResponsePartRenderer } from '@theia/ai-chat-ui/lib/browser/chat-response-part-renderer';
import { ContextFileValidationService } from '@theia/ai-chat/lib/browser/context-file-validation-service';
import { ContextFileValidationServiceImpl } from './context-file-validation-service-impl';
import { RememberCommandContribution } from './remember-command-contribution';
import { CreateTaskContextFunction, GetTaskContextFunction, EditTaskContextFunction, ListTaskContextsFunction, RewriteTaskContextFunction } from './task-context-functions';
import { FixGitHubTicketCommandContribution } from './implement-gh-ticket-command-contribution';
import { AnalyzesGhTicketCommandContribution } from './analyze-gh-ticket-command-contribution';
import { AddressGhReviewCommandContribution } from './address-pr-review-command-contribution';
import { AppTesterCapabilityContribution } from './apptester-capability-contribution';
import { GitHubCapabilityContribution } from './github-capability-contribution';
import { ShellExecutionCapabilityContribution } from './shell-execution-capability-contribution';
import { AgentModeConfirmationService, AgentModeConfirmationServiceImpl } from './agent-mode-confirmation-service';
import { ExploreAgent } from './explore-agent';
import { CodeReviewerAgent } from './code-reviewer-agent';
import { CodeReviewCapabilityContribution } from './code-review-capability-contribution';
import { PRReviewAgent } from './review/pr-review-agent';
import { PRReviewCapabilityContribution } from './review/pr-review-capability-contribution';
import { PerspectiveContribution } from '@theia/core/lib/browser/perspective-service';
import { AIFirstPerspectiveContribution } from './ai-first-perspective-contribution';
import { ChatSessionListService } from './chat-session-list-service';
import { AISessionsWidget } from './ai-sessions-widget';
import { AISessionsViewContribution } from './ai-sessions-view-contribution';

export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
    bind(PreferenceContribution).toConstantValue({ schema: aiIdePreferenceSchema });
    bind(PreferenceContribution).toConstantValue({ schema: WorkspacePreferencesSchema });

    bind(AgentModeConfirmationServiceImpl).toSelf().inSingletonScope();
    bind(AgentModeConfirmationService).toService(AgentModeConfirmationServiceImpl);

    bind(AIIdeActivationServiceImpl).toSelf().inSingletonScope();
    // rebinds the default implementation of '@theia/ai-core'
    rebind(AIActivationService).toService(AIIdeActivationServiceImpl);

    bind(AIWorkspaceRestrictionContribution).toSelf().inSingletonScope();
    bind(WorkspaceRestrictionContribution).toService(AIWorkspaceRestrictionContribution);

    bind(ArchitectAgent).toSelf().inSingletonScope();
    bind(Agent).toService(ArchitectAgent);
    bind(ChatAgent).toService(ArchitectAgent);

    bind(CoderAgent).toSelf().inSingletonScope();
    bind(Agent).toService(CoderAgent);
    bind(ChatAgent).toService(CoderAgent);

    bind(ProjectInfoAgent).toSelf().inSingletonScope();
    bind(Agent).toService(ProjectInfoAgent);
    bind(ChatAgent).toService(ProjectInfoAgent);

    bind(CreateSkillAgent).toSelf().inSingletonScope();
    bind(Agent).toService(CreateSkillAgent);
    bind(ChatAgent).toService(CreateSkillAgent);

    bind(OrchestratorChatAgent).toSelf().inSingletonScope();
    bind(Agent).toService(OrchestratorChatAgent);
    bind(ChatAgent).toService(OrchestratorChatAgent);

    bind(UniversalChatAgent).toSelf().inSingletonScope();
    bind(Agent).toService(UniversalChatAgent);
    bind(ChatAgent).toService(UniversalChatAgent);

    bind(AppTesterChatAgent).toSelf().inSingletonScope();
    bind(Agent).toService(AppTesterChatAgent);
    bind(ChatAgent).toService(AppTesterChatAgent);

    bind(GitHubChatAgent).toSelf().inSingletonScope();
    bind(Agent).toService(GitHubChatAgent);
    bind(ChatAgent).toService(GitHubChatAgent);
    bind(BrowserAutomation).toDynamicValue(ctx => {
        const provider = ctx.container.get<ServiceConnectionProvider>(RemoteConnectionProvider);
        return provider.createProxy<BrowserAutomation>(browserAutomationPath);
    }).inSingletonScope();

    bind(CommandChatAgent).toSelf().inSingletonScope();
    bind(Agent).toService(CommandChatAgent);
    bind(ChatAgent).toService(CommandChatAgent);

    bind(ExploreAgent).toSelf().inSingletonScope();
    bind(Agent).toService(ExploreAgent);
    bind(ChatAgent).toService(ExploreAgent);

    bind(CodeReviewerAgent).toSelf().inSingletonScope();
    bind(Agent).toService(CodeReviewerAgent);
    bind(ChatAgent).toService(CodeReviewerAgent);

    bind(PRReviewAgent).toSelf().inSingletonScope();
    bind(Agent).toService(PRReviewAgent);
    bind(ChatAgent).toService(PRReviewAgent);

    bind(ChatSessionListService).toSelf().inSingletonScope();

    bind(ChatWelcomeMessageProvider).to(IdeChatWelcomeMessageProvider).inSingletonScope();
    bind(ChatWelcomeMessageProvider).to(ChatSessionsWelcomeMessageProvider).inSingletonScope();
    bind(ChatBannerProvider).to(AiAllowAllModeChatBanner).inSingletonScope();
    bindRootContributionProvider(bind, ChatSessionItemActionContribution);
    bind(DefaultChatSessionItemActionContribution).toSelf().inSingletonScope();
    bind(ChatSessionItemActionContribution).toService(DefaultChatSessionItemActionContribution);
    bind(ChatAgentRecommendationService).to(DefaultChatAgentRecommendationService).inSingletonScope();

    bindToolProvider(GetWorkspaceFileList, bind);
    bindToolProvider(FileContentFunction, bind);
    bindToolProvider(GetWorkspaceDirectoryStructure, bind);
    bindToolProvider(FileDiagnosticProvider, bind);
    bindToolProvider(FindFilesByPattern, bind);
    bindToolProvider(GetSkillFileContent, bind);
    bind(WorkspaceFunctionScope).toSelf().inSingletonScope();
    bindToolProvider(WorkspaceSearchProvider, bind);

    bindToolProvider(SuggestFileContent, bind);
    bindToolProvider(WriteFileContent, bind);
    bindToolProvider(TaskListProvider, bind);
    bindToolProvider(TaskRunnerProvider, bind);
    bindToolProvider(LaunchListProvider, bind);
    bindToolProvider(LaunchRunnerProvider, bind);
    bindToolProvider(LaunchStopProvider, bind);
    bind(ReplaceContentInFileFunctionHelper).toSelf().inSingletonScope();
    bind(FileChangeSetTitleProvider).to(DefaultFileChangeSetTitleProvider).inSingletonScope();
    bind(ReplaceContentInFileFunctionHelperV2).toSelf().inSingletonScope();
    bindToolProvider(SuggestFileReplacements, bind);
    bindToolProvider(SuggestFileReplacements_Simple, bind);
    bindToolProvider(WriteFileReplacements, bind);
    bindToolProvider(WriteFileReplacements_Simple, bind);
    bindToolProvider(ListChatContext, bind);
    bindToolProvider(ResolveChatContext, bind);
    bind(AIConfigurationSelectionService).toSelf().inSingletonScope();
    bind(AIConfigurationContainerWidget).toSelf();
    bind(WidgetFactory)
        .toDynamicValue(ctx => ({
            id: AIConfigurationContainerWidget.ID,
            createWidget: () => ctx.container.get(AIConfigurationContainerWidget)
        }))
        .inSingletonScope();

    bindToolProvider(LaunchBrowserProvider, bind);
    bindToolProvider(CloseBrowserProvider, bind);
    bindToolProvider(IsBrowserRunningProvider, bind);
    bindToolProvider(QueryDomProvider, bind);

    bindViewContribution(bind, AIAgentConfigurationViewContribution);
    bind(TabBarToolbarContribution).toService(AIAgentConfigurationViewContribution);

    bind(AIVariableConfigurationWidget).toSelf();
    bind(WidgetFactory)
        .toDynamicValue(ctx => ({
            id: AIVariableConfigurationWidget.ID,
            createWidget: () => ctx.container.get(AIVariableConfigurationWidget)
        }))
        .inSingletonScope();

    bind(AIAgentConfigurationWidget).toSelf();
    bind(WidgetFactory)
        .toDynamicValue(ctx => ({
            id: AIAgentConfigurationWidget.ID,
            createWidget: () => ctx.container.get(AIAgentConfigurationWidget)
        }))
        .inSingletonScope();

    bind(ModelAliasesConfigurationWidget).toSelf();
    bind(WidgetFactory)
        .toDynamicValue(ctx => ({
            id: ModelAliasesConfigurationWidget.ID,
            createWidget: () => ctx.container.get(ModelAliasesConfigurationWidget)
        }))
        .inSingletonScope();

    bindToolProvider(SimpleSuggestFileReplacements, bind);
    bindToolProvider(SimpleWriteFileReplacements, bind);
    bindToolProvider(ClearFileChanges, bind);
    bindToolProvider(GetProposedFileState, bind);
    bindToolProvider(AddFileToChatContext, bind);

    bind(AIToolsConfigurationWidget).toSelf();
    bind(WidgetFactory)
        .toDynamicValue(ctx => ({
            id: AIToolsConfigurationWidget.ID,
            createWidget: () => ctx.container.get(AIToolsConfigurationWidget)
        }))
        .inSingletonScope();

    bind(AISkillsConfigurationWidget).toSelf();
    bind(WidgetFactory)
        .toDynamicValue(ctx => ({
            id: AISkillsConfigurationWidget.ID,
            createWidget: () => ctx.container.get(AISkillsConfigurationWidget)
        }))
        .inSingletonScope();

    bind(AIVariableContribution).to(ContextFilesVariableContribution).inSingletonScope();

    bind(PreferenceContribution).toConstantValue({ schema: AiConfigurationPreferences });

    bind(FrontendApplicationContribution).to(TemplatePreferenceContribution);

    // Register the token usage configuration widget
    bind(AITokenUsageConfigurationWidget).toSelf();
    bind(WidgetFactory)
        .toDynamicValue(ctx => ({
            id: AITokenUsageConfigurationWidget.ID,
            createWidget: () => ctx.container.get(AITokenUsageConfigurationWidget)
        }))
        .inSingletonScope();

    bind(TaskContextSummaryVariableContribution).toSelf().inSingletonScope();
    bind(AIVariableContribution).toService(TaskContextSummaryVariableContribution);

    bind(GitHubRepoService).toDynamicValue(ctx => {
        const provider = ctx.container.get<ServiceConnectionProvider>(RemoteConnectionProvider);
        return provider.createProxy<GitHubRepoService>(githubRepoServicePath);
    }).inSingletonScope();

    bind(GitHubRepoVariableContribution).toSelf().inSingletonScope();
    bind(AIVariableContribution).toService(GitHubRepoVariableContribution);
    bind(TaskContextFileStorageService).toSelf().inSingletonScope();
    rebind(TaskContextStorageService).toService(TaskContextFileStorageService);

    bind(CommandContribution).to(SummarizeSessionCommandContribution);
    bind(AIPromptFragmentsConfigurationWidget).toSelf();
    bind(WidgetFactory)
        .toDynamicValue(ctx => ({
            id: AIPromptFragmentsConfigurationWidget.ID,
            createWidget: () => ctx.container.get(AIPromptFragmentsConfigurationWidget)
        }))
        .inSingletonScope();

    bindToolProvider(SuggestTerminalCommand, bind);

    // Task context functions for Architect planning mode
    bindToolProvider(CreateTaskContextFunction, bind);
    bindToolProvider(GetTaskContextFunction, bind);
    bindToolProvider(EditTaskContextFunction, bind);
    bindToolProvider(ListTaskContextsFunction, bind);
    bindToolProvider(RewriteTaskContextFunction, bind);
    bindToolProvider(TodoWriteTool, bind);
    bindToolProvider(UserInteractionTool, bind);
    bind(ChatResponsePartRenderer).to(TodoToolRenderer).inSingletonScope();
    bind(ChatResponsePartRenderer).to(UserInteractionToolRenderer).inSingletonScope();

    bind(ContextFileValidationServiceImpl).toSelf().inSingletonScope();
    bind(ContextFileValidationService).toService(ContextFileValidationServiceImpl);

    bind(FrontendApplicationContribution).to(RememberCommandContribution);
    bind(FrontendApplicationContribution).to(FixGitHubTicketCommandContribution);
    bind(FrontendApplicationContribution).to(AddressGhReviewCommandContribution);
    bind(FrontendApplicationContribution).to(AnalyzesGhTicketCommandContribution);
    bind(FrontendApplicationContribution).to(AppTesterCapabilityContribution);
    bind(FrontendApplicationContribution).to(GitHubCapabilityContribution);
    bind(FrontendApplicationContribution).to(ShellExecutionCapabilityContribution);

    bind(FrontendApplicationContribution).to(CodeReviewCapabilityContribution);
    bind(FrontendApplicationContribution).to(PRReviewCapabilityContribution);

    bind(AIFirstPerspectiveContribution).toSelf().inSingletonScope();
    bind(PerspectiveContribution).toService(AIFirstPerspectiveContribution);

    bind(AISessionsWidget).toSelf();
    bind(WidgetFactory)
        .toDynamicValue(ctx => ({
            id: AISessionsWidget.ID,
            createWidget: () => ctx.container.get(AISessionsWidget)
        }))
        .inSingletonScope();
    bindViewContribution(bind, AISessionsViewContribution);
    bind(TabBarToolbarContribution).toService(AISessionsViewContribution);
});
