#!/usr/bin/env node

var semver = require('semver');
var fs = require('fs');
var path = require('path');
var shared = require('../lib/shared');

process.on('uncaughtException', function (err) {
    exitWithError(err);
});

var engine = process.env.npm_package_engines_vscode;
if (!engine) {
    exitWithError('Missing VSCode engine declaration in package.json.');
}

var vscodeDtsTypescriptPath = path.join(path.dirname(__dirname), 'vscode.d.ts');

console.log('Detected VS Code engine version: ' + engine);

getURLMatchingEngine(engine, function (_, data) {
    console.log('Fetching vscode.d.ts from: ' + data.url);

    shared.getContents(data.url, process.env.GITHUB_TOKEN, null, function (error, contents) {
        if (error) {
            exitWithError(error);
        }

        if (contents === 'Not Found') {
            exitWithError(new Error('Could not find vscode.d.ts at the provided URL. Please report this to https://github.com/Microsoft/vscode/issues'));
        }

        if (data.version !== '*' && semver.lt(data.version, '1.7.0')) {
            // Older versions of vscode.d.ts need a massage to play nice.
            contents = vscodeDtsToTypescript(contents);
        }

        fs.writeFileSync(vscodeDtsTypescriptPath, contents);

        console.log('vscode.d.ts successfully installed!\n');
    });
});

function vscodeDtsToTypescript(contents) {
    var markerHit = false;
    var lines = contents.split('\n').filter(function (line) {
        if (!markerHit && (line === '// when used for JS*' || line === 'declare module \'vscode\' {')) {
            markerHit = true;
        }

        return !markerHit;
    });

    lines.unshift('/// <reference path="./thenable.d.ts" />');
    lines.push('export = vscode;'); // this is to enable TS module resolution support

    return lines.join('\n');
}

function getURLMatchingEngine(engine, callback) {
    if (engine === '*') {
        // master
        return callback(null, {
            url: 'https://raw.githubusercontent.com/Microsoft/vscode/master/src/vs/vscode.d.ts',
            version: '*'
        });
    }

    shared.getContents('https://vscode-update.azurewebsites.net/api/releases/stable', null, { "X-API-Version": "2" }, function (error, tagsRaw) {
        if (error) {
            exitWithError(error);
        }

        var tagsAndCommits;
        try {
            tagsAndCommits = JSON.parse(tagsRaw);
        } catch (error) {
            exitWithError(error);
        }

        var mapTagsToCommits = Object.create(null);
        for (var i = 0; i < tagsAndCommits.length; i++) {
            var tagAndCommit = tagsAndCommits[i];
            mapTagsToCommits[tagAndCommit.version] = tagAndCommit.id;
        }

        var tags = Object.keys(mapTagsToCommits);

        var tag = minSatisfying(tags, engine);

        // check if master is on the version specified
        if (!tag) {
            return shared.getContents('https://raw.githubusercontent.com/Microsoft/vscode/master/package.json', process.env.GITHUB_TOKEN, null, function (error, packageJson) {
                if (error) {
                    exitWithError(error);
                }

                var version = JSON.parse(packageJson).version;
                if (semver.satisfies(version, engine)) {
                    // master
                    return callback(null, {
                        url: 'https://raw.githubusercontent.com/Microsoft/vscode/master/src/vs/vscode.d.ts',
                        version: version
                    });
                }

                exitWithError('Could not find satifying VSCode for version ' + engine + ' in the tags: [' + tags.join(', ') + '] or on master: ' + version);
            });
        }

        console.log('Found minimal version that qualifies engine range: ' + tag);

        return callback(null, {
            url: 'https://raw.githubusercontent.com/Microsoft/vscode/' + mapTagsToCommits[tag] + '/src/vs/vscode.d.ts',
            version: tag
        });
    });
}

function minSatisfying(versions, range) {
    return versions.filter(function (version) {
        try {
            return semver.satisfies(version, range);
        } catch (error) {
            return false; // version might be invalid so we return as not matching
        }
    }).sort(function (a, b) {
        return semver.compare(a, b);
    })[0] || null;
}

function exitWithError(error) {
    console.error('Error installing vscode.d.ts: ' + error.toString());
    process.exit(1);
}