import {type NetworkInterfaceInfo, networkInterfaces} from 'node:os';

const PORT_RANGE_MATCHER = /^(\d+|(0x[\da-f]+))(-(\d+|(0x[\da-f]+)))?$/i;
function validPort(port: number) {
    if (port > 0xFFFF) throw new Error(`bad port ${port}`);
    return port;
}

/**
 * parse port range. port can be number or string representing comma separated
 *   list of port ranges for e.g. "3434,8380-8385"
 * @param port
 */
export function* portRange(port: number | string): Generator<number> {
    if (typeof port === 'string') {
        for (const portRange of port.split(',')) {
            const trimmed = portRange.trim();
            const matchResult = PORT_RANGE_MATCHER.exec(trimmed);
            if (matchResult) {
                const start = parseInt(matchResult[1]);
                const end = parseInt(matchResult[4] ?? matchResult[1]);
                for (let i = validPort(start); i < validPort(end) + 1; i++) {
                    yield i;
                }
            }
            else {
                throw new Error(`'${portRange}' is not a valid port or range.`);
            }
        }
    } else {
        yield validPort(port);
    }
}

export const localIp = (() => {
    function first<T>(a: T[]): T | undefined {
        return a.length > 0 ? a[0] : undefined;
    }
    const addresses = Object.values(networkInterfaces())
        .flatMap((details?: NetworkInterfaceInfo[]) => {
            return (details ?? []).filter((info) => info.family === 'IPv4');
        }).reduce((acc, info) => {
            acc[info.internal ? 'internal' : 'external'].push(info);
            return acc;
        }, {internal: [] as NetworkInterfaceInfo[], external: [] as NetworkInterfaceInfo[]});
    return (first(addresses.internal) ??  first(addresses.external))?.address;
})();
