/**
 * Request Validation Middleware
 *
 * Zod-based schema validation for request bodies, queries, and params
 */

import { Request, Response, NextFunction } from 'express';
import { ZodSchema, ZodError } from 'zod';
import { ValidationError, ValidationErrorResponse } from '../types';
import { logger } from '../utils/logger';

/**
 * Format Zod error into ValidationError array
 */
function formatZodError(error: ZodError): ValidationError[] {
  return error.errors.map((err) => ({
    field: err.path.join('.'),
    message: err.message,
    rule: err.code,
    expected: 'expected' in err ? String((err as any).expected) : undefined,
    received: 'received' in err ? String((err as any).received) : undefined,
  }));
}

/**
 * Validate request body against Zod schema
 */
export function validate(schema: ZodSchema) {
  return (req: Request, res: Response, next: NextFunction): void => {
    try {
      // Validate and parse request body
      const validatedData = schema.parse(req.body);

      // Replace request body with validated data (this ensures type safety)
      req.body = validatedData;

      next();
    } catch (error) {
      if (error instanceof ZodError) {
        const validationErrors = formatZodError(error);

        logger.warn('Validation error', {
          path: req.path,
          method: req.method,
          errors: validationErrors,
          requestId: req.requestId,
        });

        const errorResponse: ValidationErrorResponse = {
          error: true,
          code: 'VALIDATION_ERROR',
          message: 'Request validation failed',
          errors: validationErrors,
          timestamp: new Date().toISOString(),
        };

        if (req.requestId) {
          errorResponse.requestId = req.requestId;
        }

        res.status(400).json(errorResponse);
      } else {
        // Unexpected error during validation
        logger.error('Unexpected error during validation', {
          error,
          path: req.path,
          method: req.method,
          requestId: req.requestId,
        });

        next(error);
      }
    }
  };
}

/**
 * Validate request query parameters against Zod schema
 */
export function validateQuery(schema: ZodSchema) {
  return (req: Request, res: Response, next: NextFunction): void => {
    try {
      // Validate and parse query parameters
      const validatedData = schema.parse(req.query);

      // Replace query with validated data
      req.query = validatedData;

      next();
    } catch (error) {
      if (error instanceof ZodError) {
        const validationErrors = formatZodError(error);

        logger.warn('Query validation error', {
          path: req.path,
          method: req.method,
          errors: validationErrors,
          requestId: req.requestId,
        });

        const errorResponse: ValidationErrorResponse = {
          error: true,
          code: 'VALIDATION_ERROR',
          message: 'Query parameter validation failed',
          errors: validationErrors,
          timestamp: new Date().toISOString(),
        };

        if (req.requestId) {
          errorResponse.requestId = req.requestId;
        }

        res.status(400).json(errorResponse);
      } else {
        next(error);
      }
    }
  };
}

/**
 * Validate request params against Zod schema
 */
export function validateParams(schema: ZodSchema) {
  return (req: Request, res: Response, next: NextFunction): void => {
    try {
      // Validate and parse params
      const validatedData = schema.parse(req.params);

      // Replace params with validated data
      req.params = validatedData;

      next();
    } catch (error) {
      if (error instanceof ZodError) {
        const validationErrors = formatZodError(error);

        logger.warn('Params validation error', {
          path: req.path,
          method: req.method,
          errors: validationErrors,
          requestId: req.requestId,
        });

        const errorResponse: ValidationErrorResponse = {
          error: true,
          code: 'VALIDATION_ERROR',
          message: 'URL parameter validation failed',
          errors: validationErrors,
          timestamp: new Date().toISOString(),
        };

        if (req.requestId) {
          errorResponse.requestId = req.requestId;
        }

        res.status(400).json(errorResponse);
      } else {
        next(error);
      }
    }
  };
}

/**
 * Custom validation middleware factory for complex validations
 *
 * Allows custom validation logic that returns ValidationError array
 */
export function customValidate(
  validationFn: (_req: Request) => ValidationError[] | null
) {
  return (req: Request, res: Response, next: NextFunction): void => {
    try {
      const errors = validationFn(req);

      if (errors && errors.length > 0) {
        logger.warn('Custom validation error', {
          path: req.path,
          method: req.method,
          errors,
          requestId: req.requestId,
        });

        const errorResponse: ValidationErrorResponse = {
          error: true,
          code: 'VALIDATION_ERROR',
          message: 'Request validation failed',
          errors,
          timestamp: new Date().toISOString(),
        };

        if (req.requestId) {
          errorResponse.requestId = req.requestId;
        }

        res.status(400).json(errorResponse);
      } else {
        next();
      }
    } catch (error) {
      next(error);
    }
  };
}
