"use strict";
/*
* Convert the following JS into an equivalent TS class
*
*
* */
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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FactorioApi = void 0;
const axios_1 = __importDefault(require("axios"));
const Logging_1 = require("./Logging");
const path_1 = __importDefault(require("path"));
const util_1 = require("util");
const child_process_1 = require("child_process");
const fs_extra_1 = __importDefault(require("fs-extra"));
const lodash_1 = __importDefault(require("lodash"));
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class FactorioApi {
    static get downloadFolder() {
        return path_1.default.join(FactorioApi.dataPath, 'downloads');
    }
    static loadUserFile(path, dataPath) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Loading user file from ${path}`);
            if (dataPath)
                FactorioApi.dataPath = dataPath;
            try {
                let fd = yield fs_extra_1.default.readJson(path, 'utf8');
                FactorioApi.username = fd.username;
                FactorioApi.token = fd.token;
            }
            catch (e) {
                Logging_1.Logging.log('error', `Could not load user file from ${path} - ${e.message}`);
            }
        });
    }
    static saveUserFile(path) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Saving user file to ${path}`);
            // write to path the user file
            yield fs_extra_1.default.writeFile(path, JSON.stringify({
                username: FactorioApi.username,
                token: FactorioApi.token
            }));
        });
    }
    static clear() {
        Logging_1.Logging.log('info', 'Clearing Factorio API data');
        FactorioApi.versionMap = new Map();
        FactorioApi.latestVersion = undefined;
        FactorioApi.authError = undefined;
        FactorioApi.isAuthenticated = false;
    }
    static queryApi(url, params = {}) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Querying Factorio API at ${url}`);
            if (FactorioApi.isAuthenticated || (this.username && this.token))
                params = Object.assign(Object.assign({}, params), { username: this.username, token: this.token });
            try {
                let resp = yield axios_1.default.get(url, {
                    params
                });
                return resp.data;
            }
            catch (e) {
                if (e.response.status === 401) {
                    FactorioApi.authError = e;
                    FactorioApi.isAuthenticated = false;
                    Logging_1.Logging.log('error', 'Factorio API credentials are invalid. Please check your username and token.');
                    throw new Error(`Factorio API credentials are invalid. Please check your username and token. ${e.message}`);
                }
                else {
                    Logging_1.Logging.log('error', e.message);
                    throw new Error(e.message);
                }
            }
        });
    }
    static initialize(params) {
        return __awaiter(this, void 0, void 0, function* () {
            FactorioApi.username = params.username;
            FactorioApi.token = params.token;
            FactorioApi.dataPath = params.dataPath;
            yield fs_extra_1.default.ensureDir(path_1.default.join(FactorioApi.dataPath, 'factory-storage'));
            if (!FactorioApi.username)
                yield FactorioApi.loadUserFile(path_1.default.join(FactorioApi.dataPath, 'factory-storage', 'user.json'));
            Logging_1.Logging.log('info', 'Initializing Factorio API - Grabbing version information');
            // Any errors are handled upstream
            FactorioApi.versionMap = yield FactorioApi.queryApi('https://factorio.com/get-available-versions');
            FactorioApi.latestVersion = yield FactorioApi.queryApi('https://factorio.com/api/latest-releases');
            if (FactorioApi.versionMap['core-linux64']) {
                FactorioApi.isAuthenticated = true;
                Logging_1.Logging.log('info', `Successfully authenticated with user ${FactorioApi.username}`);
                yield FactorioApi.saveUserFile(path_1.default.join(FactorioApi.dataPath, 'factory-storage', 'user.json'));
            }
            else {
                Logging_1.Logging.log('warn', `Not authenticated with Factorio API. Some features may be unavailable.`);
            }
            FactorioApi.cachedModList = yield FactorioApi.listFactorioMods(undefined, 'max');
        });
    }
    static listFactorioMods(page = undefined, pageSize = undefined, sort = undefined, sortOrder = undefined, namelist = undefined) {
        return __awaiter(this, void 0, void 0, function* () {
            // query parameters can be any of the following...
            /*
            * page integer,
            * page_size integer, or 'max',
            *  sort name, created_at, or updated_at,
            *  sort_order asc or desc,
            *  namelist array of strings - ONLY returns mods with given names,
            * */
            let params = {
                page: page == null ? undefined : page,
                page_size: pageSize == null ? undefined : pageSize,
                sort: sort == null ? undefined : sort,
                sort_order: sortOrder == null ? undefined : sortOrder,
                namelist: namelist == null ? undefined : namelist,
                version: FactorioApi.latestVersion.stable.headless
            };
            return yield FactorioApi.queryApi('https://mods.factorio.com/api/mods', params);
        });
    }
    static getModInfo(name) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield FactorioApi.queryApi(`https://mods.factorio.com/api/mods/${name}/full`);
        });
    }
    static getLatestModVersion(name) {
        return __awaiter(this, void 0, void 0, function* () {
            const modInfo = yield FactorioApi.queryApi(`https://mods.factorio.com/api/mods/${name}`);
            if (modInfo && modInfo.releases && modInfo.releases.length > 0) {
                return modInfo.releases[modInfo.releases.length - 1].version;
            }
            else
                throw new Error(`Could not find latest version of mod ${name}. If this mod is only local, specify the version manually`);
        });
    }
    /*
    * Downloads a given mod, returning the full path to the downloaded file. just returns path if already downloaded
    * */
    static downloadMod(name, version = 'latest') {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Beginning download process for mod ${name}_${version}`);
            /*First, check our filesystem to see if we have that file*/
            // if version is 'latest', we need to query the API to find the latest version, THEN we can state filepath
            let dlPath;
            let modPath;
            // Get the factorio mods together - query the API to find the latest version, then download it
            const modInfo = yield FactorioApi.queryApi(`https://mods.factorio.com/api/mods/${name}`);
            if (modInfo && modInfo.releases && modInfo.releases.length > 0) {
                let dlRelease;
                if (version === 'latest')
                    dlRelease = modInfo.releases[modInfo.releases.length - 1];
                else
                    dlRelease = lodash_1.default.find(modInfo.releases, (r) => {
                        return r.version === version;
                    });
                if (!dlRelease)
                    throw new Error(`Could not find a release for mod ${name} with version ${version}`);
                dlPath = 'https://mods.factorio.com' + dlRelease.download_url;
                modPath = path_1.default.join(FactorioApi.dataPath, 'mods-cache', `${name}_${dlRelease.version}.zip`);
            }
            else
                throw new Error(`Could not mod ${name} with version ${version}`);
            if (fs_extra_1.default.existsSync(modPath)) {
                Logging_1.Logging.log('info', `Found existing download of ${name} ${version} - skipping download`);
            }
            else {
                Logging_1.Logging.log('info', `Downloading mod ${name}_${version} from ${dlPath}...`);
                yield FactorioApi.downloadFile(dlPath, modPath);
            }
            return modPath;
        });
    }
    // Function used for downloading a given file once we have a URL generated. Piping is best for larger downloads like we have.
    static downloadFile(url, outputLocationPath) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Downloading ${url} to ${outputLocationPath}`);
            // Alright axios - you do NOT like to follow instructions on downloading a stream, huh?
            // keeps on stopping after 16.9kb written for any alpha version, but headless works perfectly
            // No idea why! So instead, we're changing this to use CURL instead - cross platform, and works perfectly for anything we need
            // Example working command...
            // curl -G "https://www.factorio.com/get-download/1.1.87/alpha/linux64?username=CharacterOverflow&token=<TOKENHERE>" -L --silent --output test.tar.xz
            // if the output file at the end is less than a kb (really, if its less than like 1mb) then we know something went wrong
            let dlCommand;
            if (FactorioApi.isAuthenticated) {
                dlCommand = `curl -G "${url}?username=${FactorioApi.username}&token=${FactorioApi.token}" -L --silent --output "${outputLocationPath}"`;
            }
            else
                dlCommand = `curl -G "${url}" -L --silent --output "${outputLocationPath}"`;
            // Run the download command, and wait for it to finish.
            // Times out after FACTORIO_DL_TIMEOUT, if set. Otherwise, will run forever!
            // If you have a very fast download speed / drives, you may want to set FACTORIO_DL_TIMEOUT to something like 60000 (60 seconds) as a safety measure
            yield execAsync(dlCommand, {
                cwd: FactorioApi.downloadFolder,
                timeout: process.env.FACTORIO_DL_TIMEOUT ? parseInt(process.env.FACTORIO_DL_TIMEOUT) : 0,
                killSignal: 'SIGKILL'
            });
            // Now we need to check filesize - as far as I'm concerned, if the file is less than 1mb, something went wrong
            let stats = yield fs_extra_1.default.stat(outputLocationPath);
            // if size is under 10kb, throw error. Frankly, if you're trying to download a file under 10k, grab it via axios
            if (stats && stats.size < 10000) {
                Logging_1.Logging.log('error', `Download of ${url} failed - `);
                throw new Error(`Download of ${url} failed - file size is less than 10kb. Something went wrong. Are your credentials correct?`);
            }
            // May change this to return the filesize, for now this works
            return outputLocationPath;
        });
    }
    static downloadGame(version, build, distro) {
        return __awaiter(this, void 0, void 0, function* () {
            let downloadArchive = `${this.downloadFolder}/factorio-${version}_${build}_${distro}.tar.xz`;
            // First, check if the file exists and is greater than 10mb. If so, no download needed!
            // we ONLY use existing downloads on first run through - if there are issues installing, we clear download and try again
            if (fs_extra_1.default.existsSync(downloadArchive) && fs_extra_1.default.statSync(downloadArchive).size > 10000000) {
                Logging_1.Logging.log('info', `Found existing download of Factorio ${version} ${build} ${distro} - skipping download`);
                return downloadArchive;
            }
            else
                yield fs_extra_1.default.remove(downloadArchive);
            yield FactorioApi.downloadFile(`https://www.factorio.com/get-download/${version}/${build}/${distro}`, downloadArchive);
            return downloadArchive;
        });
    }
}
exports.FactorioApi = FactorioApi;
// Gets set to true if the user has successful calls using their token. False is linux headless only!!
FactorioApi.isAuthenticated = false;
FactorioApi.versionMap = new Map();
//# sourceMappingURL=FactorioApi.js.map