import { Request, Response, NextFunction } from 'express';
import { ZodSchema, ZodError } from 'zod';

type RequestPart = 'body' | 'query' | 'params';

/**
 * Format Zod errors into a user-friendly object
 */
function formatZodError(error: ZodError): Record<string, string> {
  const formatted: Record<string, string> = {};

  for (const issue of error.issues) {
    const path = issue.path.join('.');
    if (!formatted[path]) {
      formatted[path] = issue.message;
    }
  }

  return formatted;
}

/**
 * Create a validation middleware for a specific request part
 */
export function validate<T>(schema: ZodSchema<T>, part: RequestPart = 'body') {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const data = req[part];
      const validated = await schema.parseAsync(data);

      // Replace the request part with validated/transformed data
      (req as any)[part] = validated;

      next();
    } catch (error) {
      if (error instanceof ZodError) {
        res.status(400).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Validation failed',
            details: formatZodError(error),
          },
        });
        return;
      }

      next(error);
    }
  };
}

/**
 * Validate request body
 */
export function validateBody<T>(schema: ZodSchema<T>) {
  return validate(schema, 'body');
}

/**
 * Validate query parameters
 */
export function validateQuery<T>(schema: ZodSchema<T>) {
  return validate(schema, 'query');
}

/**
 * Validate URL parameters
 */
export function validateParams<T>(schema: ZodSchema<T>) {
  return validate(schema, 'params');
}

/**
 * Validate multiple request parts at once
 */
export function validateRequest<
  TBody = any,
  TQuery = any,
  TParams = any
>(schemas: {
  body?: ZodSchema<TBody>;
  query?: ZodSchema<TQuery>;
  params?: ZodSchema<TParams>;
}) {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    const errors: Record<string, Record<string, string>> = {};

    try {
      if (schemas.params) {
        req.params = await schemas.params.parseAsync(req.params);
      }
    } catch (error) {
      if (error instanceof ZodError) {
        errors.params = formatZodError(error);
      }
    }

    try {
      if (schemas.query) {
        (req as any).query = await schemas.query.parseAsync(req.query);
      }
    } catch (error) {
      if (error instanceof ZodError) {
        errors.query = formatZodError(error);
      }
    }

    try {
      if (schemas.body) {
        req.body = await schemas.body.parseAsync(req.body);
      }
    } catch (error) {
      if (error instanceof ZodError) {
        errors.body = formatZodError(error);
      }
    }

    if (Object.keys(errors).length > 0) {
      res.status(400).json({
        success: false,
        error: {
          code: 'VALIDATION_ERROR',
          message: 'Validation failed',
          details: errors,
        },
      });
      return;
    }

    next();
  };
}
