import assert from 'node:assert';
import BoxError from '../boxerror.js';
import constants from '../constants.js';
import logger from '../logger.js';
import safe from 'safetydance';
import superagent from '@cloudron/superagent';

const { log } = logger('network/generic');


const gCache = { ipv4: {}, ipv6: {} }; // each has { timestamp, value, request }

async function getIP(type) {
    const url = `https://${type}.api.cloudron.io/api/v1/helper/public_ip`;

    gCache[type].value = null; // clear the obsolete value

    log(`getIP: querying ${url} to get ${type}`);
    const [networkError, response] = await safe(superagent.get(url).timeout(30 * 1000).retry(2).ok(() => true));

    if (networkError || response.status !== 200) {
        log(`getIP: Error getting IP. ${networkError?.message || response.status}`);
        throw new BoxError(BoxError.EXTERNAL_ERROR, `Unable to detect ${type}. API server (${type}.api.cloudron.io) unreachable`);
    }

    if (!response.body?.ip) {
        log('get: Unexpected answer. No "ip" found in response body.', response.body);
        throw new BoxError(BoxError.EXTERNAL_ERROR, `Unable to detect ${type}. No IP found in response`);
    }

    gCache[type].value = response.body.ip;
    gCache[type].timestamp = Date.now();
}

async function getIPv4(config) {
    assert.strictEqual(typeof config, 'object');

    if (constants.TEST) return '127.0.0.1';

    if (gCache.ipv4.value && (Date.now() - gCache.ipv4.timestamp <= 5 * 60 * 1000)) return gCache.ipv4.value;

    if (!gCache.ipv4.request) gCache.ipv4.request = getIP('ipv4');

    const thisRequest = gCache.ipv4.request;
    try {
        await thisRequest;
    } finally { // when we have multiple waiters, protect against a new caller creating a new request
        if (thisRequest === gCache.ipv4.request) gCache.ipv4.request = null;
    }

    return gCache.ipv4.value;
}

async function getIPv6(config) {
    assert.strictEqual(typeof config, 'object');

    if (constants.TEST) return '::1';

    if (gCache.ipv6.value && (Date.now() - gCache.ipv6.timestamp <= 5 * 60 * 1000)) return gCache.ipv6.value;

    if (!gCache.ipv6.request) gCache.ipv6.request = getIP('ipv6');

    const thisRequest = gCache.ipv6.request;
    try {
        await thisRequest;
    } finally { // when we have multiple waiters, protect against a new caller creating a new request
        if (thisRequest === gCache.ipv6.request) gCache.ipv6.request = null;
    }

    return gCache.ipv6.value;
}

async function testIPv4Config(config) {
    assert.strictEqual(typeof config, 'object');

    return null;
}

async function testIPv6Config(config) {
    assert.strictEqual(typeof config, 'object');

    return null;
}

export default {
    getIPv4,
    getIPv6,
    testIPv4Config,
    testIPv6Config
};
