"use strict";
/**
 * By convention, exit codes in this base class are below 100
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@oclif/core");
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const json_parse_better_errors_1 = __importDefault(require("json-parse-better-errors"));
const fetchWithProxy_1 = __importDefault(require("./fetchWithProxy"));
class JBrowseCommand extends core_1.Command {
    async init() { }
    async readFile(location) {
        return fs_1.promises.readFile(location, { encoding: 'utf8' });
    }
    async readJsonFile(location) {
        let contents;
        try {
            contents = await fs_1.promises.readFile(location, { encoding: 'utf8' });
        }
        catch (error) {
            this.error(error instanceof Error ? error : `${error}`, {
                suggestions: [
                    `Make sure the file "${location}" exists or use --out to point to a directory with a config.json`,
                    'Run `jbrowse add-assembly` to create a config file',
                ],
                exit: 40,
            });
        }
        let result;
        try {
            result = (0, json_parse_better_errors_1.default)(contents);
        }
        catch (error) {
            this.error(error instanceof Error ? error : `${error}`, {
                suggestions: [`Make sure "${location}" is a valid JSON file`],
                exit: 50,
            });
        }
        return result;
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    async writeJsonFile(location, contents) {
        this.debug(`Writing JSON file to ${process.cwd()} ${location}`);
        return fs_1.promises.writeFile(location, JSON.stringify(contents, null, 2));
    }
    async resolveFileLocation(location, check = true, inPlace = false) {
        let locationUrl;
        try {
            locationUrl = new URL(location);
        }
        catch (error) {
            // ignore
        }
        if (locationUrl) {
            if (check) {
                const response = await (0, fetchWithProxy_1.default)(locationUrl, { method: 'HEAD' });
                if (!response.ok) {
                    throw new Error(`${locationUrl} result ${response.statusText}`);
                }
            }
            return locationUrl.href;
        }
        let locationPath;
        try {
            locationPath = check ? await fs_1.promises.realpath(location) : location;
        }
        catch (e) {
            // ignore
        }
        if (locationPath) {
            const filePath = path_1.default.relative(process.cwd(), locationPath);
            if (inPlace && filePath.startsWith('..')) {
                this.warn(`Location ${filePath} is not in the JBrowse directory. Make sure it is still in your server directory.`);
            }
            return inPlace ? location : filePath;
        }
        return this.error(`Could not resolve to a file or a URL: "${location}"`, {
            exit: 40,
        });
    }
    async readInlineOrFileJson(inlineOrFileName) {
        let result;
        // see if it's inline JSON
        try {
            result = (0, json_parse_better_errors_1.default)(inlineOrFileName);
        }
        catch (error) {
            this.debug(`Not valid inline JSON, attempting to parse as filename: '${inlineOrFileName}'`);
            // not inline JSON, must be location of a JSON file
            result = await this.readJsonFile(inlineOrFileName);
        }
        return result;
    }
    async fetchGithubVersions() {
        let versions = [];
        for await (const iter of this.fetchVersions()) {
            versions = [...versions, ...iter];
        }
        return versions;
    }
    async getLatest() {
        for await (const versions of this.fetchVersions()) {
            // if a release was just uploaded, or an erroneous build was made
            // then it might have no build asset
            const nonprereleases = versions
                .filter(release => release.prerelease === false)
                .filter(release => release.assets && release.assets.length > 0);
            if (nonprereleases.length > 0) {
                // @ts-expect-error
                const file = nonprereleases[0].assets.find(f => f.name.includes('jbrowse-web'))?.browser_download_url;
                if (!file) {
                    throw new Error('no jbrowse-web download found');
                }
                return file;
            }
        }
        throw new Error('no version tags found');
    }
    async *fetchVersions() {
        let page = 1;
        let result;
        do {
            const response = await (0, fetchWithProxy_1.default)(`https://api.github.com/repos/GMOD/jbrowse-components/releases?page=${page}`);
            if (response.ok) {
                result = (await response.json());
                yield result.filter(release => release.tag_name.startsWith('v'));
                page++;
            }
            else {
                throw new Error(`${response.statusText}`);
            }
        } while (result && result.length > 0);
    }
    async getTag(tag) {
        const response = await (0, fetchWithProxy_1.default)(`https://api.github.com/repos/GMOD/jbrowse-components/releases/tags/${tag}`);
        if (response.ok) {
            const result = (await response.json());
            const file = result?.assets?.find(f => f.name.includes('jbrowse-web'))?.browser_download_url;
            if (!file) {
                this.error('Could not find version specified. Use --listVersions to see all available versions', { exit: 90 });
            }
            return file;
        }
        return this.error(`Could not find version: ${response.statusText}`, {
            exit: 90,
        });
    }
    async getBranch(branch) {
        return `https://s3.amazonaws.com/jbrowse.org/code/jb2/${branch}/jbrowse-web-${branch}.zip`;
    }
}
exports.default = JBrowseCommand;
