// *****************************************************************************
// 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 { FrontendApplicationContribution } from '@theia/core/lib/browser';
import { inject, injectable } from '@theia/core/shared/inversify';
import {
    CUSTOM_AGENT_WORKSPACE_DIRECTORIES, DefaultPromptFragmentCustomizationService, PromptFragmentCustomizationProperties
} from '@theia/ai-core/lib/browser/frontend-prompt-customization-service';
import {
    PROMPT_TEMPLATE_WORKSPACE_DIRECTORIES_PREF,
    PROMPT_TEMPLATE_ADDITIONAL_EXTENSIONS_PREF,
    PROMPT_TEMPLATE_WORKSPACE_FILES_PREF
} from '../common/workspace-preferences';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { Path, PreferenceService } from '@theia/core';
import { WorkspaceTrustService } from '@theia/workspace/lib/browser/workspace-trust-service';

@injectable()
export class TemplatePreferenceContribution implements FrontendApplicationContribution {

    @inject(PreferenceService)
    protected readonly preferenceService: PreferenceService;

    @inject(DefaultPromptFragmentCustomizationService)
    protected readonly customizationService: DefaultPromptFragmentCustomizationService;

    @inject(WorkspaceService)
    protected readonly workspaceService: WorkspaceService;

    @inject(WorkspaceTrustService)
    protected readonly workspaceTrustService: WorkspaceTrustService;

    onStart(): void {
        Promise.all([
            this.preferenceService.ready,
            this.workspaceService.ready,
            this.workspaceTrustService.getWorkspaceTrust()
        ]).then(() => {
            // Set initial template configuration from preferences
            this.updateConfiguration();

            // Listen for preference changes
            this.preferenceService.onPreferenceChanged(event => {
                if (event.preferenceName === PROMPT_TEMPLATE_WORKSPACE_DIRECTORIES_PREF ||
                    event.preferenceName === PROMPT_TEMPLATE_ADDITIONAL_EXTENSIONS_PREF ||
                    event.preferenceName === PROMPT_TEMPLATE_WORKSPACE_FILES_PREF) {
                    this.updateConfiguration(event.preferenceName);
                }
            });

            // Listen for workspace root changes
            this.workspaceService.onWorkspaceLocationChanged(() => {
                this.updateConfiguration();
            });

            // Listen for trust changes to reload/unload workspace templates
            this.workspaceTrustService.onDidChangeWorkspaceTrust(() => {
                this.updateConfiguration();
            });
        });
    }

    /**
     * Updates the template configuration in the customization service.
     * If a specific preference name is provided, only that configuration aspect is updated.
     * @param changedPreference Optional name of the preference that changed
     */
    protected async updateConfiguration(changedPreference?: string): Promise<void> {
        const workspaceRoots = this.workspaceService.tryGetRoots();
        if (workspaceRoots.length === 0) {
            return;
        }

        const trusted = await this.workspaceTrustService.getWorkspaceTrust();
        const configProperties: PromptFragmentCustomizationProperties = {};

        if (!changedPreference || changedPreference === PROMPT_TEMPLATE_WORKSPACE_DIRECTORIES_PREF) {
            if (trusted) {
                const relativeDirectories = this.preferenceService.get<string[]>(PROMPT_TEMPLATE_WORKSPACE_DIRECTORIES_PREF, []);
                configProperties.directoryPaths = workspaceRoots.flatMap(root =>
                    relativeDirectories.map(dir => {
                        const path = new Path(dir);
                        const uri = root.resource.resolve(path.toString());
                        return uri.path.toString();
                    })
                );
            } else {
                configProperties.directoryPaths = [];
            }
        }

        if (!changedPreference || changedPreference === PROMPT_TEMPLATE_ADDITIONAL_EXTENSIONS_PREF) {
            if (trusted) {
                configProperties.extensions = this.preferenceService.get<string[]>(PROMPT_TEMPLATE_ADDITIONAL_EXTENSIONS_PREF, []);
            } else {
                configProperties.extensions = [];
            }
        }

        if (!changedPreference || changedPreference === PROMPT_TEMPLATE_WORKSPACE_FILES_PREF) {
            if (trusted) {
                const relativeFilePaths = this.preferenceService.get<string[]>(PROMPT_TEMPLATE_WORKSPACE_FILES_PREF, []);
                configProperties.filePaths = workspaceRoots.flatMap(root =>
                    relativeFilePaths.map(filePath => {
                        const path = new Path(filePath);
                        const uri = root.resource.resolve(path.toString());
                        return uri.path.toString();
                    })
                );
            } else {
                configProperties.filePaths = [];
            }
        }

        // The built-in custom-agent directories (`.agents`/`.prompts`) depend only on the workspace
        // roots and trust state, not on any preference, so they are recomputed on every full update.
        if (!changedPreference) {
            if (trusted) {
                configProperties.agentDirectoryPaths = workspaceRoots.flatMap(root =>
                    CUSTOM_AGENT_WORKSPACE_DIRECTORIES.map(dir => root.resource.resolve(dir).path.toString())
                );
            } else {
                configProperties.agentDirectoryPaths = [];
            }
        }

        await this.customizationService.updateConfiguration(configProperties);
    }
}
