"use strict";
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 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const json_parse_better_errors_1 = __importDefault(require("json-parse-better-errors"));
// locals
const fetchWithProxy_1 = __importDefault(require("../fetchWithProxy"));
const base_1 = __importDefault(require("../base"));
class AddConnection extends base_1.default {
    async run() {
        const { args: runArgs, flags: runFlags } = await this.parse(AddConnection);
        const output = runFlags.target || runFlags.out || '.';
        const isDir = fs_1.default.lstatSync(output).isDirectory();
        this.target = isDir ? `${output}/config.json` : output;
        const { connectionUrlOrPath } = runArgs;
        const { assemblyNames, type, name, config, connectionId } = runFlags;
        const { skipCheck, force } = runFlags;
        const url = await this.resolveURL(connectionUrlOrPath, !(skipCheck || force));
        const configContents = await this.readJsonFile(this.target);
        this.debug(`Using config file ${this.target}`);
        if (!configContents.assemblies?.length) {
            this.error('No assemblies found. Please add one before adding connections', { exit: 120 });
        }
        const configType = type || this.determineConnectionType(url);
        const id = connectionId ||
            [configType, assemblyNames, +Date.now()].filter(f => !!f).join('-');
        const connectionConfig = {
            type: configType,
            name: name || id,
            ...(configType === 'UCSCTrackHubConnection'
                ? {
                    hubTxtLocation: {
                        uri: url,
                        locationType: 'UriLocation',
                    },
                }
                : {}),
            ...(configType === 'JBrowse1Connection'
                ? {
                    dataDirLocation: {
                        uri: url,
                        locationType: 'UriLocation',
                    },
                }
                : {}),
            connectionId: id,
            assemblyNames: assemblyNames
                ? assemblyNames.split(',')
                : type === 'JBrowse1Connection'
                    ? [configContents.assemblies[0]?.name]
                    : undefined,
            ...(config ? (0, json_parse_better_errors_1.default)(config) : {}),
        };
        if (!configContents.connections) {
            configContents.connections = [];
        }
        const idx = configContents.connections.findIndex(c => c.connectionId === connectionId);
        if (idx !== -1) {
            if (runFlags.force || runFlags.overwrite) {
                configContents.connections[idx] = connectionConfig;
            }
            else {
                this.error(`Cannot add connection with id ${connectionId}, a connection with that id already exists.\nUse --overwrite if you would like to replace the existing connection`, { exit: 150 });
            }
        }
        else {
            configContents.connections.push(connectionConfig);
        }
        this.debug(`Writing configuration to file ${this.target}`);
        await this.writeJsonFile(this.target, configContents);
        this.log(`${idx !== -1 ? 'Overwrote' : 'Added'} connection "${name}" ${idx !== -1 ? 'in' : 'to'} ${this.target}`);
    }
    async resolveURL(location, check = true) {
        let locationUrl;
        try {
            locationUrl = new URL(location);
        }
        catch (error) {
            this.error('The location provided is not a valid URL', { exit: 160 });
        }
        if (locationUrl) {
            let response;
            try {
                if (check) {
                    // @ts-expect-error
                    response = await (0, fetchWithProxy_1.default)(locationUrl, { method: 'HEAD' });
                }
                if (!response || response.ok) {
                    return locationUrl.href;
                }
                this.error(`Response returned with code ${response.status}`);
            }
            catch (error) {
                // ignore
                this.error(`Unable to fetch from URL, ${error}`, { exit: 170 });
            }
        }
        return this.error(`Could not resolve to a URL: "${location}"`, {
            exit: 180,
        });
    }
    determineConnectionType(url) {
        if (path_1.default.basename(url) === 'hub.txt') {
            return 'UCSCTrackHubConnection';
        }
        if (url.includes('jbrowse/data')) {
            return 'JBrowse1Connection';
        }
        return 'custom';
    }
    isValidJSON(str) {
        try {
            JSON.parse(str);
            return true;
        }
        catch (error) {
            return false;
        }
    }
}
AddConnection.description = 'Add a connection to a JBrowse 2 configuration';
AddConnection.examples = [
    '$ jbrowse add-connection http://mysite.com/jbrowse/data/ -a hg19',
    '$ jbrowse add-connection http://mysite.com/jbrowse/custom_data_folder/ --type JBrowse1Connection -a hg38',
    '$ jbrowse add-connection http://mysite.com/path/to/hub.txt',
    '$ jbrowse add-connection http://mysite.com/path/to/custom_hub_name.txt --type UCSCTrackHubConnection',
    `$ jbrowse add-connection http://mysite.com/path/to/custom --type custom --config '{"uri":{"url":"https://mysite.com/path/to/custom"}, "locationType": "UriLocation"}' -a hg19`,
    '$ jbrowse add-connection https://mysite.com/path/to/hub.txt --connectionId newId --name newName --target /path/to/jb2/installation/config.json',
];
AddConnection.args = {
    connectionUrlOrPath: core_1.Args.string({
        required: true,
        description: `URL of data directory\nFor hub file, usually called hub.txt\nFor JBrowse 1, location of JB1 data directory similar to http://mysite.com/jbrowse/data/ `,
    }),
};
AddConnection.flags = {
    type: core_1.Flags.string({
        char: 't',
        description: 'type of connection, ex. JBrowse1Connection, UCSCTrackHubConnection, custom',
    }),
    assemblyNames: core_1.Flags.string({
        char: 'a',
        description: 'For UCSC, optional: Comma separated list of assembly name(s) to filter from this connection. For JBrowse: a single assembly name',
    }),
    config: core_1.Flags.string({
        char: 'c',
        description: `Any extra config settings to add to connection in JSON object format, such as '{"uri":"url":"https://sample.com"}, "locationType": "UriLocation"}'`,
    }),
    connectionId: core_1.Flags.string({
        description: `Id for the connection that must be unique to JBrowse.  Defaults to 'connectionType-assemblyName-currentTime'`,
    }),
    name: core_1.Flags.string({
        char: 'n',
        description: 'Name of the connection. Defaults to connectionId if not provided',
    }),
    target: core_1.Flags.string({
        description: 'path to config file in JB2 installation directory to write out to.',
    }),
    out: core_1.Flags.string({
        description: 'synonym for target',
    }),
    help: core_1.Flags.help({ char: 'h' }),
    skipCheck: core_1.Flags.boolean({
        description: "Don't check whether or not the data directory URL exists or if you are in a JBrowse directory",
    }),
    overwrite: core_1.Flags.boolean({
        description: 'Overwrites any existing connections if same connection id',
    }),
    force: core_1.Flags.boolean({
        char: 'f',
        description: 'Equivalent to `--skipCheck --overwrite`',
    }),
};
exports.default = AddConnection;
