/**
 * Schema validation middleware for Fastify
 * Uses Zod for runtime validation of request bodies and query parameters
 */

import { FastifyRequest, FastifyReply } from 'fastify';
import { ZodSchema, ZodError } from 'zod';
import { createLogger } from '@4runr/shared';
import { getCorrelationId } from './correlationId.js';

const logger = createLogger('Gateway:Validation');

/**
 * Format Zod errors into a user-friendly structure
 */
function formatZodError(error: ZodError) {
  const fieldErrors: Record<string, string[]> = {};
  const formErrors: string[] = [];

  for (const issue of error.issues) {
    if (issue.path.length === 0) {
      formErrors.push(issue.message);
    } else {
      const path = issue.path.join('.');
      if (!fieldErrors[path]) {
        fieldErrors[path] = [];
      }
      fieldErrors[path].push(issue.message);
    }
  }

  return {
    fieldErrors,
    formErrors,
  };
}

/**
 * Validate request body against a Zod schema
 */
export function validateBody<T>(schema: ZodSchema<T>) {
  return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
    try {
      const result = schema.safeParse(request.body);
      
      if (!result.success) {
        const correlationId = getCorrelationId(request);
        reply.status(400).send({
          error: 'invalid_request',
          message: 'Request body validation failed',
          details: formatZodError(result.error),
          timestamp: new Date().toISOString(),
          path: request.url,
          statusCode: 400,
          ...(correlationId && { correlationId }),
        });
        return;
      }

      // Replace body with validated & transformed data
      request.body = result.data;
    } catch (error) {
      logger.error('Body validation error', { error: error instanceof Error ? error.message : String(error) });
      const correlationId = getCorrelationId(request);
      reply.status(500).send({
        error: 'server_error',
        message: 'Failed to validate request body',
        timestamp: new Date().toISOString(),
        path: request.url,
        statusCode: 500,
        ...(correlationId && { correlationId }),
      });
    }
  };
}

/**
 * Validate query parameters against a Zod schema
 */
export function validateQuery<T>(schema: ZodSchema<T>) {
  return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
    try {
      const result = schema.safeParse(request.query);
      
      if (!result.success) {
        const correlationId = getCorrelationId(request);
        reply.status(400).send({
          error: 'invalid_query',
          message: 'Query parameter validation failed',
          details: formatZodError(result.error),
          timestamp: new Date().toISOString(),
          path: request.url,
          statusCode: 400,
          ...(correlationId && { correlationId }),
        });
        return;
      }

      // Replace query with validated & transformed data
      request.query = result.data;
    } catch (error) {
      logger.error('Query validation error', { error: error instanceof Error ? error.message : String(error) });
      const correlationId = getCorrelationId(request);
      reply.status(500).send({
        error: 'server_error',
        message: 'Failed to validate query parameters',
        timestamp: new Date().toISOString(),
        path: request.url,
        statusCode: 500,
        ...(correlationId && { correlationId }),
      });
    }
  };
}

/**
 * Validate route parameters against a Zod schema
 */
export function validateParams<T>(schema: ZodSchema<T>) {
  return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
    try {
      const result = schema.safeParse(request.params);
      
      if (!result.success) {
        const correlationId = getCorrelationId(request);
        reply.status(400).send({
          error: 'invalid_params',
          message: 'Route parameter validation failed',
          details: formatZodError(result.error),
          timestamp: new Date().toISOString(),
          path: request.url,
          statusCode: 400,
          ...(correlationId && { correlationId }),
        });
        return;
      }

      // Replace params with validated & transformed data
      request.params = result.data;
    } catch (error) {
      logger.error('Params validation error', { error: error instanceof Error ? error.message : String(error) });
      const correlationId = getCorrelationId(request);
      reply.status(500).send({
        error: 'server_error',
        message: 'Failed to validate route parameters',
        timestamp: new Date().toISOString(),
        path: request.url,
        statusCode: 500,
        ...(correlationId && { correlationId }),
      });
    }
  };
}

/**
 * Validate headers against a Zod schema
 */
export function validateHeaders<T>(schema: ZodSchema<T>) {
  return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
    try {
      const result = schema.safeParse(request.headers);
      
      if (!result.success) {
        const correlationId = getCorrelationId(request);
        reply.status(400).send({
          error: 'invalid_headers',
          message: 'Header validation failed',
          details: formatZodError(result.error),
          timestamp: new Date().toISOString(),
          path: request.url,
          statusCode: 400,
          ...(correlationId && { correlationId }),
        });
        return;
      }

      // Note: We don't replace headers as they're read-only
      // Store validated headers in request context if needed
      (request as any).validatedHeaders = result.data;
    } catch (error) {
      logger.error('Headers validation error', { error: error instanceof Error ? error.message : String(error) });
      const correlationId = getCorrelationId(request);
      reply.status(500).send({
        error: 'server_error',
        message: 'Failed to validate headers',
        timestamp: new Date().toISOString(),
        path: request.url,
        statusCode: 500,
        ...(correlationId && { correlationId }),
      });
    }
  };
}

