"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const GalleryInterfaces_1 = require("azure-devops-node-api/interfaces/GalleryInterfaces");
const package_1 = require("./package");
const tmp = require("tmp");
const store_1 = require("./store");
const util_1 = require("./util");
const denodeify = require("denodeify");
const yauzl = require("yauzl");
const semver = require("semver");
const cp = require("child_process");
const exec = denodeify(cp.exec, (err, stdout, stderr) => [err, { stdout, stderr }]);
const tmpName = denodeify(tmp.tmpName);
function readManifestFromPackage(packagePath) {
    return new Promise((c, e) => {
        yauzl.open(packagePath, (err, zipfile) => {
            if (err) {
                return e(err);
            }
            const onEnd = () => e(new Error('Manifest not found'));
            zipfile.once('end', onEnd);
            zipfile.on('entry', entry => {
                if (!/^extension\/package\.json$/i.test(entry.fileName)) {
                    return;
                }
                zipfile.removeListener('end', onEnd);
                zipfile.openReadStream(entry, (err, stream) => {
                    if (err) {
                        return e(err);
                    }
                    const buffers = [];
                    stream.on('data', buffer => buffers.push(buffer));
                    stream.once('error', e);
                    stream.once('end', () => {
                        try {
                            c(JSON.parse(Buffer.concat(buffers).toString('utf8')));
                        }
                        catch (err) {
                            e(err);
                        }
                    });
                });
            });
        });
    });
}
function _publish(packagePath, pat, manifest) {
    return __awaiter(this, void 0, void 0, function* () {
        const api = yield util_1.getGalleryAPI(pat);
        const packageStream = fs.createReadStream(packagePath);
        const name = `${manifest.publisher}.${manifest.name}`;
        const fullName = `${name}@${manifest.version}`;
        console.log(`Publishing ${fullName}...`);
        return api.getExtension(null, manifest.publisher, manifest.name, null, GalleryInterfaces_1.ExtensionQueryFlags.IncludeVersions)
            .catch(err => err.statusCode === 404 ? null : Promise.reject(err))
            .then(extension => {
            if (extension && extension.versions.some(v => v.version === manifest.version)) {
                return Promise.reject(`${fullName} already exists. Version number cannot be the same.`);
            }
            var promise = extension
                ? api.updateExtension(undefined, packageStream, manifest.publisher, manifest.name)
                : api.createExtension(undefined, packageStream);
            return promise
                .catch(err => Promise.reject(err.statusCode === 409 ? `${fullName} already exists.` : err))
                .then(() => util_1.log.done(`Published ${fullName}\nYour extension will live at ${util_1.getPublishedUrl(name)} (might take a few seconds for it to show up).`));
        })
            .catch(err => {
            const message = err && err.message || '';
            if (/Invalid Resource/.test(message)) {
                err.message = `${err.message}\n\nYou're likely using an expired Personal Access Token, please get a new PAT.\nMore info: https://aka.ms/vscodepat`;
            }
            return Promise.reject(err);
        });
    });
}
function versionBump(cwd = process.cwd(), version, commitMessage) {
    if (!version) {
        return Promise.resolve(null);
    }
    switch (version) {
        case 'major':
        case 'minor':
        case 'patch':
            break;
        case 'premajor':
        case 'preminor':
        case 'prepatch':
        case 'prerelease':
        case 'from-git':
            return Promise.reject(`Not supported: ${version}`);
        default:
            if (!semver.valid(version)) {
                return Promise.reject(`Invalid version ${version}`);
            }
    }
    let command = `npm version ${version}`;
    if (commitMessage) {
        command = `${command} -m "${commitMessage}"`;
    }
    // call `npm version` to do our dirty work
    return exec(command, { cwd })
        .then(({ stdout, stderr }) => {
        process.stdout.write(stdout);
        process.stderr.write(stderr);
        return Promise.resolve(null);
    })
        .catch(err => Promise.reject(err.message));
}
function publish(options = {}) {
    let promise;
    if (options.packagePath) {
        if (options.version) {
            return Promise.reject(`Not supported: packagePath and version.`);
        }
        promise = readManifestFromPackage(options.packagePath)
            .then(manifest => ({ manifest, packagePath: options.packagePath }));
    }
    else {
        const cwd = options.cwd;
        const baseContentUrl = options.baseContentUrl;
        const baseImagesUrl = options.baseImagesUrl;
        const useYarn = options.useYarn;
        promise = versionBump(options.cwd, options.version, options.commitMessage)
            .then(() => tmpName())
            .then(packagePath => package_1.pack({ packagePath, cwd, baseContentUrl, baseImagesUrl, useYarn }));
    }
    return promise.then(({ manifest, packagePath }) => {
        if (!options.noVerify && manifest.enableProposedApi) {
            throw new Error('Extensions using proposed API (enableProposedApi: true) can\'t be published to the Marketplace');
        }
        const patPromise = options.pat
            ? Promise.resolve(options.pat)
            : store_1.getPublisher(manifest.publisher).then(p => p.pat);
        return patPromise.then(pat => _publish(packagePath, pat, manifest));
    });
}
exports.publish = publish;
function unpublish(options = {}) {
    let promise;
    if (options.id) {
        const [publisher, name] = options.id.split('.');
        promise = Promise.resolve(({ publisher, name }));
    }
    else {
        promise = package_1.readManifest(options.cwd);
    }
    return promise.then(({ publisher, name }) => {
        const fullName = `${publisher}.${name}`;
        const pat = options.pat
            ? Promise.resolve(options.pat)
            : store_1.getPublisher(publisher).then(p => p.pat);
        return util_1.read(`This will FOREVER delete '${fullName}'! Are you sure? [y/N] `)
            .then(answer => /^y$/i.test(answer) ? null : Promise.reject('Aborted'))
            .then(() => pat)
            .then(util_1.getGalleryAPI)
            .then(api => api.deleteExtension(publisher, name))
            .then(() => util_1.log.done(`Deleted extension: ${fullName}!`));
    });
}
exports.unpublish = unpublish;
