import { GitProviderRoot } from './gitProviderRoot.js';
import c from 'chalk';
import fs from "fs-extra";
import FormData from 'form-data';
import * as path from "path";
import { git, uxLog } from '../utils/index.js';
import bbPkg from 'bitbucket';
import { CONSTANTS } from '../../config/index.js';
const { Bitbucket } = bbPkg;
export class BitbucketProvider extends GitProviderRoot {
    bitbucket;
    serverUrl = 'https://bitbucket.org';
    token;
    constructor() {
        super();
        this.token = process.env.CI_SFDX_HARDIS_BITBUCKET_TOKEN || '';
        const clientOptions = { auth: { token: this.token } };
        this.bitbucket = new Bitbucket(clientOptions);
    }
    getLabel() {
        return 'sfdx-hardis Bitbucket connector';
    }
    async getCurrentJobUrl() {
        if (process.env.PIPELINE_JOB_URL) {
            return process.env.PIPELINE_JOB_URL;
        }
        if (process.env.BITBUCKET_WORKSPACE && process.env.BITBUCKET_REPO_SLUG && process.env.BITBUCKET_BUILD_NUMBER) {
            const jobUrl = `${this.serverUrl}/${process.env.BITBUCKET_WORKSPACE}/${process.env.BITBUCKET_REPO_SLUG}/pipelines/results/${process.env.BITBUCKET_BUILD_NUMBER}`;
            return jobUrl;
        }
        uxLog("warning", this, c.yellow(`[Bitbucket Integration] You need the following variables to be accessible to sfdx-hardis to build current job url:
        - BITBUCKET_WORKSPACE
        - BITBUCKET_REPO_SLUG
        - BITBUCKET_BUILD_NUMBER`));
        return null;
    }
    async getCurrentBranchUrl() {
        if (process.env.BITBUCKET_WORKSPACE && process.env.BITBUCKET_REPO_SLUG && process.env.BITBUCKET_BRANCH) {
            const currentBranchUrl = `${this.serverUrl}/${process.env.BITBUCKET_WORKSPACE}/${process.env.BITBUCKET_REPO_SLUG}/branch/${process.env.BITBUCKET_BRANCH}`;
            return currentBranchUrl;
        }
        uxLog("warning", this, c.yellow(`[Bitbucket Integration] You need the following variables to be accessible to sfdx-hardis to build current job url:
        - BITBUCKET_WORKSPACE
        - BITBUCKET_REPO_SLUG
        - BITBUCKET_BRANCH`));
        return null;
    }
    // Bitbucket does not supports mermaid in PR markdown
    async supportsMermaidInPrMarkdown() {
        return false;
    }
    // Find pull request info
    async getPullRequestInfo() {
        const pullRequestIdStr = process.env.BITBUCKET_PR_ID || null;
        const repoSlug = process.env.BITBUCKET_REPO_SLUG || null;
        const workspace = process.env.BITBUCKET_WORKSPACE || null;
        // Case when PR is found in the context
        if (pullRequestIdStr !== null) {
            const pullRequestId = Number(pullRequestIdStr);
            const pullRequest = await this.bitbucket.repositories.getPullRequest({
                pull_request_id: pullRequestId,
                repo_slug: repoSlug || '',
                workspace: workspace || '',
            });
            if (pullRequest?.data.destination) {
                // Add cross git provider properties used by sfdx-hardis
                return this.completePullRequestInfo(pullRequest.data);
            }
            else {
                uxLog("warning", this, c.yellow(`[Bitbucket Integration] Warning: incomplete PR found (id: ${pullRequestIdStr})`));
                uxLog("log", this, c.grey(JSON.stringify(pullRequest || {})));
            }
        }
        // Case when we find PR from a commit
        const sha = await git().revparse(['HEAD']);
        const latestPullRequestsOnBranch = await this.bitbucket.repositories.listPullrequestsForCommit({
            // cspell:disable-line
            commit: sha,
            repo_slug: repoSlug || '',
            workspace: workspace || '',
        });
        const latestMergedPullRequestOnBranch = latestPullRequestsOnBranch?.data?.values?.filter((pr) => pr.state === 'MERGED' && pr.merge_commit?.hash === sha);
        if (latestMergedPullRequestOnBranch?.length && latestMergedPullRequestOnBranch?.length > 0) {
            const pullRequest = latestMergedPullRequestOnBranch[0];
            // Add cross git provider properties used by sfdx-hardis
            return this.completePullRequestInfo(pullRequest);
        }
        uxLog("log", this, c.grey(`[Bitbucket Integration] Unable to find related Pull Request Info`));
        return null;
    }
    async getBranchDeploymentCheckId(gitBranch) {
        let deploymentCheckId = null;
        const repoSlug = process.env.BITBUCKET_REPO_SLUG || null;
        const workspace = process.env.BITBUCKET_WORKSPACE || null;
        const latestMergedPullRequestsOnBranch = await this.bitbucket.repositories.listPullRequests({
            repo_slug: repoSlug || '',
            workspace: workspace || '',
            state: 'MERGED',
            q: `destination.branch.name = "${gitBranch}"`,
            sort: '-updated_on',
        });
        if (latestMergedPullRequestsOnBranch?.data?.values?.length &&
            latestMergedPullRequestsOnBranch?.data?.values?.length > 0) {
            const latestPullRequest = latestMergedPullRequestsOnBranch?.data?.values[0];
            const latestPullRequestId = latestPullRequest.id;
            deploymentCheckId = await this.getDeploymentIdFromPullRequest(latestPullRequestId || 0, repoSlug || '', workspace || '', deploymentCheckId, this.completePullRequestInfo(latestPullRequest));
        }
        return deploymentCheckId;
    }
    async getPullRequestDeploymentCheckId() {
        const pullRequestInfo = await this.getPullRequestInfo();
        if (pullRequestInfo) {
            const repoSlug = process.env.BITBUCKET_REPO_SLUG || null;
            const workspace = process.env.BITBUCKET_WORKSPACE || null;
            return await this.getDeploymentIdFromPullRequest(pullRequestInfo.idNumber || 0, repoSlug || '', workspace || '', null, pullRequestInfo);
        }
        return null;
    }
    async getDeploymentIdFromPullRequest(latestPullRequestId, repoSlug, workspace, deploymentCheckId, latestPullRequest) {
        const comments = await this.bitbucket.repositories.listPullRequestComments({
            pull_request_id: latestPullRequestId,
            repo_slug: repoSlug,
            workspace: workspace,
        });
        for (const comment of comments?.data?.values || []) {
            if ((comment?.content?.raw || '').includes(`<!-- sfdx-hardis deployment-id `)) {
                const matches = /<!-- sfdx-hardis deployment-id (.*) -->/gm.exec(comment?.content?.raw || '');
                if (matches) {
                    deploymentCheckId = matches[1];
                    uxLog("log", this, c.grey(`[Bitbucket Integration] Found deployment id ${deploymentCheckId} on PR #${latestPullRequestId} ${latestPullRequest.title}`));
                    break;
                }
            }
        }
        return deploymentCheckId;
    }
    async postPullRequestMessage(prMessage) {
        const pullRequestIdStr = process.env.BITBUCKET_PR_ID || null;
        const repoSlug = process.env.BITBUCKET_REPO_SLUG || null;
        const workspace = process.env.BITBUCKET_WORKSPACE || null;
        if (repoSlug == null || pullRequestIdStr == null) {
            uxLog("log", this, c.grey('[Bitbucket integration] No repo and pull request, so no note posted...'));
            return { posted: false, providerResult: { info: 'No related pull request' } };
        }
        const pullRequestId = Number(pullRequestIdStr);
        const bitbucketBuildNumber = process.env.BITBUCKET_BUILD_NUMBER || null;
        const bitbucketJobUrl = await this.getCurrentJobUrl();
        const messageKey = `${prMessage.messageKey}-${pullRequestId}`;
        let messageBody = `**${prMessage.title || ''}**

        ${prMessage.message}
        
        \n_Powered by [sfdx-hardis](${CONSTANTS.DOC_URL_ROOT}) from job [${bitbucketBuildNumber}](${bitbucketJobUrl})_
        \n<!-- sfdx-hardis message-key ${messageKey} -->
        `;
        // Add deployment id if present
        if (globalThis.pullRequestDeploymentId) {
            messageBody += `\n<!-- sfdx-hardis deployment-id ${globalThis.pullRequestDeploymentId} -->`;
        }
        messageBody = await this.uploadAndReplaceImageReferences(messageBody, prMessage.sourceFile || "");
        const commentBody = {
            content: {
                raw: messageBody,
            },
        };
        // Check for existing comment from a previous run
        uxLog("log", this, c.grey('[Bitbucket integration] Listing comments of Pull Request...'));
        const existingComments = await this.bitbucket.repositories.listPullRequestComments({
            pull_request_id: pullRequestId,
            repo_slug: repoSlug,
            workspace: workspace || '',
        });
        let existingCommentId = null;
        for (const existingComment of existingComments?.data?.values || []) {
            if (existingComment?.content?.raw &&
                existingComment?.content.raw?.includes(`<!-- sfdx-hardis message-key ${messageKey} -->`)) {
                existingCommentId = existingComment.id || null;
            }
        }
        // Create or update MR comment
        if (existingCommentId) {
            // Update existing comment
            uxLog("log", this, c.grey('[Bitbucket integration] Updating Pull Request Comment on Bitbucket...'));
            const pullRequestComment = await this.bitbucket.repositories.updatePullRequestComment({
                workspace: workspace || '',
                repo_slug: repoSlug,
                pull_request_id: pullRequestId,
                comment_id: existingCommentId,
                _body: commentBody,
            });
            const prResult = {
                posted: (pullRequestComment?.data?.id || -1) > 0,
                providerResult: pullRequestComment,
            };
            uxLog("log", this, c.grey(`[Bitbucket integration] Updated Pull Request comment ${existingCommentId}`));
            return prResult;
        }
        else {
            // Create new comment if no existing comment was found
            uxLog("log", this, c.grey('[Bitbucket integration] Adding Pull Request Comment on Bitbucket...'));
            const pullRequestComment = await this.bitbucket.repositories.createPullRequestComment({
                workspace: workspace || '',
                repo_slug: repoSlug,
                pull_request_id: pullRequestId,
                _body: commentBody,
            });
            const prResult = {
                posted: (pullRequestComment?.data?.id || -1) > 0,
                providerResult: pullRequestComment,
            };
            if (prResult.posted) {
                uxLog("log", this, c.grey(`[Bitbucket integration] Posted Pull Request comment on ${pullRequestId}`));
            }
            else {
                uxLog("warning", this, c.yellow(`[Bitbucket integration] Unable to post Pull Request comment on ${pullRequestId}:\n${JSON.stringify(pullRequestComment, null, 2)}`));
            }
            return prResult;
        }
    }
    completePullRequestInfo(prData) {
        const prInfo = {
            idNumber: prData?.id || 0,
            idStr: prData.id ? prData?.id?.toString() : '',
            sourceBranch: prData?.source?.branch?.name || '',
            targetBranch: prData?.destination?.branch?.name || '',
            title: prData?.rendered?.title?.raw || prData?.rendered?.title?.markup || prData?.rendered?.title?.html || '',
            description: prData?.rendered?.description?.raw || prData?.rendered?.description?.markup || prData?.rendered?.description?.html || '',
            webUrl: prData?.links?.html?.href || '',
            authorName: prData?.author?.display_name || '',
            providerInfo: prData,
            customBehaviors: {}
        };
        return this.completeWithCustomBehaviors(prInfo);
    }
    // Upload the image to Bitbucket
    async uploadImage(localImagePath) {
        try {
            const imageName = path.basename(localImagePath);
            const filesForm = new FormData();
            filesForm.append("files", fs.createReadStream(localImagePath));
            const attachmentResponse = await this.bitbucket.repositories.createDownload({
                workspace: process.env.BITBUCKET_WORKSPACE || "",
                repo_slug: process.env.BITBUCKET_REPO_SLUG || "",
                _body: filesForm,
            });
            if (attachmentResponse) {
                const imageRef = `${this.serverUrl}/${process.env.BITBUCKET_WORKSPACE}/${process.env.BITBUCKET_REPO_SLUG}/downloads/${imageName}`;
                uxLog("log", this, c.grey(`[Bitbucket Integration] Image uploaded for comment: ${imageRef}`));
                return imageRef;
            }
            else {
                uxLog("warning", this, c.yellow(`[Bitbucket Integration] Image uploaded but unable to get URL from response\n${JSON.stringify(attachmentResponse, null, 2)}`));
            }
        }
        catch (e) {
            uxLog("warning", this, c.yellow(`[Bitbucket Integration] Error while uploading image in downloads section ${localImagePath}\n${e.message}`));
        }
        return null;
    }
}
//# sourceMappingURL=bitbucket.js.map