// *****************************************************************************
// 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 {
    COOLIFY_LIST_PROJECTS_FUNCTION_ID,
    COOLIFY_LIST_APPLICATIONS_FUNCTION_ID,
    COOLIFY_CREATE_APPLICATION_FUNCTION_ID,
    COOLIFY_DEPLOY_APPLICATION_FUNCTION_ID,
    COOLIFY_GET_DEPLOYMENT_LOGS_FUNCTION_ID,
    COOLIFY_GET_APPLICATION_STATUS_FUNCTION_ID
} from '../common/workspace-functions';
import { COOLIFY_API_URL_PREF, COOLIFY_API_TOKEN_PREF } from '../common/workspace-preferences';

interface CoolifyProject {
    uuid: string;
    name: string;
    description?: string;
}

interface CoolifyApplication {
    uuid: string;
    name: string;
    description?: string;
    git_repository?: string;
    git_branch?: string;
    status?: string;
}

@injectable()
export class CoolifyListProjectsProvider implements ToolProvider {

    @inject(PreferenceService)
    protected readonly preferenceService: PreferenceService;

    getTool(): ToolRequest {
        return {
            id: COOLIFY_LIST_PROJECTS_FUNCTION_ID,
            name: COOLIFY_LIST_PROJECTS_FUNCTION_ID,
            description: 'Lists all projects in your Coolify instance. Projects are organizational containers for applications, databases, and services. ' +
                'Use this to discover existing projects or to get project UUIDs needed for other Coolify operations. ' +
                'Returns an array of projects with their UUIDs, names, and descriptions.',
            parameters: {
                type: 'object',
                properties: {},
                required: []
            },
            handler: (argString, ctx?: ToolInvocationContext) => this.handleListProjects(ctx?.cancellationToken)
        };
    }

    private async handleListProjects(cancellationToken?: CancellationToken): Promise<string> {
        const { apiUrl, apiToken } = await this.getCoolifyConfig();

        try {
            const response = await fetch(`${apiUrl}/api/v1/projects`, {
                method: 'GET',
                headers: {
                    'Authorization': `Bearer ${apiToken}`,
                    'Accept': 'application/json'
                }
            });

            if (!response.ok) {
                return `Error: Failed to fetch projects (${response.status} ${response.statusText})`;
            }

            const projects: CoolifyProject[] = await response.json();

            if (projects.length === 0) {
                return 'No projects found. Create your first project in Coolify to get started.';
            }

            const projectList = projects.map(p =>
                `- ${p.name} (UUID: ${p.uuid})${p.description ? `\n  Description: ${p.description}` : ''}`
            ).join('\n');

            return `Found ${projects.length} project(s):\n\n${projectList}`;
        } catch (error) {
            return `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`;
        }
    }

    private async getCoolifyConfig(): Promise<{ apiUrl: string; apiToken: string }> {
        const apiUrl = this.preferenceService.get<string>(COOLIFY_API_URL_PREF, '');
        const apiToken = this.preferenceService.get<string>(COOLIFY_API_TOKEN_PREF, '');

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

        return { apiUrl, apiToken };
    }
}

@injectable()
export class CoolifyListApplicationsProvider implements ToolProvider {

    @inject(PreferenceService)
    protected readonly preferenceService: PreferenceService;

    getTool(): ToolRequest {
        return {
            id: COOLIFY_LIST_APPLICATIONS_FUNCTION_ID,
            name: COOLIFY_LIST_APPLICATIONS_FUNCTION_ID,
            description: 'Lists all applications within a specific Coolify project. Applications are deployable services like web apps, APIs, or backend services. ' +
                'Requires a project UUID (get it from coolify_listProjects first). ' +
                'Returns application details including name, UUID, git repository, branch, and current status.',
            parameters: {
                type: 'object',
                properties: {
                    projectUuid: {
                        type: 'string',
                        description: 'The UUID of the Coolify project to list applications from. Get this from coolify_listProjects.'
                    }
                },
                required: ['projectUuid']
            },
            handler: (argString, ctx?: ToolInvocationContext) => this.handleListApplications(argString, ctx?.cancellationToken)
        };
    }

    private async handleListApplications(argString: string, cancellationToken?: CancellationToken): Promise<string> {
        const args = JSON.parse(argString);
        const { projectUuid } = args;

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

        try {
            // List all applications and filter by project
            const response = await fetch(`${apiUrl}/api/v1/applications`, {
                method: 'GET',
                headers: {
                    'Authorization': `Bearer ${apiToken}`,
                    'Accept': 'application/json'
                }
            });

            if (!response.ok) {
                return `Error: Failed to fetch applications (${response.status} ${response.statusText})`;
            }

            const allApplications: CoolifyApplication[] = await response.json();
            // Filter by project UUID if needed (Coolify returns all apps)
            const applications = projectUuid ? 
                allApplications.filter((app: any) => app.project_uuid === projectUuid) : 
                allApplications;

            if (applications.length === 0) {
                return `No applications found in project ${projectUuid}. Create your first application in Coolify.`;
            }

            const appList = applications.map(app =>
                `- ${app.name} (UUID: ${app.uuid})\n` +
                `  Status: ${app.status || 'unknown'}\n` +
                `  ${app.git_repository ? `Repository: ${app.git_repository}` : ''}\n` +
                `  ${app.git_branch ? `Branch: ${app.git_branch}` : ''}`
            ).join('\n');

            return `Found ${applications.length} application(s):\n\n${appList}`;
        } catch (error) {
            return `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`;
        }
    }

    private async getCoolifyConfig(): Promise<{ apiUrl: string; apiToken: string }> {
        const apiUrl = this.preferenceService.get<string>(COOLIFY_API_URL_PREF, '');
        const apiToken = this.preferenceService.get<string>(COOLIFY_API_TOKEN_PREF, '');

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

        return { apiUrl, apiToken };
    }
}

@injectable()
export class CoolifyCreateApplicationProvider implements ToolProvider {

    @inject(PreferenceService)
    protected readonly preferenceService: PreferenceService;

    getTool(): ToolRequest {
        return {
            id: COOLIFY_CREATE_APPLICATION_FUNCTION_ID,
            name: COOLIFY_CREATE_APPLICATION_FUNCTION_ID,
            description: 'Creates a new application in Coolify from a Git repository. ' +
                'This sets up the application configuration but does NOT deploy it yet - use coolify_deployApplication after creation. ' +
                'Supports both public and private Git repositories (Gitea, GitHub, GitLab, etc.). ' +
                'Returns the application UUID which you can use for deployment and monitoring.',
            parameters: {
                type: 'object',
                properties: {
                    projectUuid: {
                        type: 'string',
                        description: 'The UUID of the Coolify project to create the application in. Get this from coolify_listProjects.'
                    },
                    name: {
                        type: 'string',
                        description: 'A human-readable name for the application (e.g., "my-web-app", "api-server").'
                    },
                    gitRepository: {
                        type: 'string',
                        description: 'The Git repository URL. For Gitea: https://git.vibnai.com/username/repo-name.git'
                    },
                    gitBranch: {
                        type: 'string',
                        description: 'The Git branch to deploy from. Default: "main"'
                    },
                    buildPack: {
                        type: 'string',
                        description: 'Build pack to use. Options: "nixpacks" (auto-detects Node.js, Python, etc.), "dockerfile" (uses Dockerfile), "static" (static HTML/JS). Default: "nixpacks"'
                    },
                    portsExposes: {
                        type: 'string',
                        description: 'The port(s) the application exposes (e.g., "3000", "8080", "3000,8080"). Required for web apps.'
                    },
                    fqdn: {
                        type: 'string',
                        description: 'The fully qualified domain name for the application (e.g., "my-app.vibnai.com"). This will be the live URL for your app with automatic SSL.'
                    },
                    environmentName: {
                        type: 'string',
                        description: 'The environment name within the project. Default: "production"'
                    },
                    instantDeploy: {
                        type: 'boolean',
                        description: 'If true, automatically triggers deployment after creating the application. Default: false'
                    }
                },
                required: ['projectUuid', 'name', 'gitRepository', 'portsExposes', 'fqdn']
            },
            handler: (argString, ctx?: ToolInvocationContext) => this.handleCreateApplication(argString, ctx?.cancellationToken)
        };
    }

    private async handleCreateApplication(argString: string, cancellationToken?: CancellationToken): Promise<string> {
        const args = JSON.parse(argString);
        const {
            projectUuid,
            name,
            gitRepository,
            gitBranch = 'main',
            buildPack = 'nixpacks',
            portsExposes,
            fqdn,
            environmentName = 'production',
            instantDeploy = false
        } = args;

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

        try {
            // Get the localhost server UUID (where Coolify deploys apps)
            const serversResponse = await fetch(`${apiUrl}/api/v1/servers`, {
                headers: {
                    'Authorization': `Bearer ${apiToken}`,
                    'Accept': 'application/json'
                }
            });

            if (!serversResponse.ok) {
                return `Error: Failed to fetch servers (${serversResponse.status})`;
            }

            const servers = await serversResponse.json();
            const localhostServer = servers.find((s: any) => s.is_coolify_host === true);
            
            if (!localhostServer) {
                return 'Error: Could not find Coolify host server';
            }

            const payload: Record<string, any> = {
                project_uuid: projectUuid,
                environment_name: environmentName,
                server_uuid: localhostServer.uuid,
                name,
                git_repository: gitRepository,
                git_branch: gitBranch,
                build_pack: buildPack,
                ports_exposes: portsExposes,
                domains: `https://${fqdn}`, // Coolify expects full URL with protocol
                instant_deploy: instantDeploy
            };

            const response = await fetch(`${apiUrl}/api/v1/applications/public`, {
                method: 'POST',
                headers: {
                    'Authorization': `Bearer ${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 application (${response.status} ${response.statusText})\n${errorText}`;
            }

            const result = await response.json();

            return `✅ Application created successfully!\n` +
                `Name: ${name}\n` +
                `UUID: ${result.uuid || result.application_uuid || 'N/A'}\n` +
                `URL: https://${fqdn}\n` +
                `Repository: ${gitRepository}\n` +
                `Branch: ${gitBranch}\n` +
                `${instantDeploy ? 'Deployment initiated automatically. Your app will be live at the URL above once deployment completes.' : 'Use coolify_deployApplication to deploy when ready.'}`;
        } catch (error) {
            return `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`;
        }
    }

    private async getCoolifyConfig(): Promise<{ apiUrl: string; apiToken: string }> {
        const apiUrl = this.preferenceService.get<string>(COOLIFY_API_URL_PREF, '');
        const apiToken = this.preferenceService.get<string>(COOLIFY_API_TOKEN_PREF, '');

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

        return { apiUrl, apiToken };
    }
}

@injectable()
export class CoolifyDeployApplicationProvider implements ToolProvider {

    @inject(PreferenceService)
    protected readonly preferenceService: PreferenceService;

    getTool(): ToolRequest {
        return {
            id: COOLIFY_DEPLOY_APPLICATION_FUNCTION_ID,
            name: COOLIFY_DEPLOY_APPLICATION_FUNCTION_ID,
            description: 'Triggers a deployment for a specific application in Coolify. This will pull the latest code from git, build, and deploy the application. ' +
                'Requires the application UUID (get from coolify_listApplications). ' +
                'Optionally specify a git commit SHA or tag to deploy a specific version. ' +
                'Returns the deployment UUID which can be used to check logs and status.',
            parameters: {
                type: 'object',
                properties: {
                    applicationUuid: {
                        type: 'string',
                        description: 'The UUID of the application to deploy. Get this from coolify_listApplications.'
                    },
                    gitCommitSha: {
                        type: 'string',
                        description: 'Optional: Specific git commit SHA or tag to deploy. If not provided, deploys the latest commit from the configured branch.'
                    },
                    forceRebuild: {
                        type: 'boolean',
                        description: 'Optional: Force a complete rebuild even if the code hasn\'t changed. Default: false.'
                    }
                },
                required: ['applicationUuid']
            },
            handler: (argString, ctx?: ToolInvocationContext) => this.handleDeploy(argString, ctx?.cancellationToken)
        };
    }

    private async handleDeploy(argString: string, cancellationToken?: CancellationToken): Promise<string> {
        const args = JSON.parse(argString);
        const { applicationUuid, gitCommitSha, forceRebuild } = args;

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

        try {
            const body: Record<string, string | boolean> = {};
            if (gitCommitSha) {
                body.git_commit_sha = gitCommitSha;
            }
            if (forceRebuild) {
                body.force_rebuild = true;
            }

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

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

            const result = await response.json();

            return `✅ Deployment initiated successfully!\n` +
                `Deployment UUID: ${result.deployment_uuid || 'N/A'}\n` +
                `Application: ${applicationUuid}\n` +
                `${gitCommitSha ? `Commit: ${gitCommitSha}\n` : ''}` +
                `Use coolify_getDeploymentLogs to monitor progress.`;
        } catch (error) {
            return `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`;
        }
    }

    private async getCoolifyConfig(): Promise<{ apiUrl: string; apiToken: string }> {
        const apiUrl = this.preferenceService.get<string>(COOLIFY_API_URL_PREF, '');
        const apiToken = this.preferenceService.get<string>(COOLIFY_API_TOKEN_PREF, '');

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

        return { apiUrl, apiToken };
    }
}

@injectable()
export class CoolifyGetDeploymentLogsProvider implements ToolProvider {

    @inject(PreferenceService)
    protected readonly preferenceService: PreferenceService;

    getTool(): ToolRequest {
        return {
            id: COOLIFY_GET_DEPLOYMENT_LOGS_FUNCTION_ID,
            name: COOLIFY_GET_DEPLOYMENT_LOGS_FUNCTION_ID,
            description: 'Retrieves real-time deployment logs for a specific application deployment in Coolify. ' +
                'Use this to monitor deployment progress, debug build failures, or verify successful deployments. ' +
                'Requires the application UUID. Returns the most recent deployment logs.',
            parameters: {
                type: 'object',
                properties: {
                    applicationUuid: {
                        type: 'string',
                        description: 'The UUID of the application to get logs for.'
                    },
                    limit: {
                        type: 'number',
                        description: 'Optional: Maximum number of log lines to return. Default: 100.'
                    }
                },
                required: ['applicationUuid']
            },
            handler: (argString, ctx?: ToolInvocationContext) => this.handleGetLogs(argString, ctx?.cancellationToken)
        };
    }

    private async handleGetLogs(argString: string, cancellationToken?: CancellationToken): Promise<string> {
        const args = JSON.parse(argString);
        const { applicationUuid, limit = 100 } = args;

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

        try {
            const response = await fetch(`${apiUrl}/api/v1/applications/${applicationUuid}/logs?limit=${limit}`, {
                method: 'GET',
                headers: {
                    'Authorization': `Bearer ${apiToken}`,
                    'Accept': 'application/json'
                }
            });

            if (!response.ok) {
                return `Error: Failed to fetch logs (${response.status} ${response.statusText})`;
            }

            const logs = await response.text();

            if (!logs || logs.trim().length === 0) {
                return `No logs available yet for application ${applicationUuid}. The deployment may not have started.`;
            }

            return `Deployment logs for ${applicationUuid}:\n\n${logs}`;
        } catch (error) {
            return `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`;
        }
    }

    private async getCoolifyConfig(): Promise<{ apiUrl: string; apiToken: string }> {
        const apiUrl = this.preferenceService.get<string>(COOLIFY_API_URL_PREF, '');
        const apiToken = this.preferenceService.get<string>(COOLIFY_API_TOKEN_PREF, '');

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

        return { apiUrl, apiToken };
    }
}

@injectable()
export class CoolifyGetApplicationStatusProvider implements ToolProvider {

    @inject(PreferenceService)
    protected readonly preferenceService: PreferenceService;

    getTool(): ToolRequest {
        return {
            id: COOLIFY_GET_APPLICATION_STATUS_FUNCTION_ID,
            name: COOLIFY_GET_APPLICATION_STATUS_FUNCTION_ID,
            description: 'Gets the current status of a Coolify application including running state, health checks, resource usage, and deployment history. ' +
                'Use this to verify if an application is running correctly or to troubleshoot issues. ' +
                'Returns detailed status information including container state, URL, and recent deployments.',
            parameters: {
                type: 'object',
                properties: {
                    applicationUuid: {
                        type: 'string',
                        description: 'The UUID of the application to check status for.'
                    }
                },
                required: ['applicationUuid']
            },
            handler: (argString, ctx?: ToolInvocationContext) => this.handleGetStatus(argString, ctx?.cancellationToken)
        };
    }

    private async handleGetStatus(argString: string, cancellationToken?: CancellationToken): Promise<string> {
        const args = JSON.parse(argString);
        const { applicationUuid } = args;

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

        try {
            const response = await fetch(`${apiUrl}/api/v1/applications/${applicationUuid}`, {
                method: 'GET',
                headers: {
                    'Authorization': `Bearer ${apiToken}`,
                    'Accept': 'application/json'
                }
            });

            if (!response.ok) {
                return `Error: Failed to fetch application status (${response.status} ${response.statusText})`;
            }

            const app: any = await response.json();

            const statusInfo = [
                `Application: ${app.name}`,
                `UUID: ${app.uuid}`,
                `Status: ${app.status || 'unknown'}`,
                `${app.fqdn ? `URL: ${app.fqdn}` : 'URL: Not configured'}`,
                `${app.git_repository ? `Repository: ${app.git_repository}` : ''}`,
                `${app.git_branch ? `Branch: ${app.git_branch}` : ''}`,
                `${app.git_commit_sha ? `Latest Commit: ${app.git_commit_sha.substring(0, 7)}` : ''}`,
                `Created: ${app.created_at || 'N/A'}`,
                `Updated: ${app.updated_at || 'N/A'}`
            ].filter(line => line && !line.endsWith(': ')).join('\n');

            return statusInfo;
        } catch (error) {
            return `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`;
        }
    }

    private async getCoolifyConfig(): Promise<{ apiUrl: string; apiToken: string }> {
        const apiUrl = this.preferenceService.get<string>(COOLIFY_API_URL_PREF, '');
        const apiToken = this.preferenceService.get<string>(COOLIFY_API_TOKEN_PREF, '');

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

        return { apiUrl, apiToken };
    }
}
