import assert from 'node:assert';
import net from 'node:net';


function isValid(ip) {
    assert.strictEqual(typeof ip, 'string');

    const type = net.isIP(ip);
    return type === 4 || type === 6;
}

function isValidCIDR(cidr) {
    assert.strictEqual(typeof cidr, 'string');

    const parts = cidr.split('/');
    if (parts.length !== 2) return false;

    const [ ip, prefixString ] = parts;
    const type = net.isIP(ip);
    if (type === 0) return false;

    const prefix = Number.parseInt(prefixString, 10);
    if (!Number.isInteger(prefix) || prefix < 0 || (prefix > (type === 4 ? 32 : 128)) || String(prefix) !== prefixString) return false;

    return true;
}

function isEqual(ip1, ip2) {
    assert.strictEqual(typeof ip1, 'string');
    assert.strictEqual(typeof ip2, 'string');

    if (ip1 === ip2) return true;

    const type1 = net.isIP(ip1), type2 = net.isIP(ip2);
    if (type1 === 0 || type2 === 0) return false; // otherwise, it will throw invalid socket address below

    // use blocklist to compare since strings may not be in RFC 5952 format
    const blockList = new net.BlockList();
    blockList.addAddress(ip1, `ipv${type1}`);
    return blockList.check(ip2, `ipv${type2}`);
}

function includes(cidr, ip) {
    assert.strictEqual(typeof cidr, 'string');
    assert.strictEqual(typeof ip, 'string');

    const type = net.isIP(ip);
    const [ subnet, prefix ] = cidr.split('/');
    const subnetType = net.isIP(subnet);

    const blockList = new net.BlockList();
    blockList.addSubnet(subnet, parseInt(prefix, 10), `ipv${subnetType}`);
    return blockList.check(ip, `ipv${type}`);
}

export default {
    isValid,
    isValidCIDR,
    isEqual,
    includes,
};
