/**
 * Input Validation Middleware for Cloudflare Workers
 * Uses Zod for schema validation and sanitization
 */

import { z } from 'zod';
import { SecurityError, SecurityErrorType } from 'shared/types/errors';
import { createLogger } from '../logger';
import { validatePassword, validateEmail, validateUsername } from './validationUtils';

const logger = createLogger('InputValidator');

/**
 * Input validation middleware using Zod schemas
 * 
 * @param request - The incoming request
 * @param schema - Zod schema for validation
 * @returns Validated data or throws SecurityError
 */
export async function validateInput<T extends z.ZodSchema>(
    request: Request,
    schema: T
): Promise<z.infer<T>> {
    try {
        // Handle different content types
        const contentType = request.headers.get('content-type');
        let data: unknown;
        
        if (contentType?.includes('application/json')) {
            data = await parseJSON(request);
        } else if (contentType?.includes('application/x-www-form-urlencoded')) {
            data = await parseFormData(request);
        } else if (contentType?.includes('multipart/form-data')) {
            data = await parseMultipartFormData(request);
        } else if (request.method === 'GET' || request.method === 'DELETE') {
            data = parseQueryParams(request);
        } else {
            throw new SecurityError(
                SecurityErrorType.INVALID_INPUT,
                'Unsupported content type',
                400
            );
        }
        
        // Validate with Zod
        const result = schema.safeParse(data);
        
        if (!result.success) {
            logger.warn('Validation failed', { 
                errors: result.error.issues,
                path: new URL(request.url).pathname 
            });
            
            throw new SecurityError(
                SecurityErrorType.INVALID_INPUT,
                formatValidationErrors(result.error),
                400
            );
        }
        
        logger.debug('Input validated successfully', { 
            path: new URL(request.url).pathname 
        });
        
        return result.data;
    } catch (error) {
        if (error instanceof SecurityError) {
            throw error;
        }
        
        logger.error('Input validation error', error);
        throw new SecurityError(
            SecurityErrorType.INVALID_INPUT,
            'Invalid request data',
            400
        );
    }
}

/**
 * Parse JSON body with size limit
 */
async function parseJSON(request: Request): Promise<unknown> {
    const text = await request.text();
    
    // Check size limit (1MB)
    if (text.length > 1024 * 1024) {
        throw new SecurityError(
            SecurityErrorType.INVALID_INPUT,
            'Request body too large',
            413
        );
    }
    
    try {
        return JSON.parse(text);
    } catch {
        throw new SecurityError(
            SecurityErrorType.INVALID_INPUT,
            'Invalid JSON',
            400
        );
    }
}

/**
 * Parse URL-encoded form data
 */
async function parseFormData(request: Request): Promise<Record<string, string>> {
    const text = await request.text();
    const params = new URLSearchParams(text);
    const data: Record<string, string> = {};
    
    for (const [key, value] of params) {
        data[key] = value;
    }
    
    return data;
}

/**
 * Parse multipart form data
 */
async function parseMultipartFormData(request: Request): Promise<Record<string, string | File>> {
    const formData = await request.formData();
    const data: Record<string, string | File> = {};
    
    formData.forEach((value, key) => {
        data[key] = value;
    });
    
    return data;
}

/**
 * Parse query parameters
 */
function parseQueryParams(request: Request): Record<string, string> {
    const url = new URL(request.url);
    const data: Record<string, string> = {};
    
    for (const [key, value] of url.searchParams) {
        data[key] = value;
    }
    
    return data;
}

/**
 * Format Zod validation errors for user-friendly response
 */
function formatValidationErrors(error: z.ZodError): string {
    const messages = error.issues.map(err => {
        const path = err.path.join('.');
        return path ? `${path}: ${err.message}` : err.message;
    });
    
    return messages.join(', ');
}

/**
 * Common validation schemas using centralized validation functions
 */
export const commonSchemas = {
    // Email validation using centralized function
    email: z.string().refine(
        (email) => validateEmail(email).valid,
        { error: (issue) => validateEmail(issue.input as string).error || 'Invalid email format' }
    ).transform((email) => email.toLowerCase()),
    
    // Password validation using centralized comprehensive validation
    password: z.string().refine(
        (password) => validatePassword(password).valid,
        { error: (issue) => validatePassword(issue.input as string).errors?.[0] || 'Password does not meet requirements' }
    ),
    
    // Password validation with user context (for preventing personal info in passwords)
    passwordWithUserInfo: (userInfo?: { email?: string; username?: string; name?: string }) => 
        z.string().refine(
            (password) => validatePassword(password, undefined, userInfo).valid,
            { error: (issue) => validatePassword(issue.input as string, undefined, userInfo).errors?.[0] || 'Password does not meet requirements' }
        ),
    
    // Username validation using centralized function
    username: z.string().refine(
        (username) => validateUsername(username).valid,
        { error: (issue) => validateUsername(issue.input as string).error || 'Invalid username format' }
    ),
    
    // UUID validation
    uuid: z.string().uuid(),
    
    // Pagination
    pagination: z.object({
        page: z.coerce.number().int().positive().default(1),
        limit: z.coerce.number().int().min(1).max(100).default(20),
        sortBy: z.string().optional(),
        sortOrder: z.enum(['asc', 'desc']).default('desc')
    }),
    
    // Safe string (no special chars that could be used for injection)
    safeString: z.string()
        .regex(/^[a-zA-Z0-9\s\-_]+$/, 'Only alphanumeric characters, spaces, hyphens, and underscores allowed')
        .transform(val => val.trim()),
    
    // URL validation
    url: z.string().url(),
    
    // Date validation
    date: z.string().datetime(),
};