import AIAgentService from "../Services/AIAgentService";
import LlmProviderService from "../Services/LlmProviderService";
import TelemetryExceptionService from "../Services/TelemetryExceptionService";
import ServiceService from "../Services/ServiceService";
import ServiceCodeRepositoryService from "../Services/ServiceCodeRepositoryService";
import CodeRepositoryService from "../Services/CodeRepositoryService";
import AIAgentTaskPullRequestService from "../Services/AIAgentTaskPullRequestService";
import AIAgentTaskService from "../Services/AIAgentTaskService";
import Express from "../Utils/Express";
import Response from "../Utils/Response";
import AIAgentTaskPullRequest from "../../Models/DatabaseModels/AIAgentTaskPullRequest";
import BadDataException from "../../Types/Exception/BadDataException";
import ObjectID from "../../Types/ObjectID";
import GitHubUtil from "../Utils/CodeRepository/GitHub/GitHub";
import CodeRepositoryType from "../../Types/CodeRepository/CodeRepositoryType";
import LIMIT_MAX from "../../Types/Database/LimitMax";
import URL from "../../Types/API/URL";
import PullRequestState from "../../Types/CodeRepository/PullRequestState";
import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
export default class AIAgentDataAPI {
    constructor() {
        this.router = Express.getRouter();
        this.initRoutes();
    }
    getRouter() {
        return this.router;
    }
    initRoutes() {
        // Get LLM configuration for a project
        this.router.post("/ai-agent-data/get-llm-config", async (req, res, next) => {
            try {
                const data = req.body;
                // Validate AI Agent credentials
                const aiAgent = await this.validateAIAgent(data);
                if (!aiAgent) {
                    return Response.sendErrorResponse(req, res, new BadDataException("Invalid AI Agent ID or AI Agent Key"));
                }
                // Get project ID
                if (!data["projectId"]) {
                    return Response.sendErrorResponse(req, res, new BadDataException("projectId is required"));
                }
                const projectId = new ObjectID(data["projectId"]);
                // Check if this is a Project AI Agent (has a projectId)
                const isProjectAIAgent = aiAgent.projectId !== null && aiAgent.projectId !== undefined;
                // Get LLM provider for the project
                const llmProvider = await LlmProviderService.getLLMProviderForProject(projectId);
                if (!llmProvider) {
                    return Response.sendErrorResponse(req, res, new BadDataException("No LLM provider configured for this project"));
                }
                /*
                 * Security check: Project AI Agents cannot access Global LLM Providers
                 * Only Global AI Agents (projectId is null) can access Global LLM Providers
                 */
                const isGlobalLLMProvider = llmProvider.isGlobalLlm === true;
                if (isProjectAIAgent && isGlobalLLMProvider) {
                    return Response.sendErrorResponse(req, res, new BadDataException("Project AI Agents cannot access Global LLM Providers. Please configure a project-specific LLM Provider."));
                }
                logger.debug(`LLM config fetched for project ${projectId.toString()}: ${llmProvider.llmType}`, getLogAttributesFromRequest(req));
                return Response.sendJsonObjectResponse(req, res, {
                    llmType: llmProvider.llmType,
                    apiKey: llmProvider.apiKey,
                    baseUrl: llmProvider.baseUrl,
                    modelName: llmProvider.modelName,
                });
            }
            catch (err) {
                next(err);
            }
        });
        // Get exception details with telemetry service info
        this.router.post("/ai-agent-data/get-exception-details", async (req, res, next) => {
            var _a, _b;
            try {
                const data = req.body;
                // Validate AI Agent credentials
                const aiAgent = await this.validateAIAgent(data);
                if (!aiAgent) {
                    return Response.sendErrorResponse(req, res, new BadDataException("Invalid AI Agent ID or AI Agent Key"));
                }
                // Get exception ID
                if (!data["exceptionId"]) {
                    return Response.sendErrorResponse(req, res, new BadDataException("exceptionId is required"));
                }
                const exceptionId = new ObjectID(data["exceptionId"]);
                // Get exception with service
                const exception = await TelemetryExceptionService.findOneById({
                    id: exceptionId,
                    select: {
                        _id: true,
                        message: true,
                        stackTrace: true,
                        exceptionType: true,
                        fingerprint: true,
                        primaryEntityId: true,
                        primaryEntityType: true,
                    },
                    props: {
                        isRoot: true,
                    },
                });
                if (!exception) {
                    return Response.sendErrorResponse(req, res, new BadDataException("Exception not found"));
                }
                logger.debug(`Exception details fetched: ${exception._id} - ${(_a = exception.message) === null || _a === void 0 ? void 0 : _a.substring(0, 100)}`, getLogAttributesFromRequest(req));
                /*
                 * primaryEntityId is polymorphic — resolve the Service only when it is
                 * a real Service. findOneById returns null for Host / DockerHost /
                 * KubernetesCluster / unattributed serviceIds (they aren't
                 * Services), preserving the previous "name only for real
                 * services" behaviour without the dropped ORM relation.
                 */
                const exceptionService = exception.primaryEntityId
                    ? await ServiceService.findOneById({
                        id: exception.primaryEntityId,
                        select: {
                            name: true,
                            description: true,
                        },
                        props: {
                            isRoot: true,
                        },
                    })
                    : null;
                return Response.sendJsonObjectResponse(req, res, {
                    exception: {
                        id: (_b = exception._id) === null || _b === void 0 ? void 0 : _b.toString(),
                        message: exception.message,
                        stackTrace: exception.stackTrace,
                        exceptionType: exception.exceptionType,
                        fingerprint: exception.fingerprint,
                    },
                    service: exception.primaryEntityId
                        ? {
                            id: exception.primaryEntityId.toString(),
                            name: exceptionService === null || exceptionService === void 0 ? void 0 : exceptionService.name,
                            description: exceptionService === null || exceptionService === void 0 ? void 0 : exceptionService.description,
                        }
                        : null,
                });
            }
            catch (err) {
                next(err);
            }
        });
        // Get code repositories linked to a service
        this.router.post("/ai-agent-data/get-code-repositories", async (req, res, next) => {
            var _a;
            try {
                const data = req.body;
                // Validate AI Agent credentials
                const aiAgent = await this.validateAIAgent(data);
                if (!aiAgent) {
                    return Response.sendErrorResponse(req, res, new BadDataException("Invalid AI Agent ID or AI Agent Key"));
                }
                // Get service ID (supports primaryEntityId plus legacy serviceId / telemetryServiceId from older agents)
                const serviceIdParam = data["primaryEntityId"] ||
                    data["serviceId"] ||
                    data["telemetryServiceId"];
                if (!serviceIdParam) {
                    return Response.sendErrorResponse(req, res, new BadDataException("primaryEntityId is required"));
                }
                const primaryEntityId = new ObjectID(serviceIdParam);
                // Find CodeRepositories linked to this Service
                const repositories = [];
                const serviceCodeRepositories = await ServiceCodeRepositoryService.findBy({
                    query: {
                        serviceId: primaryEntityId,
                    },
                    select: {
                        codeRepositoryId: true,
                        servicePathInRepository: true,
                        codeRepository: {
                            _id: true,
                            name: true,
                            repositoryHostedAt: true,
                            organizationName: true,
                            repositoryName: true,
                            mainBranchName: true,
                            gitHubAppInstallationId: true,
                        },
                    },
                    skip: 0,
                    limit: LIMIT_MAX,
                    props: {
                        isRoot: true,
                    },
                });
                for (const scr of serviceCodeRepositories) {
                    if (scr.codeRepository) {
                        // Check if we already have this repository
                        const existingRepo = repositories.some((r) => {
                            var _a, _b;
                            return r.id === ((_b = (_a = scr.codeRepository) === null || _a === void 0 ? void 0 : _a._id) === null || _b === void 0 ? void 0 : _b.toString());
                        });
                        if (!existingRepo) {
                            repositories.push({
                                id: ((_a = scr.codeRepository._id) === null || _a === void 0 ? void 0 : _a.toString()) || "",
                                name: scr.codeRepository.name || "",
                                repositoryHostedAt: scr.codeRepository.repositoryHostedAt || "",
                                organizationName: scr.codeRepository.organizationName || "",
                                repositoryName: scr.codeRepository.repositoryName || "",
                                mainBranchName: scr.codeRepository.mainBranchName || "main",
                                servicePathInRepository: scr.servicePathInRepository || null,
                                gitHubAppInstallationId: scr.codeRepository.gitHubAppInstallationId || null,
                            });
                        }
                    }
                }
                logger.debug(`Found ${repositories.length} code repositories for service ${primaryEntityId.toString()}`, getLogAttributesFromRequest(req));
                return Response.sendJsonObjectResponse(req, res, {
                    repositories: repositories,
                });
            }
            catch (err) {
                next(err);
            }
        });
        // Get access token for a code repository
        this.router.post("/ai-agent-data/get-repository-token", async (req, res, next) => {
            try {
                const data = req.body;
                // Validate AI Agent credentials
                const aiAgent = await this.validateAIAgent(data);
                if (!aiAgent) {
                    return Response.sendErrorResponse(req, res, new BadDataException("Invalid AI Agent ID or AI Agent Key"));
                }
                // Get code repository ID
                if (!data["codeRepositoryId"]) {
                    return Response.sendErrorResponse(req, res, new BadDataException("codeRepositoryId is required"));
                }
                const codeRepositoryId = new ObjectID(data["codeRepositoryId"]);
                // Get code repository
                const codeRepository = await CodeRepositoryService.findOneById({
                    id: codeRepositoryId,
                    select: {
                        _id: true,
                        repositoryHostedAt: true,
                        organizationName: true,
                        repositoryName: true,
                        gitHubAppInstallationId: true,
                    },
                    props: {
                        isRoot: true,
                    },
                });
                if (!codeRepository) {
                    return Response.sendErrorResponse(req, res, new BadDataException("Code repository not found"));
                }
                // Currently only supporting GitHub
                if (codeRepository.repositoryHostedAt !== CodeRepositoryType.GitHub) {
                    return Response.sendErrorResponse(req, res, new BadDataException(`Repository type ${codeRepository.repositoryHostedAt} is not yet supported`));
                }
                // Check if we have a GitHub App installation ID
                if (!codeRepository.gitHubAppInstallationId) {
                    return Response.sendErrorResponse(req, res, new BadDataException("No GitHub App installation ID found for this repository"));
                }
                /*
                 * Generate GitHub installation access token with write permissions
                 * Required for AI Agent to push branches and create pull requests
                 */
                const tokenData = await GitHubUtil.getInstallationAccessToken(codeRepository.gitHubAppInstallationId, {
                    permissions: {
                        contents: "write", // Required for pushing branches
                        pull_requests: "write", // Required for creating PRs
                        metadata: "read", // Required for reading repository metadata
                    },
                });
                const repositoryUrl = `https://github.com/${codeRepository.organizationName}/${codeRepository.repositoryName}.git`;
                logger.debug(`Generated access token for repository ${codeRepository.organizationName}/${codeRepository.repositoryName}`, getLogAttributesFromRequest(req));
                return Response.sendJsonObjectResponse(req, res, {
                    token: tokenData.token,
                    expiresAt: tokenData.expiresAt,
                    repositoryUrl: repositoryUrl,
                    organizationName: codeRepository.organizationName,
                    repositoryName: codeRepository.repositoryName,
                });
            }
            catch (err) {
                next(err);
            }
        });
        // Record a pull request created by the AI Agent
        this.router.post("/ai-agent-data/record-pull-request", async (req, res, next) => {
            var _a;
            try {
                const data = req.body;
                // Validate AI Agent credentials
                const aiAgent = await this.validateAIAgent(data);
                if (!aiAgent) {
                    return Response.sendErrorResponse(req, res, new BadDataException("Invalid AI Agent ID or AI Agent Key"));
                }
                // Validate required fields
                if (!data["taskId"]) {
                    return Response.sendErrorResponse(req, res, new BadDataException("taskId is required"));
                }
                if (!data["codeRepositoryId"]) {
                    return Response.sendErrorResponse(req, res, new BadDataException("codeRepositoryId is required"));
                }
                if (!data["pullRequestUrl"]) {
                    return Response.sendErrorResponse(req, res, new BadDataException("pullRequestUrl is required"));
                }
                if (!data["title"]) {
                    return Response.sendErrorResponse(req, res, new BadDataException("title is required"));
                }
                const taskId = new ObjectID(data["taskId"]);
                const codeRepositoryId = new ObjectID(data["codeRepositoryId"]);
                const pullRequestUrl = data["pullRequestUrl"];
                const pullRequestNumber = data["pullRequestNumber"];
                const pullRequestId = data["pullRequestId"];
                const title = data["title"];
                const description = data["description"];
                const headRefName = data["headRefName"];
                const baseRefName = data["baseRefName"];
                // Get the task to get the project ID
                const task = await AIAgentTaskService.findOneById({
                    id: taskId,
                    select: {
                        _id: true,
                        projectId: true,
                    },
                    props: {
                        isRoot: true,
                    },
                });
                if (!task) {
                    return Response.sendErrorResponse(req, res, new BadDataException("Task not found"));
                }
                // Get code repository for organization and repo name
                const codeRepository = await CodeRepositoryService.findOneById({
                    id: codeRepositoryId,
                    select: {
                        _id: true,
                        organizationName: true,
                        repositoryName: true,
                    },
                    props: {
                        isRoot: true,
                    },
                });
                // Create the pull request record
                const pullRequest = new AIAgentTaskPullRequest();
                if (task.projectId) {
                    pullRequest.projectId = task.projectId;
                }
                pullRequest.aiAgentTaskId = taskId;
                pullRequest.aiAgentId = aiAgent.id;
                pullRequest.codeRepositoryId = codeRepositoryId;
                pullRequest.pullRequestUrl = URL.fromString(pullRequestUrl);
                if (pullRequestNumber !== undefined) {
                    pullRequest.pullRequestNumber = pullRequestNumber;
                }
                if (pullRequestId !== undefined) {
                    pullRequest.pullRequestId = pullRequestId;
                }
                pullRequest.title = title;
                if (description) {
                    pullRequest.description = description;
                }
                pullRequest.pullRequestState = PullRequestState.Open;
                if (headRefName) {
                    pullRequest.headRefName = headRefName;
                }
                if (baseRefName) {
                    pullRequest.baseRefName = baseRefName;
                }
                if (codeRepository === null || codeRepository === void 0 ? void 0 : codeRepository.organizationName) {
                    pullRequest.repoOrganizationName = codeRepository.organizationName;
                }
                if (codeRepository === null || codeRepository === void 0 ? void 0 : codeRepository.repositoryName) {
                    pullRequest.repoName = codeRepository.repositoryName;
                }
                const createdPullRequest = await AIAgentTaskPullRequestService.create({
                    data: pullRequest,
                    props: {
                        isRoot: true,
                    },
                });
                logger.debug(`Recorded pull request ${pullRequestUrl} for task ${taskId.toString()}`, getLogAttributesFromRequest(req));
                return Response.sendJsonObjectResponse(req, res, {
                    success: true,
                    pullRequestId: (_a = createdPullRequest._id) === null || _a === void 0 ? void 0 : _a.toString(),
                });
            }
            catch (err) {
                next(err);
            }
        });
    }
    // Validate AI Agent credentials from request body
    async validateAIAgent(data) {
        if (!data["aiAgentId"] || !data["aiAgentKey"]) {
            return null;
        }
        const aiAgentId = new ObjectID(data["aiAgentId"]);
        const aiAgentKey = data["aiAgentKey"];
        const aiAgent = await AIAgentService.findOneBy({
            query: {
                _id: aiAgentId.toString(),
                key: aiAgentKey,
            },
            select: {
                _id: true,
                projectId: true, // Fetch projectId to check if this is a global or project AI agent
            },
            props: {
                isRoot: true,
            },
        });
        return aiAgent;
    }
}
//# sourceMappingURL=AIAgentDataAPI.js.map