/* jscpd:ignore-start */
import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import c from 'chalk';
import fs from 'fs-extra';
import open from 'open';
import * as path from 'path';
import { createTempDir, execCommand, getCurrentGitBranch, git, gitHasLocalUpdates, normalizeFileStatusPath, uxLog, } from '../../../common/utils/index.js';
import { exportData } from '../../../common/utils/dataUtils.js';
import { forceSourcePull } from '../../../common/utils/deployUtils.js';
import { callSfdxGitDelta, getGitDeltaScope, selectTargetBranch } from '../../../common/utils/gitUtils.js';
import { prompts } from '../../../common/utils/prompts.js';
import { appendPackageXmlFilesContent, parseXmlFile, removePackageXmlFilesContent, writeXmlFile, } from '../../../common/utils/xmlUtils.js';
import { WebSocketClient } from '../../../common/websocketClient.js';
import { CONSTANTS, getApiVersion, getConfig, setConfig } from '../../../config/index.js';
import CleanReferences from '../project/clean/references.js';
import CleanXml from '../project/clean/xml.js';
import { GitProvider } from '../../../common/gitProvider/index.js';
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('sfdx-hardis', 'org');
export default class SaveTask extends SfCommand {
    static title = 'Save User Story';
    static description = `
## Command Behavior

**Guides the user through the process of saving their work, preparing it for a Merge Request (also named Pull Request), and pushing changes to the remote Git repository.**

This command automates several critical steps involved in finalizing a development User Story and integrating it into the main codebase. It ensures that your local changes are properly synchronized, cleaned, and committed before being pushed.

Key functionalities include:

- **Git Status Management:** Ensures a clean Git working directory by handling ongoing merges and unstaging files.
- **Org Synchronization (Optional):** Prompts the user to pull the latest metadata updates from their scratch org or source-tracked sandbox, ensuring local files reflect the org's state.
- **Package.xml Updates:** Automatically generates \`package.xml\` and \`destructiveChanges.xml\` files based on the Git delta between your current branch and the target branch, reflecting added, modified, and deleted metadata.
- **Automated Source Cleaning:** Applies predefined cleaning operations to your local Salesforce sources, such as removing unwanted references, minimizing profiles, or cleaning XML files based on configurations in your \`.sfdx-hardis.yml\`.
  - \`autoCleanTypes\`: A list of automated source cleanings, configurable via [hardis:project:clean:references]($\{CONSTANTS.DOC_URL_ROOT}/hardis/project/clean/references/).
  - \`autoRemoveUserPermissions\`: A list of user permissions to automatically remove from profile metadata.
- **Deployment Plan Generation:** Builds an automated deployment plan based on the updated \`package.xml\` and configured deployment splits.
- **Commit and Push:** Guides the user to commit the changes and push them to the remote Git repository, optionally handling force pushes if a branch reset occurred.
- **Merge Request Guidance:** Provides information and links to facilitate the creation of a merge request after the changes are pushed.

Example \`.sfdx-hardis.yml\` configuration:

\`\`\`yaml
autoCleanTypes:
  - checkPermissions
  - destructivechanges
  - datadotcom
  - minimizeProfiles
  - listViewsMine
autoRemoveUserPermissions:
  - EnableCommunityAppLauncher
  - FieldServiceAccess
  - OmnichannelInventorySync
  - SendExternalEmailAvailable
  - UseOmnichannelInventoryAPIs
  - ViewDataLeakageEvents
  - ViewMLModels
  - ViewPlatformEvents
  - WorkCalibrationUser
\`\`\`

Advanced instructions are available in the [Publish a User Story documentation]($\{CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-publish-task/).

<details markdown="1">
<summary>Technical explanations</summary>

The command's technical implementation involves a series of orchestrated steps:

- **Git Integration:** Extensively uses the \`git\` utility for status checks, adding files, committing, and pushing. It also leverages \`sfdx-git-delta\` for generating metadata differences between Git revisions.
- **Interactive Prompts:** Employs the \`prompts\` library to interact with the user for decisions like pulling sources or pushing commits.
- **Configuration Management:** Reads and updates project and user configurations using \`getConfig\` and \`setConfig\` to store preferences and deployment plans.
- **Metadata Synchronization:** Calls \`forceSourcePull\` to retrieve metadata from the org and \`callSfdxGitDelta\` to generate \`package.xml\` and \`destructiveChanges.xml\` based on Git changes.
- **XML Manipulation:** Utilizes \`appendPackageXmlFilesContent\`, \`removePackageXmlFilesContent\`, \`parseXmlFile\`, and \`writeXmlFile\` for modifying \`package.xml\` and \`destructiveChanges.xml\` files.
- **Automated Cleaning:** Integrates with \`CleanReferences.run\` and \`CleanXml.run\` commands to perform automated cleaning operations on the Salesforce source files.
- **Deployment Plan Building:** Dynamically constructs a deployment plan by analyzing the \`package.xml\` content and applying configured deployment splits.
- **WebSocket Communication:** Uses \`WebSocketClient.sendRefreshStatusMessage\` to notify connected VS Code clients about status updates.
- **External Tool Integration:** Requires the \`sfdx-git-delta\` plugin to be installed for its core functionality.
</details>
`;
    static examples = ['$ sf hardis:work:task:save', '$ sf hardis:work:task:save --nopull --nogit --noclean'];
    // public static args = [{name: 'file'}];
    static flags = {
        nopull: Flags.boolean({
            char: 'n',
            default: false,
            description: 'No scratch pull before save',
        }),
        nogit: Flags.boolean({
            char: 'g',
            default: false,
            description: 'No automated git operations',
        }),
        noclean: Flags.boolean({
            char: 'c',
            default: false,
            description: 'No cleaning of local sources',
        }),
        auto: Flags.boolean({
            default: false,
            description: 'No user prompts (when called from CI for example)',
        }),
        targetbranch: Flags.string({
            description: 'Name of the Merge Request target branch. Will be guessed or prompted if not provided.',
        }),
        debug: Flags.boolean({
            char: 'd',
            default: false,
            description: messages.getMessage('debugMode'),
        }),
        websocket: Flags.string({
            description: messages.getMessage('websocket'),
        }),
        skipauth: Flags.boolean({
            description: 'Skip authentication check when a default username is required',
        }),
        'target-org': requiredOrgFlagWithDeprecations,
    }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default
    static requiresProject = true;
    // List required plugins, their presence will be tested before running the command
    static requiresSfdxPlugins = ['sfdx-git-delta'];
    debugMode = false;
    noPull = false;
    noGit = false;
    noClean = false;
    auto = false;
    gitUrl;
    currentBranch;
    targetBranch;
    /* jscpd:ignore-end */
    async run() {
        const { flags } = await this.parse(SaveTask);
        this.noPull = flags.nopull || false;
        this.noGit = flags.nogit || false;
        this.noClean = flags.noclean || false;
        this.auto = flags.auto || false;
        this.targetBranch = flags.targetbranch || null;
        this.debugMode = flags.debug || false;
        const localBranch = (await getCurrentGitBranch()) || '';
        // Define current and target branches
        this.gitUrl = (await git().listRemote(['--get-url']))?.trim() || '';
        this.currentBranch = (await getCurrentGitBranch()) || '';
        if (this.targetBranch == null) {
            const userConfig = await getConfig('user');
            if (userConfig?.localStorageBranchTargets && userConfig?.localStorageBranchTargets[localBranch]) {
                this.targetBranch = userConfig?.localStorageBranchTargets[localBranch];
            }
        }
        if (this.targetBranch == null) {
            this.targetBranch = await selectTargetBranch({
                message: `Please select the target branch of your future ${GitProvider.getMergeRequestName(this.gitUrl)}`,
            });
        }
        // User log info
        uxLog("action", this, c.cyan(`This command will help to prepare the ${GitProvider.getMergeRequestName(this.gitUrl)} from your branch ${c.green(localBranch)} to major branch ${c.green(this.targetBranch)}`));
        // Make sure git is clean before starting operations
        await this.cleanGitStatus();
        // Make sure commit is ready before starting operations
        const orgPullStateRes = await this.ensureCommitIsReady(flags);
        if (orgPullStateRes && orgPullStateRes.outputString) {
            return orgPullStateRes;
        }
        // Update package.xml files using sfdx-git-delta
        const gitStatusWithConfig = await this.upgradePackageXmlFilesWithDelta();
        // Apply cleaning on sources
        await this.applyCleaningOnSources();
        // Build automated deployment plan
        const gitStatusAfterDeployPlan = await this.buildDeploymentPlan();
        // Push new commit(s)
        await this.manageCommitPush(gitStatusWithConfig, gitStatusAfterDeployPlan);
        const mergeRequestUrl = GitProvider.getMergeRequestCreateUrl(this.gitUrl, this.targetBranch || '', this.currentBranch);
        // Merge request
        uxLog("action", this, c.cyan(`If your work is ${c.bold('completed')}, you can create a ${c.bold(GitProvider.getMergeRequestName(this.gitUrl))}, otherwise you can push new commits on ${c.green(this.currentBranch)} branch.`));
        let summaryMsg = c.grey("");
        if (mergeRequestUrl) {
            if (WebSocketClient.isAliveWithLwcUI()) {
                WebSocketClient.sendReportFileMessage(mergeRequestUrl, `Create ${GitProvider.getMergeRequestName(this.gitUrl)}`, 'actionUrl');
            }
            else {
                summaryMsg += c.grey(`- New ${GitProvider.getMergeRequestName(this.gitUrl)} URL: ${c.green(mergeRequestUrl)}\n`);
            }
        }
        const mergeRequestDoc = `${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-publish-task/#create-merge-request`;
        summaryMsg += c.grey(`- Repository: ${c.green(this.gitUrl.replace('.git', ''))}\n`);
        summaryMsg += c.grey(`- Source branch: ${c.green(this.currentBranch)}\n`);
        summaryMsg += c.grey(`- Target branch: ${c.green(this.targetBranch)}`);
        uxLog("log", this, summaryMsg);
        uxLog("log", this, `${c.yellow(`When your ${GitProvider.getMergeRequestName(this.gitUrl)} will have been merged:`)}
- ${c.yellow('DO NOT REUSE THE SAME BRANCH')}
- Use New User Story menu (sf hardis:work:new), even if you work in the same sandbox or scratch org :)`);
        // Manual actions file
        const config = await getConfig('project');
        if (config.manualActionsFileUrl && config.manualActionsFileUrl !== '') {
            uxLog("warning", this, c.yellow(`If you have pre-deployment or post-deployment manual actions, make sure to write them in the file ${c.green(config.manualActionsFileUrl)}`));
            if (WebSocketClient.isAliveWithLwcUI()) {
                WebSocketClient.sendReportFileMessage(config.manualActionsFileUrl, `Manual Actions file`, 'docUrl');
            }
        }
        else {
            uxLog("warning", this, c.yellow(`You should have a manual actions file defined. Ask your release manager to create one for the project and set its link in your .sfdx-hardis.yml file under manualActionsFileUrl property.`));
        }
        if (!WebSocketClient.isAliveWithLwcUI()) {
            uxLog("log", this, c.grey(`${GitProvider.getMergeRequestName(this.gitUrl)} documentation is available here -> ${c.bold(mergeRequestDoc)}`));
        }
        WebSocketClient.sendReportFileMessage(mergeRequestDoc, `View ${GitProvider.getMergeRequestName(this.gitUrl)} documentation`, 'docUrl');
        // Return an object to be displayed with --json
        return { outputString: 'Saved the User Story' };
    }
    // Clean git status
    async cleanGitStatus() {
        // Skip git stuff if requested
        if (this.noGit) {
            uxLog("action", this, c.cyan(`[Expert mode] Skipped git reset`));
            return;
        }
        let gitStatusInit = await git().status();
        // Cancel merge if ongoing merge
        if (gitStatusInit.conflicted.length > 0) {
            await git({ output: true }).merge(['--abort']);
            gitStatusInit = await git().status();
        }
        // Unstage files
        if (gitStatusInit.staged.length > 0) {
            await execCommand('git reset', this, { output: true, fail: true });
        }
    }
    async ensureCommitIsReady(flags) {
        // Manage project deploy start from scratch org
        if (this.noPull || this.auto) {
            // Skip pull
            uxLog("action", this, c.cyan(`Skipped sf project:retrieve:start from scratch org`));
            return;
        }
        // Request user if commit is ready
        const commitReadyRes = await prompts({
            type: 'select',
            name: 'value',
            message: c.cyanBright('Have you already committed the updated metadata you want to deploy ?'),
            description: 'Select your current state regarding git commits and metadata updates',
            placeholder: 'Select commit status',
            choices: [
                {
                    title: '😎 Yes, my commit(s) is ready ! I staged my files then created one or multiple commits !',
                    value: 'commitReady',
                    description: "You have already pulled updates from your org (or locally updated the files if you're a nerd) then staged your files and created a commit",
                },
                {
                    title: '😐 No, please pull my latest updates from my org so I can commit my metadatas',
                    value: 'pleasePull',
                    description: 'Pull latest updates from org so then you can stage files and create your commit',
                },
                {
                    title: '😱 What is a commit ? What does mean pull ? Help !',
                    value: 'help',
                    description: "Don't panic, just click on the link that will appear in the console (CTRL + Click) and then you will know :)",
                },
            ],
        });
        if (commitReadyRes.value === 'pleasePull') {
            // Process sf project retrieve start
            uxLog("action", this, c.cyan(`Pulling sources from scratch org ${flags['target-org'].getUsername()}...`));
            await forceSourcePull(flags['target-org'].getUsername(), this.debugMode);
            uxLog("action", this, c.cyan(`Sources has been pulled from ${flags['target-org'].getUsername()}, now you can stage and commit your updates !`));
            WebSocketClient.sendReportFileMessage("workbench.view.scm", "Commit your retrieved files", "actionCommand");
            WebSocketClient.sendReportFileMessage(`${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-publish-task/#commit-your-updates`, "Retrieve and Commit documentation", 'docUrl');
            return { outputString: 'Pull performed' };
        }
        else if (commitReadyRes.value === 'help') {
            // Show pull commit stage help
            const commitHelpUrl = `${CONSTANTS.DOC_URL_ROOT}/hardis/scratch/pull/`;
            uxLog("action", this, c.cyan(`Opening help at ${commitHelpUrl} ...`));
            await open(commitHelpUrl, { wait: true });
            return { outputString: 'Help displayed at ' };
        }
        // Extract data from org
        const dataSources = [
            {
                label: 'Email templates',
                dataPath: './scripts/data/EmailTemplate',
            },
        ];
        for (const dataSource of dataSources) {
            if (fs.existsSync(dataSource.dataPath)) {
                const exportDataRes = await prompts({
                    type: 'confirm',
                    name: 'value',
                    message: c.cyan(`Did you update ${c.green(dataSource.label)} and want to export related data ?`),
                    description: 'Confirm if you want to export data that may have been updated for this data source',
                });
                if (exportDataRes.value === true) {
                    await exportData(dataSource.dataPath, this, {
                        sourceUsername: flags['target-org'].getUsername(),
                    });
                }
            }
        }
    }
    async upgradePackageXmlFilesWithDelta() {
        uxLog("action", this, c.cyan('Updating manifest/package.xml and manifest/destructiveChanges.xml using sfdx-git-delta...'));
        // Retrieving info about current branch latest commit and master branch latest commit
        const gitDeltaScope = await getGitDeltaScope(this.currentBranch, this.targetBranch || '');
        // Build package.xml delta between most recent commit and developpement
        const localPackageXml = path.join('manifest', 'package.xml');
        const toCommitMessage = gitDeltaScope.toCommit ? gitDeltaScope.toCommit.message : '';
        uxLog("log", this, c.grey(`Calculating package.xml diff from [${c.green(this.targetBranch)}] to [${c.green(this.currentBranch)} - ${c.green(toCommitMessage)}]`));
        const tmpDir = await createTempDir();
        const packageXmlResult = await callSfdxGitDelta(gitDeltaScope.fromCommit, gitDeltaScope.toCommit ? gitDeltaScope.toCommit.hash : gitDeltaScope.fromCommit, tmpDir);
        if (packageXmlResult.status === 0) {
            // Upgrade local destructivePackage.xml
            const localDestructiveChangesXml = path.join('manifest', 'destructiveChanges.xml');
            if (!fs.existsSync(localDestructiveChangesXml)) {
                // Create default destructiveChanges.xml if not defined
                const blankDestructiveChanges = `<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <version>${getApiVersion()}</version>
</Package>
`;
                await fs.writeFile(localDestructiveChangesXml, blankDestructiveChanges);
            }
            const diffDestructivePackageXml = path.join(tmpDir, 'destructiveChanges', 'destructiveChanges.xml');
            const destructivePackageXmlDiffStr = await fs.readFile(diffDestructivePackageXml, 'utf8');
            uxLog("log", this, c.grey(c.bold(`Delta destructiveChanges.xml diff to be merged within ${c.green(localDestructiveChangesXml)}:\n`)) +
                c.red(destructivePackageXmlDiffStr));
            await appendPackageXmlFilesContent([localDestructiveChangesXml, diffDestructivePackageXml], localDestructiveChangesXml);
            if ((await gitHasLocalUpdates()) && !this.noGit) {
                await git().add(localDestructiveChangesXml);
            }
            // Upgrade local package.xml
            const diffPackageXml = path.join(tmpDir, 'package', 'package.xml');
            const packageXmlDiffStr = await fs.readFile(diffPackageXml, 'utf8');
            uxLog("log", this, c.grey(c.bold(`Delta package.xml diff to be merged within ${c.green(localPackageXml)}:\n`)) +
                c.green(packageXmlDiffStr));
            await appendPackageXmlFilesContent([localPackageXml, diffPackageXml], localPackageXml);
            await removePackageXmlFilesContent(localPackageXml, localDestructiveChangesXml, {
                outputXmlFile: localPackageXml,
            });
            if ((await gitHasLocalUpdates()) && !this.noGit) {
                await git().add(localPackageXml);
            }
        }
        else {
            uxLog("log", this, `[error] ${c.grey(JSON.stringify(packageXmlResult))}`);
            uxLog("error", this, c.red(`Unable to build git diff.${c.yellow(c.bold('Please update package.xml and destructiveChanges.xml manually'))}`));
        }
        // Commit updates
        let gitStatusWithConfig = await git().status();
        if (gitStatusWithConfig.staged.length > 0 && !this.noGit) {
            uxLog("log", this, c.grey(`Committing updated files in local git branch ${c.green(this.currentBranch)}...`));
            try {
                await git({ output: true }).commit('[sfdx-hardis] Update package content');
            }
            catch (e) {
                uxLog("warning", this, c.yellow(`There may be an issue while committing files but it can be ok to ignore it\n${c.grey(e.message)}`));
                gitStatusWithConfig = await git().status();
            }
        }
        return gitStatusWithConfig;
    }
    // Apply automated cleaning to avoid to have to do it manually
    async applyCleaningOnSources() {
        const config = await getConfig('branch');
        if (!this.noClean) {
            const gitStatusFilesBeforeClean = (await git().status()).files.map((file) => file.path);
            uxLog("other", this, JSON.stringify(gitStatusFilesBeforeClean, null, 2));
            // References cleaning
            await CleanReferences.run(['--type', 'all']);
            if (globalThis?.displayProfilesWarning === true) {
                uxLog("warning", this, c.yellow(c.bold('Please make sure the attributes removed from Profiles are defined on Permission Sets :)')));
            }
            // Xml cleaning
            if (config.cleanXmlPatterns && config.cleanXmlPatterns.length > 0) {
                uxLog("action", this, c.cyan('Cleaning sfdx project using patterns and xpaths defined in cleanXmlPatterns...'));
                await CleanXml.run([]);
            }
            // Manage git after cleaning
            const gitStatusAfterClean = await git().status();
            uxLog("other", this, JSON.stringify(gitStatusAfterClean, null, 2));
            const cleanedFiles = gitStatusAfterClean.files
                .filter((file) => !gitStatusFilesBeforeClean.includes(file.path))
                .map((file) => normalizeFileStatusPath(file.path, config));
            if (cleanedFiles.length > 0) {
                uxLog("log", this, c.grey(`Cleaned the following list of files:\n${cleanedFiles.join('\n')}`));
                if (!this.noGit) {
                    try {
                        await git().add(cleanedFiles);
                        await git({ output: true }).commit('[sfdx-hardis] Clean sfdx project');
                    }
                    catch (e) {
                        uxLog("warning", this, c.yellow(`There may be an issue while adding cleaned files but it can be ok to ignore it\n${c.grey(e.message)}`));
                    }
                }
            }
        }
    }
    async buildDeploymentPlan() {
        // Build deployment plan splits
        let splitConfig = await this.getSeparateDeploymentsConfig();
        const localPackageXml = path.join('manifest', 'package.xml');
        const packageXml = await parseXmlFile(localPackageXml);
        for (const type of packageXml.Package.types || []) {
            const typeName = type.name[0];
            splitConfig = splitConfig.map((split) => {
                if (split.types.includes(typeName) && type.members[0] !== '*') {
                    split.content[typeName] = type.members;
                }
                return split;
            });
        }
        // Generate deployment plan items
        const config = await getConfig('project');
        const deploymentPlan = config?.deploymentPlan || {};
        let packages = deploymentPlan?.packages || [];
        const blankPackageXml = packageXml;
        blankPackageXml.Package.types = [];
        for (const split of splitConfig) {
            if (Object.keys(split.content).length > 0) {
                // data case
                if (split.data) {
                    const label = `Import ${split.types.join('-')} records`;
                    packages = this.addToPlan(packages, {
                        label: label,
                        dataPath: split.data,
                        order: split.dataPos,
                        waitAfter: split.waitAfter,
                    });
                }
                // single split file case
                if (split.file) {
                    const splitPackageXml = blankPackageXml;
                    blankPackageXml.Package.types = [];
                    for (const type of Object.keys(split.content)) {
                        splitPackageXml.Package.types.push({
                            name: [type],
                            members: split.content[type],
                        });
                    }
                    await writeXmlFile(split.file, splitPackageXml);
                    const label = `Deploy ${split.types.join('-')}`;
                    packages = this.addToPlan(packages, {
                        label: label,
                        packageXmlFile: split.file,
                        order: split.filePos,
                        waitAfter: split.waitAfter,
                    });
                }
                // Multiple split file case
                if (split.files) {
                    let pos = split.filePos;
                    for (const mainTypeMember of split.content[split.mainType] || []) {
                        const splitFile = split.files.replace(`{{name}}`, mainTypeMember);
                        const splitPackageXml = blankPackageXml;
                        blankPackageXml.Package.types = [];
                        for (const type of Object.keys(split.content)) {
                            if (type !== split.mainType) {
                                const filteredMembers = split.content[type].filter((member) => member.includes(`${mainTypeMember}.`));
                                splitPackageXml.Package.types.push({
                                    name: [type],
                                    members: filteredMembers,
                                });
                            }
                        }
                        splitPackageXml.Package.types.push({
                            name: [split.mainType],
                            members: [mainTypeMember],
                        });
                        await writeXmlFile(splitFile, splitPackageXml);
                        const label = `Deploy ${split.mainType} - ${mainTypeMember}`;
                        packages = this.addToPlan(packages, {
                            label: label,
                            packageXmlFile: splitFile,
                            order: pos,
                            waitAfter: split.waitAfter,
                        });
                        pos++;
                    }
                }
            }
        }
        // Update deployment plan in config
        deploymentPlan.packages = packages.sort((a, b) => (a.order > b.order ? 1 : -1));
        await setConfig('project', { deploymentPlan: deploymentPlan });
        if (!this.noGit) {
            await git({ output: true }).add(['./config']);
            await git({ output: true }).add(['./manifest']);
        }
        let gitStatusAfterDeployPlan = await git().status();
        if (gitStatusAfterDeployPlan.staged.length > 0 && !this.noGit) {
            try {
                await git({ output: true }).commit('[sfdx-hardis] Update deployment plan');
            }
            catch (e) {
                uxLog("warning", this, c.yellow(`There may be an issue while committing files but it can be ok to ignore it\n${c.grey(e.message)}`));
                gitStatusAfterDeployPlan = await git().status();
            }
        }
        return gitStatusAfterDeployPlan;
    }
    // Manage push from user
    async manageCommitPush(gitStatusWithConfig, gitStatusAfterDeployPlan) {
        if ((gitStatusWithConfig.staged.length > 0 ||
            gitStatusAfterDeployPlan.staged.length > 0 ||
            gitStatusAfterDeployPlan?.ahead > 0 ||
            gitStatusAfterDeployPlan.tracking == null) &&
            !this.noGit &&
            !this.auto) {
            const pushResponse = await prompts({
                type: 'confirm',
                name: 'push',
                default: true,
                message: c.cyanBright(`Do you want to push your commit(s) on git server ? (git push in remote git branch ${c.green(this.currentBranch)})`),
                description: 'Choose whether to push your commits to the remote git repository',
            });
            if (pushResponse.push === true) {
                uxLog("action", this, c.cyan(`Pushing new commit(s) in remote git branch ${c.green(`origin/${this.currentBranch}`)}...`));
                const configUSer = await getConfig('user');
                let pushResult;
                if (configUSer.canForcePush === true) {
                    // Force push if hardis:work:resetselection has been called before
                    pushResult = await git({ output: true }).push(['-u', 'origin', this.currentBranch, '--force']);
                    await setConfig('user', { canForcePush: false });
                }
                else {
                    pushResult = await git({ output: true }).push(['-u', 'origin', this.currentBranch]);
                }
                // Update merge request info
                if (pushResult && pushResult.remoteMessages) {
                    let mergeRequestsStored = configUSer.mergeRequests || [];
                    if (mergeRequestsStored.filter((mergeRequest) => mergeRequest?.branch === this.currentBranch).length === 1) {
                        mergeRequestsStored = mergeRequestsStored.map((mergeRequestStored) => {
                            if (mergeRequestStored?.branch === this.currentBranch) {
                                return this.updateMergeRequestInfo(mergeRequestStored, pushResult);
                            }
                        });
                    }
                    else {
                        mergeRequestsStored.push(this.updateMergeRequestInfo({ branch: this.currentBranch }, pushResult));
                    }
                    // Update user config file & send Websocket event
                    await setConfig('user', { mergeRequests: mergeRequestsStored.filter((mr) => mr !== null) });
                    WebSocketClient.sendRefreshStatusMessage();
                }
            }
        }
    }
    updateMergeRequestInfo(mergeRequestStored, mergeRequestInfo) {
        if (this.debugMode) {
            uxLog("log", this, c.grey(JSON.stringify(mergeRequestInfo, null, 2)));
        }
        if (mergeRequestInfo?.remoteMessages?.id) {
            mergeRequestStored.id = mergeRequestInfo.remoteMessages.id;
        }
        else {
            delete mergeRequestStored.id;
        }
        if (mergeRequestInfo?.remoteMessages?.pullRequestUrl) {
            mergeRequestStored.urlCreate = mergeRequestInfo.remoteMessages.pullRequestUrl;
        }
        else {
            delete mergeRequestStored.urlCreate;
        }
        if (mergeRequestInfo?.remoteMessages?.all[0] &&
            mergeRequestInfo?.remoteMessages?.all[0].includes('View merge request')) {
            mergeRequestStored.url = mergeRequestInfo?.remoteMessages?.all[1];
        }
        else {
            delete mergeRequestStored.url;
        }
        return mergeRequestStored;
    }
    async getSeparateDeploymentsConfig() {
        const config = await getConfig('project');
        if (config.separateDeploymentsConfig || config.separateDeploymentsConfig === false) {
            return config.separateDeploymentConfig || [];
        }
        const separateDeploymentConfig = [
            /*  NV: Commented because seems to be now useless
           {
              types: ["EmailTemplate"],
              file: "manifest/splits/packageXmlEmails.xml",
              filePos: -20,
              data: "scripts/data/EmailTemplate",
              dataPos: -21,
              content: {},
            },
            {
              types: ["Flow", "Workflow"],
              file: "manifest/splits/packageXmlFlowWorkflow.xml",
              filePos: 6,
              content: {},
            }, */
            {
                types: ['SharingRules', 'SharingOwnerRule'],
                files: 'manifest/splits/packageXmlSharingRules{{name}}.xml',
                filePos: 30,
                mainType: 'SharingRules',
                waitAfter: 30,
                content: {},
            },
        ];
        return separateDeploymentConfig;
    }
    // Add item to .sfdx-hardis.yml deploymentPlan property
    addToPlan(packages, item) {
        let updated = false;
        if (item.waitAfter === null) {
            delete item.waitAfter;
        }
        packages = packages.map((pckg) => {
            if (pckg.label === item.label) {
                pckg = item;
                updated = true;
            }
            return pckg;
        });
        if (updated === false) {
            packages.push(item);
        }
        return packages;
    }
}
//# sourceMappingURL=save.js.map