"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const simple_git_1 = require("simple-git");
const path_1 = require("path");
const fs_extra_1 = require("fs-extra");
const fs_1 = require("fs");
const ramda_1 = require("ramda");
const move_1 = require("../../../utils/move");
const cli_customize_1 = require("../../../utils/cli-customize");
const remove_1 = require("../../../utils/remove");
const MergeTool_interface_1 = require("../../../interfaces/MergeTool.interface");
const mergeUtils_1 = require("./mergeUtils");
const PrepareStrategy_1 = require("./PrepareStrategy");
var AddOptions;
(function (AddOptions) {
    AddOptions["All"] = "-A";
})(AddOptions || (AddOptions = {}));
var MergeOptions;
(function (MergeOptions) {
    MergeOptions["NoCommit"] = "--no-commit";
})(MergeOptions || (MergeOptions = {}));
var BranchOptions;
(function (BranchOptions) {
    BranchOptions["Rename"] = "-m";
})(BranchOptions || (BranchOptions = {}));
const moveResolvingConflicts = (resolve) => (from, to) => (file) => __awaiter(void 0, void 0, void 0, function* () {
    const originFile = (0, path_1.join)(from, file);
    const source = yield fs_1.promises.readFile(originFile, 'utf8');
    yield (0, fs_extra_1.outputFile)((0, path_1.join)(to, file), (0, mergeUtils_1.resolveConflicts)(source, resolve));
});
class MergeTool {
    constructor(cwd) {
        this.cwd = cwd;
    }
    init() {
        return __awaiter(this, void 0, void 0, function* () {
            yield (0, fs_extra_1.ensureDir)(this.cwd);
            this.gitDriver = (0, simple_git_1.default)(this.cwd);
            yield this.gitDriver.init();
        });
    }
    prepareToMerge({ base, local, remote }, onProgressCallback = () => { }) {
        return __awaiter(this, void 0, void 0, function* () {
            const prepareBase = yield PrepareStrategy_1.default.getPrepareStrategy(base);
            yield prepareBase.execute(this.cwd, (0, ramda_1.o)(onProgressCallback, (0, mergeUtils_1.scale)(0, 10)));
            yield this.gitDriver.add(AddOptions.All);
            yield this.gitDriver.commit('initial');
            yield this.gitDriver.branch([BranchOptions.Rename, 'master', mergeUtils_1.LOCAL_BRANCH]);
            yield this.gitDriver.branch([mergeUtils_1.UPSTREAM_BRANCH]);
            yield (0, remove_1.default)(this.cwd, ['**/*', '!.git/**/*']);
            const prepareLocal = yield PrepareStrategy_1.default.getPrepareStrategy(local);
            yield prepareLocal.execute(this.cwd, (0, ramda_1.o)(onProgressCallback, (0, mergeUtils_1.scale)(10, 90)));
            yield this.gitDriver.add(AddOptions.All);
            yield this.gitDriver.commit('add local changes');
            yield this.gitDriver.checkout([mergeUtils_1.UPSTREAM_BRANCH]);
            yield (0, remove_1.default)(this.cwd, ['**/*', '!.git/**/*']);
            const prepareRemote = yield PrepareStrategy_1.default.getPrepareStrategy(remote);
            yield prepareRemote.execute(this.cwd, (0, ramda_1.o)(onProgressCallback, (0, mergeUtils_1.scale)(90, 100)));
            yield this.gitDriver.add(AddOptions.All);
            yield this.gitDriver.commit('add remote changes');
        });
    }
    merge() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.gitDriver.checkout([mergeUtils_1.LOCAL_BRANCH]);
            let conflicts = [];
            try {
                yield this.gitDriver.merge([mergeUtils_1.UPSTREAM_BRANCH, MergeOptions.NoCommit]);
            }
            catch (error) {
                const res = error.git;
                conflicts = res.conflicts.filter(({ meta }) => meta);
            }
            return {
                conflicts,
                status: (0, ramda_1.pick)(MergeTool_interface_1.mergeStatuses, yield this.gitDriver.status()),
            };
        });
    }
    resolveConflictsForDeletedFiles(resolve, { status, conflicts }) {
        if (conflicts.length === 0)
            return status;
        const getActionForDeletedFile = mergeUtils_1.HANDLE_DELETED[resolve];
        const actions = conflicts.reduce((actions, conflict) => {
            actions.push(getActionForDeletedFile(conflict));
            return actions;
        }, []);
        return (0, mergeUtils_1.applyActionsForDeletedFiles)(actions, status);
    }
    applyMergeResult(dest, mergeStatus, resolve) {
        return __awaiter(this, void 0, void 0, function* () {
            const hasConflicts = resolve !== MergeTool_interface_1.ResolveConflicts.Manually && mergeStatus.conflicted.length;
            const allFilesToMove = (0, mergeUtils_1.concatFiles)((0, ramda_1.omit)(hasConflicts ? ['conflicted', 'deleted'] : ['deleted'], mergeStatus));
            yield Promise.all([
                (0, move_1.moveFiles)({ files: (0, mergeUtils_1.getFilesList)(allFilesToMove), from: this.cwd, to: dest }),
                (0, mergeUtils_1.getFilesList)(mergeStatus.deleted).map(deletedFile => fs_1.promises.unlink((0, path_1.join)(dest, deletedFile))),
                hasConflicts
                    ? (0, move_1.moveFiles)({ files: (0, mergeUtils_1.getFilesList)(mergeStatus.conflicted), fileHandler: moveResolvingConflicts(resolve), from: this.cwd, to: dest })
                    : Promise.resolve()
            ]);
        });
    }
    generateMergeStatusMessage(status, resolve) {
        const statusesToShow = (0, ramda_1.clone)(status);
        if (mergeUtils_1.AutoResolve.includes(resolve)) {
            statusesToShow.modified.push(...statusesToShow.conflicted);
            delete statusesToShow.conflicted;
        }
        return Object.entries(statusesToShow)
            .map(([status, files]) => {
            if (files.length === 0)
                return null;
            const message = mergeUtils_1.MESSAGES_BY_STATUS[status];
            const setColor = (0, cli_customize_1.colorize)(mergeUtils_1.COLORS_BY_STATUS[status]);
            return [...files]
                .sort((file1, file2) => (0, mergeUtils_1.getName)(file1) > (0, mergeUtils_1.getName)(file2) ? 1 : -1)
                .map(file => typeof file === 'string'
                ? setColor(`${message}${file}`)
                : setColor(`${message}${file.file} / ${file.message}`))
                .join('\n');
        })
            .filter(Boolean)
            .join('\n');
    }
}
exports.default = MergeTool;
//# sourceMappingURL=index.js.map