// *****************************************************************************
// 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 { ToolInvocationContext, ToolProvider, ToolRequest } from '@theia/ai-core';
import { CancellationToken } from '@theia/core';
import { injectable, inject } from '@theia/core/shared/inversify';
import { PreferenceService } from '@theia/core/lib/common/preferences/preference-service';
import {
    GITEA_CREATE_REPOSITORY_FUNCTION_ID,
    GIT_PUSH_TO_REMOTE_FUNCTION_ID
} from '../common/workspace-functions';
import { GITEA_API_URL_PREF, GITEA_API_TOKEN_PREF, GITEA_USERNAME_PREF } from '../common/workspace-preferences';

interface GiteaRepository {
    id: number;
    name: string;
    full_name: string;
    description?: string;
    html_url: string;
    clone_url: string;
    ssh_url: string;
}

@injectable()
export class GiteaCreateRepositoryProvider implements ToolProvider {

    @inject(PreferenceService)
    protected readonly preferenceService: PreferenceService;

    getTool(): ToolRequest {
        return {
            id: GITEA_CREATE_REPOSITORY_FUNCTION_ID,
            name: GITEA_CREATE_REPOSITORY_FUNCTION_ID,
            description: 'Creates a new Git repository on Gitea (self-hosted Git service). ' +
                'The repository is created under the configured username and is ready to receive code pushes. ' +
                'Returns the repository URL and clone information.',
            parameters: {
                type: 'object',
                properties: {
                    name: {
                        type: 'string',
                        description: 'The repository name (e.g., "my-web-app", "api-server"). Must be lowercase with hyphens or underscores only.'
                    },
                    description: {
                        type: 'string',
                        description: 'Optional: A brief description of the repository.'
                    },
                    isPrivate: {
                        type: 'boolean',
                        description: 'Optional: Whether the repository should be private. Default: true for user projects.'
                    },
                    autoInit: {
                        type: 'boolean',
                        description: 'Optional: Initialize with README. Default: false (will be initialized when pushing code).'
                    }
                },
                required: ['name']
            },
            handler: (argString, ctx?: ToolInvocationContext) => this.handleCreateRepository(argString, ctx?.cancellationToken)
        };
    }

    private async handleCreateRepository(argString: string, cancellationToken?: CancellationToken): Promise<string> {
        const args = JSON.parse(argString);
        const { name, description = '', isPrivate = true, autoInit = false } = args;

        const { apiUrl, apiToken } = await this.getGiteaConfig();

        try {
            const payload = {
                name,
                description,
                private: isPrivate,
                auto_init: autoInit,
                default_branch: 'main'
            };

            const response = await fetch(`${apiUrl}/api/v1/user/repos`, {
                method: 'POST',
                headers: {
                    'Authorization': `token ${apiToken}`,
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                body: JSON.stringify(payload)
            });

            if (!response.ok) {
                const errorText = await response.text();
                return `Error: Failed to create repository (${response.status} ${response.statusText})\n${errorText}`;
            }

            const repo: GiteaRepository = await response.json();

            return `✅ Repository created successfully!\n` +
                `Name: ${repo.full_name}\n` +
                `URL: ${repo.html_url}\n` +
                `Clone URL: ${repo.clone_url}\n` +
                `SSH URL: ${repo.ssh_url}\n` +
                `Visibility: ${isPrivate ? 'Private' : 'Public'}\n` +
                `\nNext: Use git_pushToRemote to push your code to this repository.`;
        } catch (error) {
            return `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`;
        }
    }

    private async getGiteaConfig(): Promise<{ apiUrl: string; apiToken: string; username: string }> {
        const apiUrl = this.preferenceService.get<string>(GITEA_API_URL_PREF, '');
        const apiToken = this.preferenceService.get<string>(GITEA_API_TOKEN_PREF, '');
        const username = this.preferenceService.get<string>(GITEA_USERNAME_PREF, '');

        if (!apiUrl || !apiToken || !username) {
            throw new Error('Gitea API URL, Token, and Username must be configured in preferences');
        }

        return { apiUrl, apiToken, username };
    }
}

@injectable()
export class GitPushToRemoteProvider implements ToolProvider {

    @inject(PreferenceService)
    protected readonly preferenceService: PreferenceService;

    getTool(): ToolRequest {
        return {
            id: GIT_PUSH_TO_REMOTE_FUNCTION_ID,
            name: GIT_PUSH_TO_REMOTE_FUNCTION_ID,
            description: 'Initializes a Git repository in the workspace and pushes code to a remote Gitea repository. ' +
                'This automates: git init, git add, git commit, git remote add, and git push. ' +
                'Use this after generating code to push it to Gitea for deployment. ' +
                'Requires the remote repository URL from gitea_createRepository.',
            parameters: {
                type: 'object',
                properties: {
                    remoteUrl: {
                        type: 'string',
                        description: 'The HTTPS clone URL of the Gitea repository (e.g., https://git.vibnai.com/mark/my-app.git). Get this from gitea_createRepository.'
                    },
                    commitMessage: {
                        type: 'string',
                        description: 'Optional: Initial commit message. Default: "Initial commit from Theia Code OS"'
                    },
                    branch: {
                        type: 'string',
                        description: 'Optional: Branch name to push to. Default: "main"'
                    },
                    workspacePath: {
                        type: 'string',
                        description: 'Optional: Relative path within workspace to push. Default: workspace root (all files)'
                    }
                },
                required: ['remoteUrl']
            },
            handler: (argString, ctx?: ToolInvocationContext) => this.handlePushToRemote(argString, ctx?.cancellationToken)
        };
    }

    private async handlePushToRemote(argString: string, cancellationToken?: CancellationToken): Promise<string> {
        const args = JSON.parse(argString);
        const {
            remoteUrl,
            commitMessage = 'Initial commit from Theia Code OS',
            branch = 'main',
            workspacePath = '.'
        } = args;

        const { apiToken, username } = await this.getGiteaConfig();

        // Inject credentials into URL for authentication
        const authenticatedUrl = remoteUrl.replace('https://', `https://${username}:${apiToken}@`);

        try {
            // Note: This is a simplified implementation that returns shell commands to execute
            // In a production environment, this would use the Terminal API or ProcessManager
            const commands = [
                `cd ${workspacePath}`,
                'git init',
                'git add .',
                `git commit -m "${commitMessage}"`,
                `git branch -M ${branch}`,
                `git remote add origin ${authenticatedUrl}`,
                `git push -u origin ${branch}`
            ].join(' && ');

            return `⚠️ Git push not yet implemented - please execute these commands in the terminal:\n\n${commands}\n\n` +
                `Note: The URL includes authentication credentials automatically.`;
        } catch (error) {
            return `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`;
        }
    }

    private async getGiteaConfig(): Promise<{ apiUrl: string; apiToken: string; username: string }> {
        const apiUrl = this.preferenceService.get<string>(GITEA_API_URL_PREF, '');
        const apiToken = this.preferenceService.get<string>(GITEA_API_TOKEN_PREF, '');
        const username = this.preferenceService.get<string>(GITEA_USERNAME_PREF, '');

        if (!apiUrl || !apiToken || !username) {
            throw new Error('Gitea API URL, Token, and Username must be configured in preferences');
        }

        return { apiUrl, apiToken, username };
    }
}
