/**
 * Schema Validation Utility
 * Implements MCP Design Guide Section 2.3 principles for robust tool input validation
 */
import { ErrorHandler } from './error-handler.js';
export class SchemaValidator {
    /**
     * Enhanced schema patterns for common MCP tool parameters
     */
    static COMMON_SCHEMAS = {
        // Identifiers
        ID: {
            type: 'string',
            pattern: '^[a-zA-Z0-9\\-_]+$',
            minLength: 1,
            maxLength: 50,
            description: 'Alphanumeric identifier with hyphens and underscores'
        },
        UUID: {
            type: 'string',
            pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
            description: 'UUID v4 format'
        },
        // Names and titles
        NAME: {
            type: 'string',
            minLength: 1,
            maxLength: 100,
            pattern: '^[a-zA-Z0-9\\s\\-_\\.]+$',
            description: 'Name with alphanumeric characters, spaces, hyphens, underscores, and periods'
        },
        TITLE: {
            type: 'string',
            minLength: 1,
            maxLength: 150,
            description: 'Title or summary text'
        },
        // Descriptions
        SHORT_DESCRIPTION: {
            type: 'string',
            minLength: 5,
            maxLength: 500,
            description: 'Short description text'
        },
        LONG_DESCRIPTION: {
            type: 'string',
            minLength: 10,
            maxLength: 2000,
            description: 'Detailed description text'
        },
        // Dates and times
        DATE: {
            type: 'string',
            format: 'date',
            description: 'Date in YYYY-MM-DD format'
        },
        DATETIME: {
            type: 'string',
            format: 'date-time',
            description: 'Date and time in ISO 8601 format'
        },
        // Priority levels
        PRIORITY: {
            type: 'string',
            enum: ['low', 'medium', 'high', 'critical'],
            description: 'Priority level'
        },
        // Status values
        AGILE_STATUS: {
            type: 'string',
            enum: ['planned', 'active', 'completed', 'cancelled'],
            description: 'Agile item status'
        },
        TASK_STATUS: {
            type: 'string',
            enum: ['todo', 'in_progress', 'review', 'testing', 'done', 'blocked'],
            description: 'Task status'
        },
        // Story points (Fibonacci sequence)
        STORY_POINTS: {
            type: 'number',
            enum: [0, 1, 2, 3, 5, 8, 13, 21],
            description: 'Story points using Fibonacci sequence'
        },
        // Duration constraints
        SPRINT_DURATION: {
            type: 'number',
            minimum: 1,
            maximum: 30,
            description: 'Sprint duration in days'
        },
        // Email format
        EMAIL: {
            type: 'string',
            format: 'email',
            description: 'Valid email address'
        },
        // URL format
        URL: {
            type: 'string',
            format: 'uri',
            description: 'Valid URL'
        },
        // Arrays with constraints
        TAG_ARRAY: {
            type: 'array',
            items: {
                type: 'string',
                minLength: 1,
                maxLength: 30,
                pattern: '^[a-zA-Z0-9\\-_]+$'
            },
            maxItems: 10,
            uniqueItems: true,
            description: 'Array of tags (max 10, alphanumeric with hyphens/underscores)'
        },
        STRING_ARRAY: {
            type: 'array',
            items: {
                type: 'string',
                minLength: 1,
                maxLength: 200
            },
            maxItems: 20,
            description: 'Array of strings (max 20 items)'
        }
    };
    /**
     * Create enhanced schema for tool definitions
     */
    static createToolSchema(fields, required = []) {
        const properties = {};
        for (const [fieldName, schemaType] of Object.entries(fields)) {
            if (typeof schemaType === 'string') {
                // Use predefined schema
                if (this.COMMON_SCHEMAS[schemaType]) {
                    properties[fieldName] = { ...this.COMMON_SCHEMAS[schemaType] };
                }
                else {
                    throw new Error(`Unknown schema type: ${schemaType}`);
                }
            }
            else {
                // Use custom schema object
                properties[fieldName] = schemaType;
            }
        }
        return {
            type: 'object',
            properties,
            required,
            additionalProperties: false
        };
    }
    /**
     * Validate parameters against schema with detailed error reporting
     */
    static validateParameters(params, schema, context) {
        if (!params || typeof params !== 'object') {
            throw ErrorHandler.createValidationError('params', params, 'must be an object', context);
        }
        // Check required fields
        if (schema.required) {
            for (const field of schema.required) {
                if (!(field in params) || params[field] === undefined || params[field] === null) {
                    throw ErrorHandler.createValidationError(field, params[field], 'is required', context);
                }
            }
        }
        // Validate each property
        for (const [fieldName, value] of Object.entries(params)) {
            if (value === undefined || value === null) {
                continue; // Skip undefined/null values for optional fields
            }
            const fieldSchema = schema.properties[fieldName];
            if (!fieldSchema && !schema.additionalProperties) {
                throw ErrorHandler.createValidationError(fieldName, value, 'is not allowed (additional properties forbidden)', context);
            }
            if (fieldSchema) {
                this.validateField(fieldName, value, fieldSchema, context);
            }
        }
    }
    /**
     * Validate individual field against its schema
     */
    static validateField(fieldName, value, schema, context) {
        // Type validation
        if (schema.type) {
            if (!this.validateType(value, schema.type)) {
                throw ErrorHandler.createValidationError(fieldName, value, `must be of type ${schema.type}`, context);
            }
        }
        // String validations
        if (schema.type === 'string' && typeof value === 'string') {
            if (schema.minLength && value.length < schema.minLength) {
                throw ErrorHandler.createValidationError(fieldName, value, `must be at least ${schema.minLength} characters long`, context);
            }
            if (schema.maxLength && value.length > schema.maxLength) {
                throw ErrorHandler.createValidationError(fieldName, value, `must be no more than ${schema.maxLength} characters long`, context);
            }
            if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
                throw ErrorHandler.createValidationError(fieldName, value, `must match pattern: ${schema.pattern}`, context);
            }
            if (schema.format) {
                this.validateFormat(fieldName, value, schema.format, context);
            }
        }
        // Number validations
        if (schema.type === 'number' && typeof value === 'number') {
            if (schema.minimum !== undefined && value < schema.minimum) {
                throw ErrorHandler.createValidationError(fieldName, value, `must be at least ${schema.minimum}`, context);
            }
            if (schema.maximum !== undefined && value > schema.maximum) {
                throw ErrorHandler.createValidationError(fieldName, value, `must be no more than ${schema.maximum}`, context);
            }
        }
        // Array validations
        if (schema.type === 'array' && Array.isArray(value)) {
            if (schema.minItems && value.length < schema.minItems) {
                throw ErrorHandler.createValidationError(fieldName, value, `must have at least ${schema.minItems} items`, context);
            }
            if (schema.maxItems && value.length > schema.maxItems) {
                throw ErrorHandler.createValidationError(fieldName, value, `must have no more than ${schema.maxItems} items`, context);
            }
            if (schema.uniqueItems) {
                const uniqueValues = new Set(value);
                if (uniqueValues.size !== value.length) {
                    throw ErrorHandler.createValidationError(fieldName, value, 'must contain unique items only', context);
                }
            }
            // Validate each array item
            if (schema.items) {
                value.forEach((item, index) => {
                    this.validateField(`${fieldName}[${index}]`, item, schema.items, context);
                });
            }
        }
        // Enum validation
        if (schema.enum && !schema.enum.includes(value)) {
            throw ErrorHandler.createValidationError(fieldName, value, `must be one of: ${schema.enum.join(', ')}`, context);
        }
    }
    /**
     * Validate data type
     */
    static validateType(value, expectedType) {
        switch (expectedType) {
            case 'string':
                return typeof value === 'string';
            case 'number':
                return typeof value === 'number' && !isNaN(value);
            case 'boolean':
                return typeof value === 'boolean';
            case 'array':
                return Array.isArray(value);
            case 'object':
                return typeof value === 'object' && value !== null && !Array.isArray(value);
            default:
                return true;
        }
    }
    /**
     * Validate string formats
     */
    static validateFormat(fieldName, value, format, context) {
        switch (format) {
            case 'email':
                const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
                if (!emailRegex.test(value)) {
                    throw ErrorHandler.createValidationError(fieldName, value, 'must be a valid email address', context);
                }
                break;
            case 'uri':
                try {
                    new URL(value);
                }
                catch {
                    throw ErrorHandler.createValidationError(fieldName, value, 'must be a valid URL', context);
                }
                break;
            case 'date':
                const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
                if (!dateRegex.test(value) || isNaN(Date.parse(value))) {
                    throw ErrorHandler.createValidationError(fieldName, value, 'must be a valid date in YYYY-MM-DD format', context);
                }
                break;
            case 'date-time':
                if (isNaN(Date.parse(value))) {
                    throw ErrorHandler.createValidationError(fieldName, value, 'must be a valid ISO 8601 date-time', context);
                }
                break;
        }
    }
    /**
     * Create a decorator for automatic parameter validation
     */
    static validateParams(schema) {
        return function (target, propertyKey, descriptor) {
            const originalMethod = descriptor.value;
            descriptor.value = async function (...args) {
                const params = args[0];
                const context = {
                    tool: propertyKey,
                    module: target.constructor.name,
                };
                SchemaValidator.validateParameters(params, schema, context);
                return await originalMethod.apply(this, args);
            };
            return descriptor;
        };
    }
}
//# sourceMappingURL=schema-validator.js.map