"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());
    });
};
const debug_1 = require('../debug');
const debug = debug_1.default('pnpm:fetch');
const crypto = require('crypto');
const gunzip = require('gunzip-maybe');
const tar = require('tar-fs');
const fs = require('fs');
function createRemoteTarballFetcher(dist) {
    return (target, opts) => fetchFromRemoteTarball(target, dist, opts);
}
exports.createRemoteTarballFetcher = createRemoteTarballFetcher;
function createLocalTarballFetcher(dist) {
    return (target, opts) => fetchFromLocalTarball(target, dist, opts);
}
exports.createLocalTarballFetcher = createLocalTarballFetcher;
/**
 * Fetches a tarball `tarball` and extracts it into `dir`
 */
function fetchFromRemoteTarball(dir, dist, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        const stream = yield opts.got.getStream(dist.tarball);
        return fetchStream(dir, dist, opts.log, stream);
    });
}
exports.fetchFromRemoteTarball = fetchFromRemoteTarball;
function fetchFromLocalTarball(dir, dist, opts) {
    return unpackStream(fs.createReadStream(dist.tarball), dir);
}
exports.fetchFromLocalTarball = fetchFromLocalTarball;
function fetchStream(dir, dist, log, stream) {
    return new Promise((resolve, reject) => {
        const actualShasum = crypto.createHash('sha1');
        let size = 0;
        let downloaded = 0;
        unpackStream(stream
            .on('response', start)
            .on('data', (_) => { actualShasum.update(_); })
            .on('error', reject), dir).then(finish).catch(reject);
        // without pausing, gunzip/tar-fs would miss the beginning of the stream
        stream.resume();
        function start(res) {
            if (res.statusCode !== 200) {
                return reject(new Error('' + dist.tarball + ': invalid response ' + res.statusCode));
            }
            log('download-start');
            if ('content-length' in res.headers) {
                size = +res.headers['content-length'];
                res.on('data', (chunk) => {
                    downloaded += chunk.length;
                    log('downloading', { done: downloaded, total: size });
                });
            }
        }
        function finish() {
            const digest = actualShasum.digest('hex');
            debug(`finish ${dist.shasum} ${dist.tarball}`);
            if (dist.shasum && digest !== dist.shasum) {
                return reject(new Error('' + dist.tarball + ': incorrect shasum (expected ' + dist.shasum + ', got ' + digest + ')'));
            }
            return resolve(dir);
        }
    });
}
function unpackStream(stream, dir) {
    return new Promise((resolve, reject) => {
        stream
            .pipe(gunzip()).on('error', reject)
            .pipe(tar.extract(dir, { strip: 1 })).on('error', reject)
            .on('finish', resolve);
    });
}
//# sourceMappingURL=fetch.js.map