//#region imports
import { Helpers__NS__error, Helpers__NS__info, Helpers__NS__logInfo, Helpers__NS__logWarn, Helpers__NS__questionYesNo, Helpers__NS__tryRemoveDir, Helpers__NS__warn } from 'tnp-core/lib-prod';
import { chalk, dateformat, fse, Utils__NS__uniqArray } from 'tnp-core/lib-prod';
import { crossPlatformPath, path, ___NS__kebabCase, UtilsTerminal__NS__input, UtilsTerminal__NS__pressAnyKeyToContinueAsync, UtilsTerminal__NS__select } from 'tnp-core/lib-prod';
import { GlobalTaskManager } from 'tnp-core/lib-prod';
import { CommitData, HelpersTaon__NS__consoleGui__NS__question__NS__yesNo, HelpersTaon__NS__git__NS__allOrigins, HelpersTaon__NS__git__NS__backupBranch, HelpersTaon__NS__git__NS__changeRemoteFromHttpsToSSh, HelpersTaon__NS__git__NS__changeRemoveFromSshToHttps, HelpersTaon__NS__git__NS__changesSummary, HelpersTaon__NS__git__NS__checkIfthereAreSomeUncommitedChange, HelpersTaon__NS__git__NS__checkout, HelpersTaon__NS__git__NS__checkoutFromTo, HelpersTaon__NS__git__NS__checkTagExists, HelpersTaon__NS__git__NS__cleanRepoFromAnyFilesExceptDotGitFolder, HelpersTaon__NS__git__NS__clone, HelpersTaon__NS__git__NS__commit, HelpersTaon__NS__git__NS__countCommits, HelpersTaon__NS__git__NS__currentBranchName, HelpersTaon__NS__git__NS__fetch, HelpersTaon__NS__git__NS__getACTION_MSG_RESET_GIT_HARD_COMMIT, HelpersTaon__NS__git__NS__getBranchesNames, HelpersTaon__NS__git__NS__getChangedFilesInCommitByHash, HelpersTaon__NS__git__NS__getChangedFilesInCommitByIndex, HelpersTaon__NS__git__NS__getCommitHashByIndex, HelpersTaon__NS__git__NS__getCommitMessageByHash, HelpersTaon__NS__git__NS__getCommitMessageByIndex, HelpersTaon__NS__git__NS__getListOfCurrentGitChanges, HelpersTaon__NS__git__NS__getOriginURL, HelpersTaon__NS__git__NS__getRemoteProvider, HelpersTaon__NS__git__NS__getUserInfo, HelpersTaon__NS__git__NS__hasAnyCommits, HelpersTaon__NS__git__NS__isGitRoot, HelpersTaon__NS__git__NS__isInMergeProcess, HelpersTaon__NS__git__NS__isInsideGitRepo, HelpersTaon__NS__git__NS__lastCommitDate, HelpersTaon__NS__git__NS__lastCommitHash, HelpersTaon__NS__git__NS__lastCommitMessage, HelpersTaon__NS__git__NS__lastTagHash, HelpersTaon__NS__git__NS__lastTagNameForMajorVersion, HelpersTaon__NS__git__NS__lastTagVersionName, HelpersTaon__NS__git__NS__meltActionCommits, HelpersTaon__NS__git__NS__penultimateCommitHash, HelpersTaon__NS__git__NS__penultimateCommitMessage, HelpersTaon__NS__git__NS__pullCurrentBranch, HelpersTaon__NS__git__NS__pushCurrentBranch, HelpersTaon__NS__git__NS__rebase, HelpersTaon__NS__git__NS__resetFiles, HelpersTaon__NS__git__NS__resetHard, HelpersTaon__NS__git__NS__resetSoftHEAD, HelpersTaon__NS__git__NS__restoreLastVersion, HelpersTaon__NS__git__NS__revertFileChanges, HelpersTaon__NS__git__NS__setUserInfos, HelpersTaon__NS__git__NS__stageAllAndCommit, HelpersTaon__NS__git__NS__stageAllFiles, HelpersTaon__NS__git__NS__stagedFiles, HelpersTaon__NS__git__NS__stageFile, HelpersTaon__NS__git__NS__stash, HelpersTaon__NS__git__NS__stashApply, HelpersTaon__NS__git__NS__tagAndPushToGitRepo, HelpersTaon__NS__git__NS__thereAreSomeUncommitedChangeExcept, HelpersTaon__NS__git__NS__uncommitedFiles, HelpersTaon__NS__git__NS__unstageAllFiles, HelpersTaon__NS__pressKeyAndContinue } from '../../index';
import { PULL_ACTION_NAME, PUSH_ACTION_NAME } from '../constants';
import { translate } from '../translate';
import { BaseFeatureForProject } from './base-feature-for-project';
//#endregion
export class BaseGit extends BaseFeatureForProject {
    //#region methods & getters / unstage all files
    unstageAllFiles() {
        //#region @backendFunc
        HelpersTaon__NS__git__NS__unstageAllFiles(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / revert file changes
    revertFileChanges(fileReletivePath) {
        //#region @backendFunc
        HelpersTaon__NS__git__NS__revertFileChanges(this.project.location, fileReletivePath);
        //#endregion
    }
    //#endregion
    //#region methods & getters / clone
    async clone(url, destinationFolderName = '', branchName) {
        //#region @backendFunc
        const clondeFolderpath = await HelpersTaon__NS__git__NS__clone({
            cwd: this.project.location,
            url,
            destinationFolderName,
        });
        if (branchName) {
            try {
                HelpersTaon__NS__git__NS__checkout(clondeFolderpath, branchName);
                await HelpersTaon__NS__git__NS__pullCurrentBranch(clondeFolderpath, {
                    askToRetry: true,
                });
            }
            catch (error) { }
        }
        return crossPlatformPath([
            clondeFolderpath,
            destinationFolderName || '',
        ]).replace(/\/$/g, '');
        //#endregion
    }
    //#endregion
    //#region methods & getters / restore last version
    restoreLastVersion(relativeFilePath) {
        //#region @backendFunc
        HelpersTaon__NS__git__NS__restoreLastVersion(this.project.location, relativeFilePath);
        //#endregion
    }
    //#endregion
    //#region methods & getters / stage all files
    stageAllFiles() {
        //#region @backendFunc
        HelpersTaon__NS__git__NS__stageAllFiles(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / stage all files
    stageFile(relativeFilePath) {
        //#region @backendFunc
        HelpersTaon__NS__git__NS__stageFile(this.project.location, relativeFilePath);
        //#endregion
    }
    //#endregion
    //#region methods & getters / stash
    stash(options) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__stash(this.project.location, options);
        //#endregion
    }
    //#endregion
    //#region methods & getters / stash
    rebase(toBranch) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__rebase(this.project.location, toBranch);
        //#endregion
    }
    //#endregion
    //#region methods & getters / stash apply
    stashApply() {
        //#region @backendFunc
        HelpersTaon__NS__git__NS__stashApply(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / fetch
    fetch(all = false) {
        //#region @backendFunc
        HelpersTaon__NS__git__NS__fetch(this.project.location, all);
        //#endregion
    }
    //#endregion
    //#region methods & getters / reset files
    resetFiles(...relativePathes) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__resetFiles(this.project.location, ...relativePathes);
        //#endregion
    }
    //#endregion
    //#region methods & getters / is inside git repo
    get isInsideGitRepo() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__isInsideGitRepo(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / is without git repository
    get isWithoutGitRepository() {
        //#region @backendFunc
        return !fse.existsSync(path.join(this.project.location, '.git'));
        //#endregion
    }
    //#endregion
    //#region methods & getters / is git root
    get isGitRoot() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__isGitRoot(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / origin url
    get originURL() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__getOriginURL(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / commit
    commit(commitMessage) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__commit(this.project.location, commitMessage);
        //#endregion
    }
    //#endregion
    //#region methods & getters / add and commit
    /**
     * alias to stage all and commit
     */
    addAndCommit(commitMessage) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__stageAllAndCommit(this.project.location, commitMessage);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get default develop Branch
    /**
     * general default development branch for all kinds of projects
     */
    getDefaultDevelopmentBranch() {
        return 'develop';
    }
    //#endregion
    //#region methods & getters / is using action commit
    isUsingActionCommit() {
        return false;
    }
    //#endregion
    jiraIssuesAreOutsideBrackets() {
        return false;
    }
    async getUserInfo(global = false) {
        //#region @backendFunc
        return await HelpersTaon__NS__git__NS__getUserInfo(this.project.location, global);
        //#endregion
    }
    async setUserInfo({ name, email }, global = false) {
        //#region @backendFunc
        return await HelpersTaon__NS__git__NS__setUserInfos({
            cwd: this.project.location,
            name,
            email,
            global,
        });
        //#endregion
    }
    //#region methods & getters / stage all and commit
    stageAllAndCommit(commitMessage) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__stageAllAndCommit(this.project.location, commitMessage);
        //#endregion
    }
    //#endregion
    //#region methods & getters / push current branch
    async pushCurrentBranch(options) {
        //#region @backendFunc
        const res = await HelpersTaon__NS__git__NS__pushCurrentBranch(this.project.location, options);
        GlobalTaskManager.addProgress(PUSH_ACTION_NAME);
        return res;
        //#endregion
    }
    //#endregion
    //#region methods & getters / all origins
    get allOrigins() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__allOrigins(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / uncommited files
    get uncommitedFiles() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__uncommitedFiles(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / there are some uncommited change
    get thereAreSomeUncommitedChange() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__checkIfthereAreSomeUncommitedChange(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / there are some uncommited change except
    thereAreSomeUncommitedChangeExcept(filesList = []) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__thereAreSomeUncommitedChangeExcept(filesList, this.project.location);
        //#endregion
    }
    //#endregion
    /**
     * Resolve last changes
     * - uncommited changes
     * - action commits to melt
     */
    async resolveLastChanges(options) {
        //#region @backendFunc
        options = options || {};
        const displayLastCommitInfo = () => {
            Helpers__NS__info(`
        Project: ${this.project.genericName}
        ${chalk.gray(`Last commit hash: ${this.project.git.lastCommitHash()}`)}
        ${chalk.gray(`Last commit date: ${dateformat(this.project.git.lastCommitDate(), 'dd-mm-yyyy HH:MM:ss')}`)}
        ${chalk.gray(`Last commit message: "${this.project.git.lastCommitMessage()}"`)}
      `);
        };
        const getUncommitedFiles = () => {
            return Utils__NS__uniqArray([
                ...this.uncommitedFiles,
                ...this.stagedFilesRelativePaths,
            ]);
        };
        let hadMeltedActionCommits = false;
        while (true) {
            let uncommitedFiles = getUncommitedFiles();
            let hasUncommitedChanges = uncommitedFiles.length > 0;
            let lastCommitName = this.lastCommitMessage();
            const hasActionCommitsToMelt = this.hasActionCommitsToMelt;
            if (!hasUncommitedChanges && !hasActionCommitsToMelt) {
                break;
            }
            displayLastCommitInfo();
            if (hasActionCommitsToMelt) {
                hadMeltedActionCommits = true;
                Helpers__NS__info(`LAST COMMIT MESSAGE SUGGEST TEMPORARY CHANGES`);
                // if (!options.tryAutomaticActionFirst) {
                await UtilsTerminal__NS__pressAnyKeyToContinueAsync({
                    message: `Melting last changes.. press any key to continue`,
                });
                // }
                this.project.git.meltActionCommits();
                lastCommitName = this.lastCommitMessage();
                uncommitedFiles = getUncommitedFiles();
                hasUncommitedChanges = uncommitedFiles.length > 0;
            }
            if (hasUncommitedChanges) {
                const reason = 'THERE ARE SOME UNCOMMITED CHANGES';
                const data = `
${uncommitedFiles
                    .map(c => `${options.projectNameAsOutputPrefix ? ` ${options.projectNameAsOutputPrefix}/` : ''}${c}`)
                    .join('\n')}`;
                Helpers__NS__info(`
Please provide proper commit message for lastest changes in your project:

          ${this.project.genericName}

        ${chalk.yellow(reason)}
        ${data}

        `);
                //#region handle select options
                const optionsSelect = {
                    openInVscode: {
                        name: 'Open in vscode',
                    },
                    // autoCommit: {
                    //   name: 'Inteligently auto commit all changes',
                    // },
                    addToLastCommitAndPush: {
                        name: `Add to last commit ("${lastCommitName}") and ${hadMeltedActionCommits ? 'force ' : ''}push`,
                    },
                    commitAsChoreUpdateAndPushNormal: {
                        name: `Commit as "chore: update" and normal push`,
                    },
                    commitAsChoreUpdateAndPushForce: {
                        name: `Commit as "chore: update" and force push`,
                    },
                    discard: {
                        name: `Discard all changes`,
                    },
                    discardAndPushNormal: {
                        name: `Discard all changes and normal push`,
                    },
                    discardAndPushForce: {
                        name: `Discard all changes and force push`,
                    },
                    push: {
                        name: `Just ${hadMeltedActionCommits ? 'force ' : ''}push changes...`,
                    },
                    // commitAsAndPush: {
                    //   name: `Commit as < you will choose commit message > and ${hadMeltedActionCommits ? 'force ' : ''}push`,
                    // },
                    skip: {
                        name: 'Skip resolving last changes for this project',
                    },
                    exit: {
                        name: 'Exit',
                    },
                };
                const selected = await UtilsTerminal__NS__select({
                    choices: optionsSelect,
                    question: `${options.questionPrefixMessage ? `[${options.questionPrefixMessage}] ` : ''}What to do with uncommited changes ?`,
                    // autocomplete: true,
                });
                //#endregion
                //#region handle actions
                if (selected === 'openInVscode') {
                    await this.project.openInEditor();
                    await UtilsTerminal__NS__pressAnyKeyToContinueAsync({
                        message: `Press any key to continue when you done`,
                    });
                }
                // if (selected === 'autoCommit') {
                //   await automaticAction();
                // }
                if (selected === 'skip') {
                    return;
                }
                if (selected === 'discardAndPushNormal' ||
                    selected === 'discardAndPushForce') {
                    try {
                        this.project.git.resetHard({ HEAD: 0 });
                    }
                    catch (error) { }
                    await this.project.git.pushCurrentBranch({
                        askToRetry: true,
                        force: selected === 'discardAndPushForce',
                    });
                    return;
                }
                if (selected === 'discard') {
                    try {
                        this.project.git.resetHard({ HEAD: 0 });
                    }
                    catch (error) { }
                    return;
                }
                if (selected === 'exit') {
                    process.exit(0);
                }
                if (selected === 'commitAsChoreUpdateAndPushNormal') {
                    this.project.git.stageAllFiles();
                    this.project.git.commit('chore: update');
                    await this.project.git.pushCurrentBranch({
                        askToRetry: true,
                        force: false,
                    });
                    return;
                }
                if (selected === 'commitAsChoreUpdateAndPushForce') {
                    this.project.git.stageAllFiles();
                    this.project.git.commit('chore: update');
                    await this.project.git.pushCurrentBranch({
                        askToRetry: true,
                        force: true,
                    });
                    return;
                }
                if (selected === 'push') {
                    await this.project.git.pushCurrentBranch({
                        askToRetry: true,
                        force: hadMeltedActionCommits,
                    });
                    return;
                }
                // if (selected === 'commitAsAndPush') {
                //   const input = await UtilsTerminal__NS__input({
                //     question: `Commit message`,
                //     defaultValue: lastCommitName,
                //   });
                //   this.project.git.stageAllFiles();
                //   this.project.git.commit(input);
                //   await this.project.git.pushCurrentBranch({
                //     askToRetry: true,
                //     force: hadMeltedActionCommits,
                //   });
                //   return;
                // }
                if (selected == 'addToLastCommitAndPush') {
                    this.project.git.resetSoftHEAD(1);
                    this.project.git.stageAllFiles();
                    this.project.git.commit(lastCommitName);
                    await this.project.git.pushCurrentBranch({
                        askToRetry: true,
                        force: hadMeltedActionCommits,
                    });
                    return;
                }
                //#endregion
            }
        }
        //#endregion
    }
    //#region methods & getters / has action commits to melt
    get hasActionCommitsToMelt() {
        //#region @backendFunc
        const lastCommitMessage = this.lastCommitMessage() || '';
        return (lastCommitMessage.trim() ===
            HelpersTaon__NS__git__NS__getACTION_MSG_RESET_GIT_HARD_COMMIT());
        //#endregion
    }
    //#endregion
    //#region methods & getters / melt action commits
    /**
     * @returns number of melted commits
     */
    meltActionCommits() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__meltActionCommits(this.project.location);
        //#endregion
    }
    //#endregion
    //#region push to git repo
    /**
     *
     * @param newVersion
     * @param pushWithoutAsking
     */
    async tagAndPushToGitRepo(newVersion, autoReleaseUsingConfig, isCiProcess) {
        //#region @backendFunc
        return await HelpersTaon__NS__git__NS__tagAndPushToGitRepo(this.project.location, {
            newVersion,
            skipAskingQuestionBeforePush: autoReleaseUsingConfig,
            isCiProcess,
        });
        //#endregion
    }
    //#endregion
    //#region methods & getters / pull current branch
    async pullCurrentBranch(options) {
        //#region @backendFunc
        await HelpersTaon__NS__git__NS__pullCurrentBranch(this.project.location, {
            ...options,
        });
        GlobalTaskManager.addProgress(PULL_ACTION_NAME);
        //#endregion
    }
    //#endregion
    //#region methods & getters / current branch name
    get currentBranchName() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__currentBranchName(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / list of current git changes
    get listOfCurrentGitChanges() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__getListOfCurrentGitChanges(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get branches names by
    getBranchesNamesBy(pattern) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__getBranchesNames(this.project.location, pattern);
        //#endregion
    }
    //#endregion
    //#region methods & getters / reset soft HEAD
    resetSoftHEAD(HEAD = 1) {
        //#region @backendFunc
        HelpersTaon__NS__git__NS__resetSoftHEAD(this.project.location, HEAD);
        //#endregion
    }
    //#endregion
    //#region methods & getters / reset hard
    resetHard(options) {
        //#region @backendFunc
        HelpersTaon__NS__git__NS__resetHard(this.project.location, options);
        //#endregion
    }
    //#endregion
    //#region methods & getters / count commits
    countCommits() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__countCommits(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / has any commits
    hasAnyCommits() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__hasAnyCommits(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / is in merge process
    get isInMergeProcess() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__isInMergeProcess(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / last commit date
    lastCommitDate() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__lastCommitDate(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / last commit hash
    lastCommitHash() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__lastCommitHash(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / last commit message
    lastCommitMessage() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__lastCommitMessage(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / last commit message
    async penultimateCommitMessage() {
        //#region @backendFunc
        return await HelpersTaon__NS__git__NS__penultimateCommitMessage(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get commit message by index
    async getCommitMessageByIndex(index) {
        //#region @backendFunc
        return await HelpersTaon__NS__git__NS__getCommitMessageByIndex(this.project.location, index);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get commit message by hash
    async getCommitMessageByHash(hash) {
        //#region @backendFunc
        return await HelpersTaon__NS__git__NS__getCommitMessageByHash(this.project.location, hash);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get commit hash by index
    async getCommitHashByIndex(index) {
        //#region @backendFunc
        return await HelpersTaon__NS__git__NS__getCommitHashByIndex(this.project.location, index);
        //#endregion
    }
    //#endregion
    //#region methods & getters / penultimate commit hash
    penultimateCommitHash() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__penultimateCommitHash(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / check tag exists
    checkTagExists(tag) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__checkTagExists(tag, this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / checkout
    checkout(branchName, options) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__checkout(this.project.location, branchName, options);
        //#endregion
    }
    //#endregion
    allowedGitEmailDomains() {
        return [];
    }
    skipLintOnPush() {
        return true;
    }
    cleanRepoFromAnyFilesExceptDotGitFolder() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__cleanRepoFromAnyFilesExceptDotGitFolder(this.project.location);
        //#endregion
    }
    //#region methods & getters / checkout from to
    checkoutFromTo(checkoutFromBranch, branch, origin = 'origin') {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__checkoutFromTo(checkoutFromBranch, branch, origin, this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / last tag name for major version
    /**
     *
     * @param majorVersion example: v1, v2 etc.
     * @returns tag name
     */
    lastTagNameForMajorVersion(majorVersion) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__lastTagNameForMajorVersion(this.project.location, majorVersion);
        //#endregion
    }
    //#endregion
    //#region methods & getters / last tag hash
    lastTagHash() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__lastTagHash(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / remote origin url
    get remoteOriginUrl() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__getOriginURL(this.project.location);
        //#endregion
    }
    //#endregion
    //#region methods & getters / remote origin url
    /**
     * get provider of remote origin
     */
    get remoteProvider() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__getRemoteProvider(this.project.location) || '';
        //#endregion
    }
    //#endregion
    //#region methods & getters / last tag version name
    get lastTagVersionName() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__lastTagVersionName(this.project.location) || '';
        //#endregion
    }
    //#endregion
    //#region methods & getters / staged files
    get stagedFiles() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__stagedFiles(this.project.location);
        //#endregion
    }
    get stagedFilesRelativePaths() {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__stagedFiles(this.project.location, true);
        //#endregion
    }
    //#endregion
    //#region methods & getters / rename origin
    /**
     * TODO does this make any sense
     */
    renameOrigin(newNameOrUlr) {
        //#region @backendFunc
        if (!newNameOrUlr.endsWith('.git')) {
            newNameOrUlr = newNameOrUlr + '.git';
        }
        const oldOrigin = this.project.git.originURL;
        if (!newNameOrUlr.startsWith('git@') &&
            !newNameOrUlr.startsWith('https://')) {
            newNameOrUlr = oldOrigin.replace(path.basename(oldOrigin), newNameOrUlr);
        }
        try {
            this.project.run(`git remote rm origin`).sync();
        }
        catch (error) { }
        try {
            this.project.run(`git remote add origin ${newNameOrUlr}`).sync();
            Helpers__NS__info(`Origin changed:
    from: ${oldOrigin}
      to: ${newNameOrUlr}\n`);
        }
        catch (e) {
            Helpers__NS__error(`Not able to change origin.. reverting to old`, true, true);
            this.project.run(`git remote add origin ${oldOrigin}`).sync();
        }
        //#endregion
    }
    //#endregion
    //#region getters & methods / translate git commit from args
    /**
     * By default no translation of commit
     */
    transalteGitCommitFromArgs() {
        return { from: void 0, to: void 0 };
    }
    //#endregion
    //#region getters & methods / us git branches when commiting and pushing
    /**
     * By defult true.. when commit branches will not function.
     * (false is better for simple projects)
     */
    useGitBranchesWhenCommitingAndPushing() {
        return true;
    }
    //#endregion
    //#region getters & methods / automatically add all changes when pushing to git
    /**
     * usefull when pushing in project with childrens as git repos
     */
    automaticallyAddAllChangesWhenPushingToGit() {
        return false;
    }
    //#endregion
    //#region getters & methods / use git branches as metadata for commits
    /**
     * usefull when pushing in project with childrens as git repos
     */
    useGitBranchesAsMetadataForCommits() {
        return true;
    }
    //#endregion
    //#region getters & methods / clone to
    /**
     * @returns true if clone was successful
     */
    async cloneTo(cwd, newProjectName) {
        //#region @backendFunc
        if (!newProjectName) {
            newProjectName = path.basename(this.project.location);
        }
        const dest = crossPlatformPath([newProjectName, newProjectName]);
        if (fse.existsSync(dest)) {
            const res = await Helpers__NS__questionYesNo(`Folder ${newProjectName} alredy exist, delete it ?`);
            if (res) {
                Helpers__NS__tryRemoveDir(dest);
            }
            else {
                HelpersTaon__NS__pressKeyAndContinue('Operation not completed... press any key');
                return false;
            }
        }
        Helpers__NS__info(`Cloning ${this.project.name}...`);
        // copy instead cloning
        await HelpersTaon__NS__git__NS__clone({
            cwd,
            url: this.project.git.remoteOriginUrl,
            destinationFolderName: newProjectName,
        });
        Helpers__NS__info(`Clone success`);
        return true;
        //#endregion
    }
    //#endregion
    //#region getters & methods / get children
    /**
     * This is only for push/pull process
     *
     * There are 2 types of projects:
     * - with linked-projects.json
     * - with children from external folder
     *
     * projects that are children of this project (with its own git repo)
     */
    get gitChildren() {
        let childrenRepos = [];
        if (this.project.linkedProjects.linkedProjects.length > 0) {
            childrenRepos = this.project.linkedProjects.linkedProjects
                .map(c => {
                return this.project.ins.From([
                    this.project.location,
                    c.relativeClonePath,
                ]);
            })
                .filter(f => !!f);
        }
        else {
            childrenRepos = this.project.children;
        }
        childrenRepos = childrenRepos.filter(f => f.git.isInsideGitRepo && f.git.isGitRoot);
        return childrenRepos;
    }
    //#endregion
    //#region getters & methods / set remote origin type
    async setRemoteOriginType(setOrigin) {
        //#region @backendFunc
        if (setOrigin === 'https') {
            setOrigin = 'http';
        }
        if (setOrigin === 'ssh') {
            await HelpersTaon__NS__git__NS__changeRemoteFromHttpsToSSh(this.project.location);
        }
        else if (setOrigin === 'http') {
            await HelpersTaon__NS__git__NS__changeRemoveFromSshToHttps(this.project.location);
        }
        //#endregion
    }
    //#endregion
    //#region methods & getters / push process
    async pullProcess(options = {}) {
        //#region @backendFunc
        options = options || {};
        options.skipCloneGitChildren = !!options.skipCloneGitChildren;
        let { skipCloneGitChildren, setOrigin } = options;
        await this._beforePullProcessAction(setOrigin, skipCloneGitChildren);
        await this.setRemoteOriginType(setOrigin);
        let uncommitedChanges = this.project.git.thereAreSomeUncommitedChange;
        if (uncommitedChanges) {
            Helpers__NS__warn(`Stashing uncommit changes... in ${this.project.genericName}`);
            try {
                this.project.git.stageAllFiles();
            }
            catch (error) { }
            try {
                this.project.git.stash();
            }
            catch (error) { }
        }
        await this.project.git.pullCurrentBranch({ askToRetry: true });
        await this._afterPullProcessAction(setOrigin, skipCloneGitChildren);
        const location = this.project.location;
        this.project.ins.unload(this);
        this.project.ins.add(this.project.ins.From(location));
        await this.project.linkedProjects.saveLocationToDB();
        if (this.automaticallyAddAllChangesWhenPushingToGit()) {
            for (const child of this.gitChildren) {
                await child.git.pullProcess(options);
            }
        }
        await this.project.linkedProjects.saveAllLinkedProjectsToDB();
        //#endregion
    }
    //#endregion
    async validateEmailDomain(allowedEmailsEnds) {
        //#region @backendFunc
        if (allowedEmailsEnds.length === 0) {
            return;
        }
        while (true) {
            let gitConfig = null;
            try {
                gitConfig = await this.project.git.getUserInfo();
            }
            catch (error) { }
            const errorsMgs = {
                notUsingEmail: `You are not using any email(s) address(es) for git operations.`,
                notUsingName: `You are not using name for git operations`,
                notUsingEmailAndName: `You are not using any email(s) address(es) for git operations.`,
                notUsingAllowedEmails: `You are not using allowed ${allowedEmailsEnds.join(',')} email domains.`,
            };
            const errors = {
                notUsingEmail: !gitConfig?.email ? errorsMgs.notUsingEmail : null,
                notUsingName: !gitConfig?.name ? errorsMgs.notUsingName : null,
                notUsingAllowedEmails: !allowedEmailsEnds.some(allowedEmailEnd => gitConfig?.email?.endsWith(allowedEmailEnd))
                    ? errorsMgs.notUsingAllowedEmails
                    : null,
            };
            if (allowedEmailsEnds.length === 0) {
                delete errors.notUsingAllowedEmails;
            }
            if (!Object.values(errors).some(e => e)) {
                return;
            }
            Object.values(errors)
                .filter(e => e)
                .forEach(e => {
                Helpers__NS__warn(e);
            });
            Helpers__NS__warn(`
        Please provide proper email and name for git operations.

        `);
            const options = {
                changeLocalEmailAndName: {
                    name: 'Change local .git/config email and name',
                },
                useItAnyway: {
                    name: 'Use it anyway',
                },
                exitProcess: {
                    name: 'Exit process',
                },
            };
            if (!gitConfig?.email || !gitConfig?.name) {
                delete options.useItAnyway;
            }
            const selected = await UtilsTerminal__NS__select({
                question: 'Select action',
                choices: options,
            });
            if (selected === 'changeLocalEmailAndName') {
                const email = await UtilsTerminal__NS__input({
                    question: 'Enter git email',
                });
                const name = await UtilsTerminal__NS__input({
                    question: 'Enter your git name and surname (or just name)',
                });
                await this.project.git.setUserInfo({ email, name });
            }
            if (selected === 'exitProcess') {
                process.exit(0);
            }
        }
        //#endregion
    }
    async _afterPullProcessAction(setOrigin, skipCloneGitChildren = false) {
        if (!skipCloneGitChildren) {
            await this.project.linkedProjects.cloneNonexistedLinkedProjects(setOrigin);
        }
    }
    async backupBranch(branchName) {
        //#region @backendFunc
        return await HelpersTaon__NS__git__NS__backupBranch(this.project.location, branchName);
        //#endregion
    }
    //#region methods & getters / push process
    async pushProcess(options = {}) {
        //#region @backendFunc
        let { force = false, typeofCommit, skipLint, forcePushNoQuestion, origin = 'origin', exitCallBack, askToConfirmPush, askToConfirmCommit, askToConfirmBranchChange, args = [], commitMessageRequired, overrideCommitMessage, skipChildren, setOrigin, currentOrigin, mergeUpdateCommits, } = options;
        await this._beforePushProcessAction(setOrigin);
        await this.setRemoteOriginType(setOrigin);
        await this.project.linkedProjects.saveLocationToDB();
        const commitData = await this._getCommitMessage(typeofCommit, args, commitMessageRequired, currentOrigin);
        if (mergeUpdateCommits && this.project.git.stagedFiles.length > 0) {
            //#region merge update commits
            Helpers__NS__logInfo(`Pushing detailed changes to project: ${this.project.genericName}`);
            Helpers__NS__logInfo(`Modified files... `);
            Helpers__NS__logInfo([...this.project.git.stagedFiles]
                .map(f => ` - ${chalk.underline(f)}`)
                .join('\n'));
            while (true) {
                const mergeAction = {
                    useUpdateCommit: {
                        name: `Use temporary update commit ` +
                            `"${HelpersTaon__NS__git__NS__getACTION_MSG_RESET_GIT_HARD_COMMIT()}" and force push`,
                    },
                    provideCommitMessage: {
                        name: 'Provide commit message and force push',
                    },
                    resetRemoveChanges: {
                        name: 'Reset/remove changes and force push',
                    },
                    openInVscode: {
                        name: 'Open project in vscode',
                    },
                };
                const action = force
                    ? 'useUpdateCommit'
                    : await UtilsTerminal__NS__select({
                        choices: mergeAction,
                        question: 'What to do ?',
                    });
                if (action === 'openInVscode') {
                    await this.project.openInEditor();
                    continue;
                }
                if (action === 'resetRemoveChanges') {
                    force = true;
                    forcePushNoQuestion = true;
                    this.project.git.resetHard();
                    break;
                }
                if (action === 'useUpdateCommit') {
                    force = true;
                    forcePushNoQuestion = true;
                    overrideCommitMessage =
                        HelpersTaon__NS__git__NS__getACTION_MSG_RESET_GIT_HARD_COMMIT();
                }
                if (action === 'provideCommitMessage') {
                    force = true;
                    forcePushNoQuestion = true;
                    try {
                        overrideCommitMessage = await UtilsTerminal__NS__input({
                            question: `Please provide commit message for current changes:`,
                            defaultValue: 'update',
                        });
                    }
                    catch (error) { }
                }
                if (overrideCommitMessage) {
                    break;
                }
                //#endregion
            }
        }
        // #region warning about missing sub-issue
        if (commitData.typeOfCommit === 'feature' &&
            commitData.jiraNumbers?.length === 1 &&
            commitData.issuesFromOtherProjects?.length === 0) {
            Helpers__NS__info(`

        You current feature branch "${this.project.git.currentBranchName}"
        doesn't have ${chalk.bold('main-issue')} and ${chalk.bold('sub-issue')} inlcueded.

        Proper example: feature/JIRANUM-<number-of-sub-issue>-JIRANUM-<number-of-main-issue>-commit-name

          `);
            if (!(await Helpers__NS__questionYesNo('Continue without sub-issue?'))) {
                process.exit(0);
            }
        }
        //#endregion
        //#region automatic push to git
        if (!this.automaticallyAddAllChangesWhenPushingToGit()) {
            if (commitData.commitMessage
                ?.split(':')
                .map(p => p.trim())
                .every(p => p === this.project.git.currentBranchName)) {
                // QUICK_FIX
                Helpers__NS__error(`

        Please provide more specific commit message than branch name
        or maybe you forgot ?
        - TEAM ID? (example TEAM2# <= hash at the end)
        - JIRA ISSUE? (example JIRA-1234)

        `, false, true);
            }
        }
        //#endregion
        //#region lint
        if (this.skipLintOnPush()) {
            skipLint = true;
        }
        if (commitData.typeOfCommit === 'release') {
            skipLint = true;
        }
        // console.log({ skipLint, typeofCommit });
        const numberOfStagedFiles = this.project.git.stagedFiles.length;
        if (numberOfStagedFiles === 0) {
            Helpers__NS__warn(`No staged files...`);
            skipLint = true;
        }
        if (!skipLint) {
            while (true) {
                try {
                    await this.project.lint(options);
                    break;
                }
                catch (error) {
                    Helpers__NS__warn('Fix your code...');
                    if (!(await HelpersTaon__NS__consoleGui__NS__question__NS__yesNo('Try again lint before commit ?'))) {
                        break;
                    }
                }
            }
        }
        //#endregion
        if (this.project.useFeatureInBranchNameForTests() &&
            commitData.typeOfCommit === 'test') {
            commitData.useFeatureBranchForTestBranch = true;
        }
        if (!commitData.isActionCommit) {
            const commitMessageFromBranch = (await CommitData.getFromBranch(commitData.branchName, {
                currentOrigin,
                jiraIssuesAreOutsideBrackets: this.project.git.jiraIssuesAreOutsideBrackets(),
            })).commitMessage;
            Helpers__NS__info(`

      PROJECT: ${this.project.genericName}

      Current commit:
      - message to include {${overrideCommitMessage ? overrideCommitMessage : commitData.commitMessage}}
      ${this.useGitBranchesAsMetadataForCommits() && !overrideCommitMessage
                ? `- branch to checkout {${commitData.branchName}}`
                : `- using ${chalk.bold('current')} branch {${this.project.git.currentBranchName}} \n` +
                    `(generated branches from metadata not allowed for this project) \n
          `}`);
            if (!overrideCommitMessage &&
                commitMessageFromBranch !== commitData.commitMessage) {
                Helpers__NS__logWarn(`Commit from args and commit from branch are different
        commit message from args: ${commitData.commitMessage}
        commit message from branch: ${commitMessageFromBranch}

        ADVICE: Is is better to use words instead characters to describe multiple
        commit changes in one commit message

        `);
            }
            else {
                Helpers__NS__logInfo(chalk.gray(`Commit from args and commit from branch are the same...`));
            }
            const lastCommitMessage = this.project.git.lastCommitMessage();
            if (lastCommitMessage &&
                [commitData.commitMessage, overrideCommitMessage]
                    .filter(f => !!f)
                    .some(m => m === lastCommitMessage)) {
                if (await Helpers__NS__questionYesNo('Soft reset last commit with same message ?')) {
                    this.project.git.resetSoftHEAD(1);
                }
            }
            if (!(await Helpers__NS__questionYesNo('Commit and push this ?'))) {
                exitCallBack();
            }
        }
        if (this.automaticallyAddAllChangesWhenPushingToGit()) {
            // my project
            this.project.git.stageAllFiles();
        }
        if (this.useGitBranchesAsMetadataForCommits() && !overrideCommitMessage) {
            Helpers__NS__info('Checkingout branches (if needed)...');
            if (this.project.git.currentBranchName?.trim() !== commitData.branchName) {
                if (askToConfirmBranchChange) {
                    Helpers__NS__info(`Changing branch to: ${commitData.branchName}`);
                    if (!(await Helpers__NS__questionYesNo('Confirm branch change ?'))) {
                        exitCallBack();
                    }
                }
                try {
                    this.project.git.checkout(commitData.branchName, {
                        createBranchIfNotExists: true,
                    });
                }
                catch (error) {
                    Helpers__NS__error('Please modyfiy you commit message or delete branch,');
                }
            }
        }
        if (askToConfirmCommit) {
            Helpers__NS__info(`Commit message: ${overrideCommitMessage
                ? overrideCommitMessage
                : commitData.commitMessage}`);
            if (!(await Helpers__NS__questionYesNo('Confirm commit ?'))) {
                exitCallBack();
            }
        }
        try {
            this.project.git.commit(overrideCommitMessage
                ? overrideCommitMessage
                : commitData.commitMessage);
        }
        catch (error) {
            Helpers__NS__warn(`Not commiting anything... `);
        }
        if (askToConfirmPush) {
            if (!(await Helpers__NS__questionYesNo('Confirm push ?'))) {
                exitCallBack();
            }
        }
        await this.project.git.pushCurrentBranch({
            force,
            origin,
            forcePushNoQuestion,
            askToRetry: true,
        });
        if (this.automaticallyAddAllChangesWhenPushingToGit() && !skipChildren) {
            if (this.project.linkedProjects.getLinkedProjectsConfig().skipRecrusivePush) {
                Helpers__NS__warn(`Skipping recrusive (children) push for ${this.project.genericName}`);
                return;
            }
            for (const child of this.gitChildren) {
                if (mergeUpdateCommits) {
                    await child.git.meltActionCommits();
                }
                await child.git.pushProcess(options);
            }
        }
        await this.project.linkedProjects.saveAllLinkedProjectsToDB();
        //#endregion
    }
    //#endregion
    //#region methods & getters / before any action on git root
    _beforeAnyActionOnGitRoot() {
        //#region @backendFunc
        if (!this.project.git.isInsideGitRepo) {
            Helpers__NS__error(`Project ${chalk.bold(this.project.name)} is not a git repository
      locaiton: ${this.project.location}`, false, true);
        }
        if (!this.project.git.isGitRoot) {
            Helpers__NS__error(`Project ${chalk.bold(this.project.name)} is not a git root
      locaiton: ${this.project.location}`, false, true);
        }
        //#endregion
    }
    //#endregion
    //#region methods & getters / before push action
    async _beforePushProcessAction(setOrigin) {
        //#region @backendFunc
        this._beforeAnyActionOnGitRoot();
        // for first projects
        if (this.project.git.isInsideGitRepo &&
            this.project.git.isGitRoot &&
            !this.project.git.currentBranchName?.trim()) {
            if (await HelpersTaon__NS__consoleGui__NS__question__NS__yesNo('Repository is empty...Commit "master" branch and commit all as "first commit" ?')) {
                this.project.git.checkout('master');
                this.project.git.stageAllFiles();
                this.project.git.commit('first commit ');
            }
        }
        await this.validateEmailDomain(this.allowedGitEmailDomains());
        //#endregion
    }
    //#endregion
    //#region methods & getters / before push action
    async _beforePullProcessAction(setOrigin, skipCloneGitChildren = false) {
        //#region @backendFunc
        this._beforeAnyActionOnGitRoot();
        if (!skipCloneGitChildren) {
            await this.project.linkedProjects.cloneNonexistedLinkedProjects(setOrigin);
        }
        //#endregion
    }
    //#endregion
    //#region methods & getters / resovle commit message
    async _getCommitMessage(typeOfCommit, args, commitMessageRequired, 
    /**
     * only needed when push github
     * and I forgot to add my username before issue
     * taon pfix proper input my-repo#344
     * that should be
     * taon pfix proper input my-username/my-repo#344
     */
    currentOrigin) {
        //#region @backendFunc
        let commitData;
        if (this.useGitBranchesWhenCommitingAndPushing()) {
            let argsCommitData = await CommitData.getFromArgs(args, {
                typeOfCommit,
                currentOrigin,
                jiraIssuesAreOutsideBrackets: this.project.git.jiraIssuesAreOutsideBrackets(),
            });
            // console.log({ argsCommitData })
            if (argsCommitData.message) {
                commitData = argsCommitData;
            }
            else {
                const commitDataBranch = await CommitData.getFromBranch(this.project.git.currentBranchName, {
                    releaseWords: this.project.releaseProcess.getReleaseWords(),
                    currentOrigin,
                    jiraIssuesAreOutsideBrackets: this.project.git.jiraIssuesAreOutsideBrackets(),
                });
                commitData = commitDataBranch;
                // console.log({ commitDataBranch })
            }
        }
        else {
            let argsCommitData = await CommitData.getFromArgs(args, {
                typeOfCommit,
                currentOrigin,
                jiraIssuesAreOutsideBrackets: this.project.git.jiraIssuesAreOutsideBrackets(),
            });
            // console.log({ argsCommitData })
            // console.log(argsCommitData)
            if (!argsCommitData.message && commitMessageRequired) {
                Helpers__NS__error('Please provide message in argument', false, true);
            }
            if (!argsCommitData.message) {
                argsCommitData.message =
                    HelpersTaon__NS__git__NS__getACTION_MSG_RESET_GIT_HARD_COMMIT();
            }
            commitData = argsCommitData;
        }
        if (commitData.message !==
            HelpersTaon__NS__git__NS__getACTION_MSG_RESET_GIT_HARD_COMMIT()) {
            const { from, to } = this.transalteGitCommitFromArgs();
            if (from && to) {
                commitData.message = ___NS__kebabCase(await translate(commitData.message, { from, to }));
            }
        }
        return commitData;
        //#endregion
    }
    //#endregion
    //#region methods & getters / prevent accidental branch change for taon projects
    /**
     * This will prevent accidental branch change for taon projects
     * @returns branch name
     */
    duringPushWarnIfProjectNotOnSpecyficDevBranch() {
        return '';
    }
    //#endregion
    //#region methods & getters / get changed files in commit by hash
    async getChangedFilesInCommitByHash(hash) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__getChangedFilesInCommitByHash(this.project.location, hash);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get changed files in commit
    /**
     * @param index 0 - means last commit
     */
    async getChangedFilesInCommitByIndex(index) {
        //#region @backendFunc
        return HelpersTaon__NS__git__NS__getChangedFilesInCommitByIndex(this.project.location, index);
        //#endregion
    }
    //#endregion
    //#region methods & getters / get changes summary
    async changesSummary(options) {
        //#region @backendFunc
        options = options || {};
        const fillStringWithSpaceUpTo = (str, length, specialCharacter = ' ') => {
            return str + specialCharacter.repeat(length - str.length);
        };
        options.prefix =
            options.prefix ??
                `${fillStringWithSpaceUpTo(`[${this.project.name}]`, 40, '.')} `;
        return await HelpersTaon__NS__git__NS__changesSummary(this.project.location, options.prefix);
        //#endregion
    }
    //#endregion
    useBranchNameDirectlyAsCommitMessage() {
        return false;
    }
}
//# sourceMappingURL=c:/Users/darek/projects/npm/taon-dev/tnp-helpers/dist-nocutsrc-prod/lib/base/classes/base-git.js.map