"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.XdrSchemaValidator = void 0;
/**
 * XDR schema validator for validating XDR schemas and values according to RFC 4506.
 */
class XdrSchemaValidator {
    /**
     * Validates an XDR schema structure.
     */
    validateSchema(schema) {
        try {
            return this.validateSchemaInternal(schema);
        }
        catch {
            return false;
        }
    }
    /**
     * Validates if a value conforms to the given XDR schema.
     */
    validateValue(value, schema) {
        try {
            return this.validateValueInternal(value, schema);
        }
        catch {
            return false;
        }
    }
    validateSchemaInternal(schema) {
        if (!schema || typeof schema !== 'object' || !schema.type) {
            return false;
        }
        switch (schema.type) {
            // Primitive types
            case 'void':
            case 'int':
            case 'unsigned_int':
            case 'boolean':
            case 'hyper':
            case 'unsigned_hyper':
            case 'float':
            case 'double':
            case 'quadruple':
                return true;
            case 'enum':
                return this.validateEnumSchema(schema);
            // Wide primitive types
            case 'opaque':
                return this.validateOpaqueSchema(schema);
            case 'vopaque':
                return this.validateVarlenOpaqueSchema(schema);
            case 'string':
                return this.validateStringSchema(schema);
            // Composite types
            case 'array':
                return this.validateArraySchema(schema);
            case 'varray':
                return this.validateVarlenArraySchema(schema);
            case 'struct':
                return this.validateStructSchema(schema);
            case 'union':
                return this.validateUnionSchema(schema);
            case 'optional':
                return this.validateOptionalSchema(schema);
            case 'const':
                return this.validateConstantSchema(schema);
            default:
                return false;
        }
    }
    validateEnumSchema(schema) {
        if (!schema.values || typeof schema.values !== 'object') {
            return false;
        }
        const values = Object.values(schema.values);
        const uniqueValues = new Set(values);
        // Check for duplicate values
        if (values.length !== uniqueValues.size) {
            return false;
        }
        // Check that all values are integers
        return values.every((value) => Number.isInteger(value));
    }
    validateOpaqueSchema(schema) {
        return typeof schema.size === 'number' && Number.isInteger(schema.size) && schema.size >= 0;
    }
    validateVarlenOpaqueSchema(schema) {
        return !schema.size || (typeof schema.size === 'number' && Number.isInteger(schema.size) && schema.size >= 0);
    }
    validateStringSchema(schema) {
        return !schema.size || (typeof schema.size === 'number' && Number.isInteger(schema.size) && schema.size >= 0);
    }
    validateArraySchema(schema) {
        if (!schema.elements || typeof schema.size !== 'number' || !Number.isInteger(schema.size) || schema.size < 0) {
            return false;
        }
        return this.validateSchemaInternal(schema.elements);
    }
    validateVarlenArraySchema(schema) {
        if (!schema.elements) {
            return false;
        }
        if (schema.size !== undefined) {
            if (typeof schema.size !== 'number' || !Number.isInteger(schema.size) || schema.size < 0) {
                return false;
            }
        }
        return this.validateSchemaInternal(schema.elements);
    }
    validateStructSchema(schema) {
        if (!Array.isArray(schema.fields)) {
            return false;
        }
        const fieldNames = new Set();
        for (const field of schema.fields) {
            if (!Array.isArray(field) || field.length !== 2) {
                return false;
            }
            const [fieldSchema, fieldName] = field;
            if (typeof fieldName !== 'string' || fieldName === '') {
                return false;
            }
            if (fieldNames.has(fieldName)) {
                return false; // Duplicate field name
            }
            fieldNames.add(fieldName);
            if (!this.validateSchemaInternal(fieldSchema)) {
                return false;
            }
        }
        return true;
    }
    validateUnionSchema(schema) {
        if (!Array.isArray(schema.arms) || schema.arms.length === 0) {
            return false;
        }
        const discriminants = new Set();
        for (const arm of schema.arms) {
            if (!Array.isArray(arm) || arm.length !== 2) {
                return false;
            }
            const [discriminant, armSchema] = arm;
            // Check for duplicate discriminants
            if (discriminants.has(discriminant)) {
                return false;
            }
            discriminants.add(discriminant);
            // Validate discriminant type
            if (typeof discriminant !== 'number' && typeof discriminant !== 'string' && typeof discriminant !== 'boolean') {
                return false;
            }
            if (!this.validateSchemaInternal(armSchema)) {
                return false;
            }
        }
        // Validate default schema if present
        if (schema.default && !this.validateSchemaInternal(schema.default)) {
            return false;
        }
        return true;
    }
    validateOptionalSchema(schema) {
        if (!schema.element) {
            return false;
        }
        return this.validateSchemaInternal(schema.element);
    }
    validateConstantSchema(schema) {
        if (typeof schema.value !== 'number' || !Number.isInteger(schema.value)) {
            return false;
        }
        return true;
    }
    validateValueInternal(value, schema) {
        switch (schema.type) {
            case 'void':
                return value === null || value === undefined;
            case 'int':
                return typeof value === 'number' && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647;
            case 'unsigned_int':
                return typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 4294967295;
            case 'boolean':
                return typeof value === 'boolean';
            case 'hyper':
                return (typeof value === 'number' && Number.isInteger(value)) || typeof value === 'bigint';
            case 'unsigned_hyper':
                return ((typeof value === 'number' && Number.isInteger(value) && value >= 0) ||
                    (typeof value === 'bigint' && value >= BigInt(0)));
            case 'float':
            case 'double':
            case 'quadruple':
                return typeof value === 'number';
            case 'enum': {
                const enumSchema = schema;
                return typeof value === 'string' && value in enumSchema.values;
            }
            case 'opaque': {
                const opaqueSchema = schema;
                return value instanceof Uint8Array && value.length === opaqueSchema.size;
            }
            case 'vopaque': {
                const vopaqueSchema = schema;
                return value instanceof Uint8Array && (!vopaqueSchema.size || value.length <= vopaqueSchema.size);
            }
            case 'string': {
                const stringSchema = schema;
                return typeof value === 'string' && (!stringSchema.size || value.length <= stringSchema.size);
            }
            case 'array': {
                const arraySchema = schema;
                return (Array.isArray(value) &&
                    value.length === arraySchema.size &&
                    value.every((item) => this.validateValueInternal(item, arraySchema.elements)));
            }
            case 'varray': {
                const varraySchema = schema;
                return (Array.isArray(value) &&
                    (!varraySchema.size || value.length <= varraySchema.size) &&
                    value.every((item) => this.validateValueInternal(item, varraySchema.elements)));
            }
            case 'struct': {
                const structSchema = schema;
                if (!value || typeof value !== 'object' || Array.isArray(value)) {
                    return false;
                }
                const valueObj = value;
                return structSchema.fields.every(([fieldSchema, fieldName]) => fieldName in valueObj && this.validateValueInternal(valueObj[fieldName], fieldSchema));
            }
            case 'union': {
                const unionSchema = schema;
                // For union validation, we need additional context about which arm is selected
                // This is a simplified validation - in practice, the discriminant would be known
                const matchesArm = unionSchema.arms.some(([, armSchema]) => this.validateValueInternal(value, armSchema));
                const matchesDefault = unionSchema.default ? this.validateValueInternal(value, unionSchema.default) : false;
                return matchesArm || matchesDefault;
            }
            case 'optional': {
                const optionalSchema = schema;
                // Optional values can be null/undefined or match the element schema
                return value === null || value === undefined || this.validateValueInternal(value, optionalSchema.element);
            }
            case 'const': {
                // Constants have no runtime value validation
                return true;
            }
            default:
                return false;
        }
    }
}
exports.XdrSchemaValidator = XdrSchemaValidator;
//# sourceMappingURL=XdrSchemaValidator.js.map