"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bmcAddressValidationSchema = exports.day2ApiVipValidationSchema = exports.hostPrefixValidationSchema = exports.ipBlockValidationSchema = exports.vipArrayValidationSchema = exports.vipNoSuffixValidationSchema = exports.hostSubnetValidationSchema = exports.vipRangeValidationSchema = exports.macAddressValidationSchema = exports.ipNoSuffixValidationSchema = exports.ipValidationSchema = void 0;
const tslib_1 = require("tslib");
const Yup = tslib_1.__importStar(require("yup"));
const ip_address_1 = require("ip-address");
const is_cidr_1 = tslib_1.__importDefault(require("is-cidr"));
const is_in_subnet_1 = require("is-in-subnet");
const utils_1 = require("../components/ui/formik/utils");
const utils_2 = require("../components/clusterConfiguration/utils");
const regexes_1 = require("./regexes");
const utils_3 = require("./utils");
const config_1 = require("../config");
const cidr_tools_1 = require("cidr-tools");
const parse_url_1 = tslib_1.__importDefault(require("parse-url"));
const ipValidationSchema = (t) => Yup.string().test('ip-validation', t('ai:Not a valid IP address'), (value) => ip_address_1.Address4.isValid(value || '') || ip_address_1.Address6.isValid(value || ''));
exports.ipValidationSchema = ipValidationSchema;
const ipNoSuffixValidationSchema = (t) => Yup.string().test('ip-validation-no-suffix', t('ai:Not a valid IP address'), (value) => {
    if (!value)
        return true;
    const address = (0, utils_1.getAddress)(value || '');
    return !!address && address.address === address.addressMinusSuffix;
});
exports.ipNoSuffixValidationSchema = ipNoSuffixValidationSchema;
const macAddressValidationSchema = (t) => Yup.string().matches(regexes_1.MAC_REGEX, {
    message: (value) => t('ai:Value "{{value}}" is not valid MAC address.', { value }),
    excludeEmptyString: true,
});
exports.macAddressValidationSchema = macAddressValidationSchema;
const vipRangeValidationSchema = (hostSubnets, { machineNetworks }, allowSuffix, t) => Yup.string().test('vip-validation', t('ai:IP Address is outside of selected subnet'), (value) => {
    var _a;
    if (!value) {
        return true;
    }
    try {
        const validator = allowSuffix ? exports.ipValidationSchema : exports.ipNoSuffixValidationSchema;
        validator(t).validateSync(value);
    }
    catch (err) {
        return true;
    }
    // Find host subnets that match the selected machine networks
    const cidrs = (_a = machineNetworks === null || machineNetworks === void 0 ? void 0 : machineNetworks.map((network) => network.cidr)) !== null && _a !== void 0 ? _a : [];
    const matchingSubnets = hostSubnets.filter((hostSubnet) => cidrs.includes(hostSubnet.subnet));
    for (const hostSubnet of matchingSubnets) {
        if (hostSubnet === null || hostSubnet === void 0 ? void 0 : hostSubnet.subnet) {
            // Workaround for bug in CIM backend. hostIDs are empty
            if (!hostSubnet.hostIDs.length) {
                return true;
            }
            else if ((0, is_in_subnet_1.isInSubnet)(value, hostSubnet.subnet)) {
                return true;
            }
        }
    }
    return false;
});
exports.vipRangeValidationSchema = vipRangeValidationSchema;
const vipBroadcastValidationSchema = ({ machineNetworks }, t) => Yup.string().test('vip-no-broadcast', t('ai:The IP address cannot be a network or broadcast address'), function (value) {
    var _a, _b, _c, _d, _e;
    if (!value) {
        return true;
    }
    const vipAddress = (_a = (0, utils_1.getAddress)(value)) === null || _a === void 0 ? void 0 : _a.address;
    if (!vipAddress) {
        return true;
    }
    const index = (0, utils_3.getArrayIndexFromPath)(this.path || '');
    const machineNetworkCidr = (_e = (_c = (_b = machineNetworks === null || machineNetworks === void 0 ? void 0 : machineNetworks[index]) === null || _b === void 0 ? void 0 : _b.cidr) !== null && _c !== void 0 ? _c : (_d = machineNetworks === null || machineNetworks === void 0 ? void 0 : machineNetworks[0]) === null || _d === void 0 ? void 0 : _d.cidr) !== null && _e !== void 0 ? _e : '';
    if (!machineNetworkCidr) {
        return true;
    }
    const machineNetwork = (0, utils_1.getAddress)(machineNetworkCidr);
    if (!machineNetwork) {
        return true;
    }
    const machineNetworkBroadcast = machineNetwork.endAddress().address;
    const machineNetworkAddress = machineNetwork.startAddress().address;
    return vipAddress !== machineNetworkBroadcast && vipAddress !== machineNetworkAddress;
});
exports.hostSubnetValidationSchema = Yup.string().when(['managedNetworkingType'], {
    is: (managedNetworkingType) => managedNetworkingType === 'clusterManaged',
    then: () => Yup.string().notOneOf([config_1.NO_SUBNET_SET], 'Host subnet must be selected.'),
});
const vipNoSuffixValidationSchema = (hostSubnets, values, t) => Yup.mixed().when(['vipDhcpAllocation', 'managedNetworkingType'], {
    is: (vipDhcpAllocation, managedNetworkingType) => !vipDhcpAllocation && managedNetworkingType !== 'userManaged',
    then: () => {
        // Per-item schema for VIPs (apiVips[*].ip / ingressVips[*].ip)
        // 1) Require value once set
        // 2) Validate IP without suffix
        // 3) Ensure VIP family matches machineNetworks[idx] family
        // 4) Validate range vs selected subnets
        // 5) No broadcast/network address
        // 6) Uniqueness across API/Ingress
        const vipFamilyMatchSchema = Yup.string().test('vip-family-match-machine-network', t('ai:IP family must match the corresponding machine network family.'), function (value) {
            var _a, _b;
            if (!value) {
                return true;
            }
            const index = (0, utils_3.getArrayIndexFromPath)(this.path || '');
            if (Number.isNaN(index)) {
                return true;
            }
            const cidr = ((_b = (_a = values.machineNetworks) === null || _a === void 0 ? void 0 : _a[index]) === null || _b === void 0 ? void 0 : _b.cidr) || '';
            const mnIsV4 = is_cidr_1.default.v4(cidr);
            const mnIsV6 = is_cidr_1.default.v6(cidr);
            // If machine network at index is not selected/valid, don't block validation here
            if (!mnIsV4 && !mnIsV6) {
                return true;
            }
            const ipIsV4 = (0, utils_3.isIPv4Address)(value);
            const ipIsV6 = (0, utils_3.isIPv6Address)(value);
            if (!ipIsV4 && !ipIsV6) {
                return true;
            }
            return mnIsV4 ? ipIsV4 : ipIsV6;
        });
        // Ensure API and Ingress VIPs at the same index are not the same
        const vipUniqueSchema = Yup.string().test('vip-uniqueness', function (value) {
            var _a, _b, _c, _d;
            if (!value) {
                return true;
            }
            const index = (0, utils_3.getArrayIndexFromPath)(this.path || '');
            if (Number.isNaN(index)) {
                return true;
            }
            const apiVip = (_b = (_a = values.apiVips) === null || _a === void 0 ? void 0 : _a[index]) === null || _b === void 0 ? void 0 : _b.ip;
            const ingressVip = (_d = (_c = values.ingressVips) === null || _c === void 0 ? void 0 : _c[index]) === null || _d === void 0 ? void 0 : _d.ip;
            if (!apiVip || !ingressVip) {
                return true;
            }
            if (apiVip === ingressVip) {
                return this.createError({
                    message: index === 0
                        ? t('ai:The primary Ingress and API IP addresses cannot be the same.')
                        : t('ai:The secondary Ingress and API IP addresses cannot be the same.'),
                });
            }
            return true;
        });
        const requiredField = (message) => Yup.string().test('required-field', message, function (value) {
            var _a, _b, _c, _d, _e, _f;
            const index = (0, utils_3.getArrayIndexFromPath)(this.path || '');
            const api1 = ((_c = (_b = (_a = values.apiVips) === null || _a === void 0 ? void 0 : _a[1]) === null || _b === void 0 ? void 0 : _b.ip) !== null && _c !== void 0 ? _c : '').toString().trim();
            const ingress1 = ((_f = (_e = (_d = values.ingressVips) === null || _d === void 0 ? void 0 : _d[1]) === null || _e === void 0 ? void 0 : _e.ip) !== null && _f !== void 0 ? _f : '').toString().trim();
            // Dual-stack: secondary VIPs (index 1) are optional; skip required when both are empty
            if (index === 1 && api1 === '' && ingress1 === '')
                return true;
            return !!value;
        });
        return requiredField(t('ai:Required field'))
            .concat((0, exports.ipNoSuffixValidationSchema)(t))
            .concat(vipFamilyMatchSchema)
            .concat((0, exports.vipRangeValidationSchema)(hostSubnets, values, false, t))
            .concat(vipBroadcastValidationSchema(values, t))
            .concat(vipUniqueSchema);
    },
});
exports.vipNoSuffixValidationSchema = vipNoSuffixValidationSchema;
const vipArrayValidationSchema = (hostSubnets, values, t) => values.managedNetworkingType === 'clusterManaged'
    ? Yup.array().of(Yup.object({
        clusterId: Yup.string(),
        ip: (0, exports.vipNoSuffixValidationSchema)(hostSubnets, values, t),
    }))
    : Yup.array();
exports.vipArrayValidationSchema = vipArrayValidationSchema;
const ipBlockValidationSchema = (reservedCidrs, t) => Yup.string()
    .required('A value is required.')
    .test('valid-ip-address', t('ai:Invalid IP address block. Expected value is a network expressed in CIDR notation (IP/netmask). For example: 123.123.123.0/24, 2055:d7a::/116'), (value) => !!value && (is_cidr_1.default.v4(value) || is_cidr_1.default.v6(value)))
    .test('valid-netmask', t('ai:IPv4 netmask must be between 1-25 and include at least 128 addresses.\nIPv6 netmask must be between 8-128 and include at least 256 addresses.'), (value) => {
    const suffix = parseInt((value || '').split('/')[1]);
    return ((is_cidr_1.default.v4(value || '') && 0 < suffix && suffix < 26) ||
        (is_cidr_1.default.v6(value || '') && 7 < suffix && suffix < 129));
})
    .test('cidr-is-not-unspecified', t('ai:The specified CIDR is invalid because its resulting routing prefix matches the unspecified address.'), (cidr) => {
    const ip = (0, utils_2.getSubnet)(cidr);
    if (ip === null) {
        return false;
    }
    // The first address is used to represent the network
    const startAddress = ip.startAddress().address;
    return startAddress !== regexes_1.IP_V4_ZERO && startAddress !== regexes_1.IP_V6_ZERO;
})
    .test('valid-cidr-base-address', ({ value }) => t('ai:{{value}} is not a valid CIDR', { value: value }), (cidr) => {
    const ip = (0, utils_2.getSubnet)(cidr);
    if (ip === null) {
        return false;
    }
    const networkAddress = ip.startAddress().parsedAddress;
    const ipAddress = ip.parsedAddress;
    const result = ipAddress.every((part, idx) => part === networkAddress[idx]);
    return result;
})
    .test('cidrs-can-not-overlap', t('ai:Provided CIDRs can not overlap.'), (cidr) => {
    try {
        if (cidr && reservedCidrs && reservedCidrs.length > 0) {
            return !(0, cidr_tools_1.overlap)(cidr, reservedCidrs);
        }
    }
    catch (_a) {
        return false;
    }
    // passing by default
    return true;
});
exports.ipBlockValidationSchema = ipBlockValidationSchema;
const hostPrefixValidationSchema = (clusterNetworkCidr, t) => {
    const requiredText = t('ai:Required field');
    const minMaxText = t('ai:The host prefix is a number between 1 and 32 for IPv4 and between 8 and 128 for IPv6.');
    const netBlock = (clusterNetworkCidr || '').split('/')[1];
    if (!netBlock) {
        return Yup.number().required(requiredText).min(1, minMaxText).max(32, minMaxText);
    }
    let netBlockNumber = parseInt(netBlock);
    if (isNaN(netBlockNumber)) {
        netBlockNumber = 1;
    }
    const errorMsgPrefix = t('ai:The host prefix is a number between size of the cluster network CIDR range');
    const errorMsgIPv4 = t('ai:{{errorMsgPrefix}} ({{netBlockNumber}}) and 25.', {
        errorMsgPrefix,
        netBlockNumber,
    });
    const errorMsgIPv6 = t('ai:{{errorMsgPrefix}} (8) and 128.', { errorMsgPrefix });
    if (is_cidr_1.default.v6(clusterNetworkCidr || '')) {
        return Yup.number().required(requiredText).min(8, errorMsgIPv6).max(128, errorMsgIPv6);
    }
    if (is_cidr_1.default.v4(clusterNetworkCidr || '')) {
        return Yup.number()
            .required(requiredText)
            .min(netBlockNumber, errorMsgIPv4)
            .max(25, errorMsgIPv4);
    }
    return Yup.number().required(requiredText);
};
exports.hostPrefixValidationSchema = hostPrefixValidationSchema;
const day2ApiVipValidationSchema = (t) => Yup.string().test('day2-api-vip', t('ai:Provide a valid DNS name or IP Address'), (value) => {
    if (!value) {
        return true;
    }
    return (0, utils_3.isIPorDN)(value);
});
exports.day2ApiVipValidationSchema = day2ApiVipValidationSchema;
const bmcAddressValidationSchema = (t) => {
    return Yup.string()
        .required()
        .test('valid-bmc-address', t('ai:The Value is not valid BMC address, supported protocols are redfish-virtualmedia or idrac-virtualmedia.'), (val) => {
        try {
            const url = (0, parse_url_1.default)(val);
            return ['redfish-virtualmedia', 'idrac-virtualmedia'].includes(url.protocol);
        }
        catch (error) {
            return false;
        }
    });
};
exports.bmcAddressValidationSchema = bmcAddressValidationSchema;
//# sourceMappingURL=addressValidation.js.map