import c from "chalk";
import { getCurrentGitBranch, isCI, uxLog } from "../utils/index.js";
import { AzureDevopsProvider } from "./azureDevops.js";
import { GithubProvider } from "./github.js";
import { GitlabProvider } from "./gitlab.js";
import { BitbucketProvider } from "./bitbucket.js";
import Debug from "debug";
import { CONSTANTS, getEnvVar } from "../../config/index.js";
import { prompts } from "../utils/prompts.js";
import { removeMermaidLinks } from "../utils/mermaidUtils.js";
const debug = Debug("sfdxhardis");
export class GitProvider {
    static async getInstance(prompt = false) {
        // Azure
        if (process.env.SYSTEM_ACCESSTOKEN) {
            const serverUrl = process.env.SYSTEM_COLLECTIONURI || null;
            // a Personal Access Token must be defined
            const token = process.env.CI_SFDX_HARDIS_AZURE_TOKEN || process.env.SYSTEM_ACCESSTOKEN || null;
            if (serverUrl == null || token == null) {
                uxLog("warning", this, c.yellow(`To benefit from Azure Pipelines advanced integration, you need to define the following variables as ENV vars:
- SYSTEM_COLLECTIONURI
- SYSTEM_ACCESSTOKEN or CI_SFDX_HARDIS_AZURE_TOKEN`));
                return null;
            }
            return new AzureDevopsProvider();
        }
        // Gitlab
        else if (process.env.CI_JOB_TOKEN) {
            const token = process.env.CI_SFDX_HARDIS_GITLAB_TOKEN || process.env.ACCESS_TOKEN || null;
            if (token == null) {
                uxLog("warning", this, c.yellow(`To benefit from Gitlab advanced integration, you need to :
- Go to Settings -> Access tokens -> create a project token named "SFDX HARDIS BOT" with developer access and scope "api", then copy its value
- Go to Settings -> CI/CD -> Variables -> Create a masked variable named CI_SFDX_HARDIS_GITLAB_TOKEN, and paste the access token value`));
                return null;
            }
            return new GitlabProvider();
        }
        // Github
        else if (process.env.GITHUB_TOKEN) {
            return new GithubProvider();
        }
        // Bitbucket
        else if (process.env.BITBUCKET_WORKSPACE) {
            const token = process.env.CI_SFDX_HARDIS_BITBUCKET_TOKEN || null;
            if (token == null) {
                uxLog("warning", this, c.yellow(`To benefit from Bitbucket advanced integration, you need to :
- Go to Repository Settings -> Access Tokens -> Create a repository access token with the scopes pullrequest, pullrequest:write, repository, repository:write and copy its value
- Go to Repository Settings -> Repository Variables -> Create a variable named CI_SFDX_HARDIS_BITBUCKET_TOKEN and paste the access token value`));
                return null;
            }
            return new BitbucketProvider();
        }
        // If prompt allowed and no vars found, request to user
        else if (prompt && !isCI) {
            await GitProvider.handleManualGitServerAuth();
            return this.getInstance(false);
        }
        else if (isCI) {
            uxLog("log", this, c.grey("To use sfdx-hardis GitProvider capabilities, SYSTEM_ACCESSTOKEN, CI_JOB_TOKEN, GITHUB_TOKEN or CI_SFDX_HARDIS_BITBUCKET_TOKEN must be accessible for Azure Pipelines, Gitlab, GitHub or Bitbucket"));
        }
        return null;
    }
    static async handleManualGitServerAuth() {
        const promptRes = await prompts({
            message: "Please select your Git Service Provider",
            description: "Choose your git hosting service to enable CI/CD integration features",
            type: "select",
            choices: [
                { title: "Azure DevOps", value: "azure" },
                { title: "GitHub", value: "github" },
                { title: "Gitlab", value: "gitlab" },
                { title: "Bitbucket", value: "bitbucket" },
            ]
        });
        if (promptRes.value === "azure") {
            await AzureDevopsProvider.handleLocalIdentification();
        }
        else {
            uxLog("warning", this, c.yellow(`[GitProvider] Local authentication is not yet implemented for ${promptRes.value}`));
        }
    }
    static async managePostPullRequestComment() {
        const gitProvider = await GitProvider.getInstance();
        if (gitProvider == null) {
            uxLog("warning", this, c.yellow("[Git Provider] WARNING: No git provider found to post pull request comment. Maybe you should configure it ?"));
            uxLog("warning", this, c.yellow(`[Git Provider] See documentation: ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integrations-home/#git-providers`));
            return;
        }
        const prData = globalThis.pullRequestData;
        const prCommentSent = globalThis.pullRequestCommentSent || false;
        if (prData && gitProvider && prCommentSent === false) {
            uxLog("warning", this, c.yellow("[Git Provider] Try to post a pull request comment/note..."));
            let markdownBody = "";
            if (prData.deployErrorsMarkdownBody) {
                markdownBody += prData.deployErrorsMarkdownBody;
            }
            if (prData.codeCoverageMarkdownBody) {
                markdownBody += "\n\n" + prData.codeCoverageMarkdownBody;
            }
            if (prData.commitsSummary) {
                markdownBody += "\n\n" + prData.commitsSummary;
            }
            if (prData?.flowDiffMarkdown?.markdownSummary) {
                markdownBody += "\n\n" + prData.flowDiffMarkdown.markdownSummary;
            }
            markdownBody = removeMermaidLinks(markdownBody); // Remove "click" elements that are useless and ugly on some providers :)
            const prMessageRequest = {
                title: prData.title || '',
                message: markdownBody,
                status: prData.status || 'tovalidate',
                messageKey: prData.messageKey || '',
            };
            // Post main message
            const postResult = await gitProvider.tryPostPullRequestMessage(prMessageRequest);
            if (postResult && postResult.posted === true) {
                globalThis.pullRequestCommentSent = true;
            }
            // Post additional comments
            for (const flowDiff of prData?.flowDiffMarkdown?.flowDiffMarkdownList || []) {
                const flowDiffMessage = removeMermaidLinks(flowDiff.markdown); // Remove "click" elements that are useless and ugly on some providers :)
                const prMessageRequestAdditional = {
                    title: `Differences for Flow ${flowDiff.name}`,
                    message: flowDiffMessage,
                    status: "valid",
                    messageKey: `sfdx-hardis-flow-diff-${flowDiff.name}`,
                    sourceFile: flowDiff.markdownFile,
                };
                await gitProvider.tryPostPullRequestMessage(prMessageRequestAdditional);
            }
        }
        else {
            uxLog("error", this, c.grey(`${JSON.stringify(prData || { noPrData: "" })} && ${gitProvider} && ${prCommentSent}`));
            uxLog("warning", this, c.yellow("[Git Provider] Skip post pull request comment"));
        }
    }
    static async getDeploymentCheckId() {
        const gitProvider = await GitProvider.getInstance();
        if (gitProvider == null) {
            return null;
        }
        try {
            // Exotic way: get deployment check Id from current Pull Request: https://github.com/hardisgroupcom/sfdx-hardis/issues/637
            if (this.isDeployBeforeMerge()) {
                return gitProvider.getPullRequestDeploymentCheckId();
            }
            // Classic way: get deployment check Id from latest merged Pull Request
            const currentGitBranch = await getCurrentGitBranch() || "";
            return gitProvider.getBranchDeploymentCheckId(currentGitBranch);
        }
        catch (e) {
            uxLog("warning", this, c.yellow(`Error while trying to retrieve deployment check id:\n${e.message}`));
            return null;
        }
    }
    static async getCurrentBranchUrl() {
        const gitProvider = await GitProvider.getInstance();
        if (gitProvider == null) {
            return null;
        }
        return gitProvider.getCurrentBranchUrl();
    }
    static async getJobUrl() {
        const gitProvider = await GitProvider.getInstance();
        if (gitProvider == null) {
            return null;
        }
        return gitProvider.getCurrentJobUrl();
    }
    static async supportsMermaidInPrMarkdown() {
        const gitProvider = await GitProvider.getInstance();
        if (gitProvider == null) {
            return false;
        }
        return gitProvider.supportsMermaidInPrMarkdown();
    }
    static async supportsSvgAttachments() {
        const gitProvider = await GitProvider.getInstance();
        if (gitProvider == null) {
            return false;
        }
        return gitProvider.supportsSvgAttachments();
    }
    static prInfoCache = null;
    static async getPullRequestInfo(options = { useCache: false }) {
        // Return cached result if available and caching is enabled
        if (options.useCache && GitProvider.prInfoCache !== null) {
            debug("[PR Info] Returning cached pull request info");
            return GitProvider.prInfoCache;
        }
        const gitProvider = await GitProvider.getInstance();
        if (gitProvider == null) {
            debug("[PR Info] No GitProvider instance found");
            return null;
        }
        let prInfo = null;
        try {
            prInfo = await gitProvider.getPullRequestInfo();
            debug("[GitProvider][PR Info] " + JSON.stringify(prInfo, null, 2));
            GitProvider.prInfoCache = prInfo;
        }
        catch (e) {
            uxLog("warning", this, c.yellow("[GitProvider] Unable to get Pull Request info: " + e.message));
            uxLog("warning", this, c.yellow(`[GitProvider] Maybe you misconfigured your ${gitProvider.getLabel()} ?`));
            uxLog("warning", this, c.yellow(`[GitProvider] See ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integrations-home/#git-providers`));
            prInfo = null;
        }
        return prInfo;
    }
    static isDeployBeforeMerge() {
        const deployBeforeMerge = getEnvVar("SFDX_HARDIS_DEPLOY_BEFORE_MERGE") || false;
        return [true, "true"].includes(deployBeforeMerge);
    }
    static getMergeRequestName(gitUrl) {
        if (gitUrl.includes("gitlab")) {
            return "Merge Request";
        }
        // Default fallback
        return "Pull Request";
    }
    static getMergeRequestCreateUrl(gitUrl, targetBranch, sourceBranch) {
        const gitUrlHttp = gitUrl.replace(".git", "").trim();
        // GitLab
        if (gitUrlHttp.includes("gitlab")) {
            // https://gitlab.com/group/project/-/merge_requests/new?merge_request[source_branch]=feature&merge_request[target_branch]=main
            return `${gitUrlHttp}/-/merge_requests/new?merge_request[source_branch]=${encodeURIComponent(sourceBranch)}&merge_request[target_branch]=${encodeURIComponent(targetBranch)}`;
        }
        // GitHub
        if (gitUrlHttp.includes("github")) {
            // https://github.com/org/repo/compare/main...feature?expand=1
            return `${gitUrlHttp}/compare/${encodeURIComponent(targetBranch)}...${encodeURIComponent(sourceBranch)}?expand=1`;
        }
        // Gitea (common pattern)
        if (gitUrlHttp.includes("gitea")) {
            // https://gitea.example.com/org/repo/compare/main...feature
            return `${gitUrlHttp}/compare/${encodeURIComponent(targetBranch)}...${encodeURIComponent(sourceBranch)}`;
        }
        // Azure DevOps
        if (gitUrlHttp.includes("dev.azure.com")) {
            // https://dev.azure.com/org/project/_git/repo/pullrequestcreate?sourceRef=feature&targetRef=main
            // Try to extract the repo path after _git/
            const match = gitUrlHttp.match(/dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\/([^/]+)/);
            if (match) {
                const org = match[1];
                const project = match[2];
                const repo = match[3];
                return `https://dev.azure.com/${org}/${project}/_git/${repo}/pullrequestcreate?sourceRef=${encodeURIComponent(sourceBranch)}&targetRef=${encodeURIComponent(targetBranch)}`;
            }
        }
        // Bitbucket (cloud)
        if (gitUrlHttp.includes("bitbucket.org")) {
            // https://bitbucket.org/org/repo/pull-requests/new?source=feature&dest=main
            return `${gitUrlHttp}/pull-requests/new?source=${encodeURIComponent(sourceBranch)}&dest=${encodeURIComponent(targetBranch)}`;
        }
        // Bitbucket (server/DC)
        if (gitUrlHttp.includes("/scm/")) {
            // e.g. http://bitbucket.example.com/scm/project/repo
            // https://bitbucket.example.com/projects/PROJECT/repos/REPO/pull-requests?create&sourceBranch=feature&targetBranch=main
            const match = gitUrlHttp.match(/\/scm\/([^/]+)\/([^/]+)/);
            if (match) {
                const project = match[1];
                const repo = match[2];
                return gitUrlHttp.replace(/\/scm\/[^/]+\/[^/]+$/, `/projects/${project.toUpperCase()}/repos/${repo}/pull-requests?create&sourceBranch=${encodeURIComponent(sourceBranch)}&targetBranch=${encodeURIComponent(targetBranch)}`);
            }
        }
        // Fallback: just return null
        return null;
    }
}
//# sourceMappingURL=index.js.map