Source: instanceList.js

export const DEFAULT_LISTS = {
    mastodon: ["https://mastodon.social", "https://fosstodon.org", "https://mstdn.social", "https://mastodon.online", "https://mastodon.world"],
    lemmy: ["https://lemmy.ml", "https://lemmy.world", "https://lemm.ee"],
};

const INST_LISTS = "instanceLists";
const ALLOWED_NETWORKS = new Set(["mastodon", "lemmy"]);

/**
 * Loads hardcoded default instance lists into localStorage.
 * @param {Storage} storage The storage object to reset to default.
 */
function setDefaultLists(storage = localStorage) {
    saveLists(DEFAULT_LISTS, storage);
}

/**
 * Fetches current instance lists from localStorage. If there aren't any in localStorage,
 * sets them to the hardcoded defaults before returning them.
 * @returns The current set of instance lists, as an object of arrays indexed by network name.
 */
export function fetchInstanceLists(storage = localStorage) {
    if (storage.getItem(INST_LISTS) === null) {
        setDefaultLists(storage);
        // can return DEFAULT_LISTS as a fallback against localStorage not working
        // but is that even necessary?
    }
    return JSON.parse(storage.getItem(INST_LISTS));
}

/**
 * Takes an object, stringifies it and stores it in storage.
 * @param {Object} instanceLists An object, should be pairs of network names and URL string arrays
 * @param {*} storage The storage object to write to with the key 'instanceLists'
 */
export function saveLists(instanceLists, storage = localStorage) {
    storage.setItem(INST_LISTS, JSON.stringify(instanceLists));
}

/**
 * @param {string} network The name of the network (e.g. 'mastodon' or 'lemmy')
 * @param {string} url The URL of the instance to add.
 * @returns {Promise<boolean>} Promise which resolves to True if the instance addition was successful, False otherwise
 */
export async function addInstance(network, url, storage = localStorage) {
    if (!(ALLOWED_NETWORKS.has(network)) || !validUrl(url)) return false;

    const instanceList = fetchInstanceLists(storage);
    // network is already guaranteed to be in ALLOWED_NETWORKS and thus on the default list
    instanceList[network].unshift(url);
    storage.setItem(INST_LISTS, JSON.stringify(instanceList));
    return true;
}

/**
 * Calls removeInstance -- if it's successful, remove corresponding element from UI.
 * @param {string} network
 * @param {string} url
 * @param {Storage} storage
 */
export function handleRemoveInstance(network, url, storage = localStorage) {
    const i = removeInstance(network, url, storage);
    if (i !== -1) {
        const list = document.getElementById(`${network}-instance-list`);
        // first child will always be add instance input box
        // index 0 of instance list will be child 1
        list.removeChild(list.children[i + 1]);
    }
}
/**
 * Removes an instance from localStorage. Currently O(n) with respect to number of instances.
 * @param {string} network
 * @param {string} url
 * @param {Storage} storage
 * @returns {number} Index removed if successful, -1 otherwise
 */
export function removeInstance(network, url, storage = localStorage) {
    const instanceLists = fetchInstanceLists(storage);
    if (network in instanceLists && instanceLists[network].includes(url)) {
        const ind = instanceLists[network].indexOf(url);
        instanceLists[network].splice(ind, 1);
        saveLists(instanceLists, storage);
        return ind;
    }
    return -1;
}

/**
 * @param {string} url A URL string to validate
 * @returns {boolean} true if the URL string is valid, false otherwise
 */
function validUrl(url) {
    try {
        new URL(url);
        return true;
    }
    catch {
        return false;
    }
}