"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SemanticVersioner = exports.WorkspaceVersioner = void 0;
const tslib_1 = require("tslib");
const codiac_common_1 = require("@codiac.io/codiac-common");
const contracts_1 = require("@codiac.io/codiac-common/contracts");
const inversify_1 = require("inversify");
const cli_context_repo_1 = require("./data/cli-context-repo");
const dx_constants_1 = require("./dx-constants");
const semver = require("semver");
/** Manages the semantic version number for image exports declared in the workspace (aka: the codiac.json).  */
let WorkspaceVersioner = class WorkspaceVersioner {
    constructor(logger, projCfgRepo
    // @inject(SemanticVersioner) private versioner: SemanticVersioner
    ) {
        this.logger = logger;
        this.projCfgRepo = projCfgRepo;
        this.versioner = new SemanticVersioner(logger);
    }
    /** Increments the version number of the given image export by the given increment.
     *  Retains existing version number if no increment is specified.
     *  Sets to the default init version number if the imageExport doesnt yet have a version declared.
     *  Writes changes to the project config (aka: codiac.json).
     *  Throws if existing version is invalid.
     *
     * @param cfg Project config doc (aka: codiac.json deserialized)
     */
    incrementImageVersion(imageExport, cfg, increment) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                try {
                    if (!imageExport.version)
                        imageExport.version = dx_constants_1.DxConstants.DEFAULT_INIT_IMAGE_VERSION; // TODO: Absorb this into this class
                    let newVersion = this.versioner.incrementVersion(imageExport.version, increment);
                    if (newVersion != imageExport.version) {
                        imageExport.version = newVersion;
                        if (!cfg.images)
                            cfg.images = {};
                        cfg.images[imageExport.name] = imageExport;
                        yield this.projCfgRepo.updateProjectConfigFile(cfg);
                    }
                    resolve(imageExport);
                }
                catch (err) {
                    reject(err);
                }
            }));
        });
    }
    /** Increments the project code version number by the given increment.
     *  Retains existing version number if no increment is specified.
     *  Writes changes to the project config (aka: codiac.json).
     *  Throws if existing version is invalid.
     *
     * @param cfg Project config doc (aka: codiac.json deserialized)
     */
    incrementCodeVersion(cfg, increment) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                try {
                    let newVersion = this.versioner.incrementVersion(cfg.codeVersion, increment);
                    if (newVersion != cfg.codeVersion) {
                        cfg.codeVersion = newVersion;
                        yield this.projCfgRepo.updateProjectConfigFile(cfg);
                    }
                    resolve(cfg);
                }
                catch (err) {
                    reject(err);
                }
            }));
        });
    }
};
WorkspaceVersioner = tslib_1.__decorate([
    (0, inversify_1.injectable)(),
    tslib_1.__param(0, (0, inversify_1.inject)(codiac_common_1.INTERFACES.IBetterLogger)),
    tslib_1.__param(1, (0, inversify_1.inject)(cli_context_repo_1.ProjectContextRepo)),
    tslib_1.__metadata("design:paramtypes", [Object, cli_context_repo_1.ProjectContextRepo
        // @inject(SemanticVersioner) private versioner: SemanticVersioner
    ])
], WorkspaceVersioner);
exports.WorkspaceVersioner = WorkspaceVersioner;
let SemanticVersioner = class SemanticVersioner {
    constructor(logger) {
        this.logger = logger;
    }
    /** Increments the given semantic version nnumber by the given increment.
     *  Returns existing version number if no increment is specified.
     *  Throws if existing version is invalid.
     */
    incrementVersion(existing, increment) {
        try {
            let newVersion = existing;
            if (increment) {
                if (increment.fullName) {
                    this.logger.notice(`Applying new version number ${increment.fullName}...`);
                    newVersion = increment.fullName;
                }
                else if (increment.major) {
                    this.logger.notice(`Incrementing ${"major"} version number...`);
                    newVersion = semver.inc(existing, "major");
                }
                else if (increment.minor) {
                    this.logger.notice(`Incrementing ${"minor"} version number...`);
                    newVersion = semver.inc(existing, "minor");
                }
                else if (increment.patch) {
                    this.logger.notice(`Incrementing ${"patch"} version number...`);
                    newVersion = semver.inc(existing, "patch");
                }
                else if (increment.preId) {
                    this.logger.notice(`Incrementing ${"prerelease"} id and number...`);
                    newVersion = this.addPrereleaseId(existing, increment.preId);
                    newVersion = semver.inc(newVersion, "prerelease");
                }
                else if (increment.prerelease) {
                    this.logger.notice(`Incrementing ${"prerelease"} version number...`);
                    newVersion = semver.inc(existing, "prerelease");
                }
                if (newVersion == null)
                    throw new contracts_1.CodiacError(`Cannot increment; current version number [${existing}] is not a valid semantic version.`);
            }
            return newVersion;
        }
        catch (err) {
            throw err;
        }
    }
    /** Prefixes the prerelease version segment
     * with the given prerelease identifier.
     *
     * using format:  `[major].[minor].[patch]-[id].[prereleaseNumber]`
     *
     * eg:  `1.2.3`  remains  `1.2.3`
     * eg:  `1.2.3-4`  becomes  `1.2.3-thisBranch.4`
     * eg:  `1.2.3-toro.4`  becomes  `1.2.3-thisBranch.4`   */
    addPrereleaseId(version, prereleaseId) {
        let output = version;
        let parts = semver.parse(version);
        // let ultimateId = /[^\w\d\.-]/g.exec(id); 
        let ultimateId = prereleaseId.replace(/[^\w\d\.-]|_/g, "-");
        // console.log(ultimateId);
        if (parts) {
            let pre = parts.prerelease; // semver.prerelease(version);
            let newPre;
            if (pre === null || pre.length == 0) {
                // CASE 0) no prerelease at all.
                newPre = [ultimateId];
            }
            else {
                let newBuild = [...parts.build];
                if (pre.length == 1) {
                    if (typeof pre[0] === 'number') {
                        // CASE 1) prerelease is a number
                        output = `${ultimateId}.${pre[0]}`;
                        newPre = [ultimateId, 0];
                        newBuild = [];
                    }
                    else {
                        // CASE 2) just a string body - no numeric suffix
                        newPre = [ultimateId, 0];
                        newBuild = [];
                    }
                }
                else if (pre.length == 2) {
                    // CASE 3) string body with numeric suffix
                    if (pre[0] === ultimateId) {
                        // CASE 3A) id already in prerelease
                        newPre = [...pre]; // id is already in the prerelease; keep it as is.
                    }
                    else {
                        // CASE 3B) given version has a diff prerelease id
                        newPre = [ultimateId, 0];
                        newBuild = [];
                    }
                }
                else {
                    // more than two segments in prerelease?  
                    // Is this the case when a build number is attached?  eg:  [id].[number]+[build] 
                    newPre = [...pre]; // not supporting this case yet...  
                }
            }
            output = `${parts.major}.${parts.minor}.${parts.patch}`;
            if (newPre.length > 0) {
                output = output.concat("-", newPre.join("."));
            }
        }
        return output;
    }
    /** Rewrites the entire semantic version with the given semantic version.
     *
     * @param version (required) Must be a valid semantic version (eg: `1.2.3` or `1.2.3-something` or `1.2.3-something.4`).
     */
    bump(change) {
        try {
            let newVersion = "";
            return newVersion;
        }
        catch (err) {
            throw err;
        }
    }
    /** Rewrites the entire semantic version with the given semantic version.
     *
     * @param version (required) Must be a valid semantic version (eg: `1.2.3` or `1.2.3-something` or `1.2.3-something.4`).
     */
    set(version) {
        return this.bump({ fullName: version });
    }
    /** Increments the major number of the semantic version for the current image definition.
     *
     * That is: the term `[major]` in the version format:
     * ```
     * [major].[minor].[patch]-[prereleaseName].[prereleaseNumber]
     * ```
     *
     * eg:  `1.2.3` becomes `2.0.0`
     *
     * eg:  `1.2.3-my-branch.3` becomes `2.0.0`
     */
    major() {
        return this.bump({ major: true });
    }
    /** Increments the minor number of the semantic version for the current image definition.
     *
     * That is: the term `[minor]` in the version format:
     * ```
     * [major].[minor].[patch]-[prereleaseName].[prereleaseNumber]
     * ```
     *
     * eg:  `1.2.3` becomes `1.3.0`
     *
     * eg:  `1.2.3-my-branch.3` becomes `1.3.0`
     */
    minor(newValue) {
        return this.bump({ minor: true });
    }
    /** Increments the patch number of the semantic version for the current image definition.
     *
     * That is: the term `[patch]` in the version format:
     * ```
     * [major].[minor].[patch]-[prereleaseName].[prereleaseNumber]
     * ```
     *
     * eg:  `1.2.3` becomes `1.2.4`
     *
     * eg:  `1.2.3-my-branch.3` becomes `1.2.3`
     *
     * *(note: NOT 1.2.4 because prereleases lead up to a patch release, thus the term "cut a version")*
     */
    patch() {
        return this.bump({ patch: true });
    }
    /** Increments the prerelease of the semantic version for the current image definition.
     * Passing in an alphanumeric prerelease name
     * will prefix the number and delimit with a period (.).
     *
     * That is: the `[prereleaseName]` and `[prereleaseNumber]` terms in the version format:
     * ```
     * [major].[minor].[patch]-[prereleaseName].[prereleaseNumber]
     * ```
     *
     * eg: without providing a name...
     *
     * `1.2.3` becomes `1.2.4-0`
     *
     * `1.2.3-my-branch.3` becomes `1.2.3-my-branch.4`
     *
     * `1.2.3-my-branch` becomes `1.2.3-my-branch.0`
     *
     * eg: providing "workitem-pdq" as name...
     *
     * `1.2.3` becomes `1.2.4-workitem-pdq.0`
     *
     * `1.2.3-workitem-pdq.0` becomes `1.2.3-workitem-pdq.1`
     *
     * `1.2.3-my-branch.3` becomes `1.2.3-workitem-pdq.0`
     *
     * `1.2.3-my-branch` becomes `1.2.3-workitem-pdq.0`
     */
    prerelease(name) {
        return this.bump({ prerelease: true, preId: name });
    }
};
SemanticVersioner = tslib_1.__decorate([
    (0, inversify_1.injectable)(),
    tslib_1.__param(0, (0, inversify_1.inject)(codiac_common_1.INTERFACES.IBetterLogger)),
    tslib_1.__metadata("design:paramtypes", [Object])
], SemanticVersioner);
exports.SemanticVersioner = SemanticVersioner;
//# sourceMappingURL=workspace-versioner.js.map