import { NextRequest, NextResponse } from "next/server";
import { z, ZodError } from "zod";

/**
 * Format Zod validation errors into user-friendly messages
 *
 * @param error - The ZodError to format
 * @returns Object with field paths as keys and error messages as values
 *
 * @example
 * const errors = formatZodErrors(zodError)
 * // { "email": ["Invalid email address"], "password": ["Password too short"] }
 */
export function formatZodErrors(error: ZodError): Record<string, string[]> {
  const formatted: Record<string, string[]> = {};

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

  return formatted;
}

/**
 * Validate request body against a Zod schema
 *
 * @param request - The Next.js request object
 * @param schema - The Zod schema to validate against
 * @returns Success object with validated data or failure object with errors
 *
 * @example
 * const result = await validateRequestBody(request, userSchema)
 * if (!result.success) {
 *   return validationErrorResponse(result.errors)
 * }
 * const { email, password } = result.data
 */
export async function validateRequestBody<T>(
  request: NextRequest,
  schema: z.ZodSchema<T>,
): Promise<
  | { success: true; data: T }
  | { success: false; errors: Record<string, string[]> }
> {
  try {
    const body = await request.json();
    const data = schema.parse(body);
    return { success: true, data };
  } catch (error) {
    if (error instanceof ZodError) {
      return { success: false, errors: formatZodErrors(error) };
    }
    throw error;
  }
}

/**
 * Validate URL query parameters against a Zod schema
 *
 * @param request - The Next.js request object
 * @param schema - The Zod schema to validate against
 * @returns Success object with validated data or failure object with errors
 *
 * @example
 * const result = validateQueryParams(request, paginationSchema)
 * if (!result.success) {
 *   return validationErrorResponse(result.errors)
 * }
 * const { page, limit } = result.data
 */
export function validateQueryParams<T>(
  request: NextRequest,
  schema: z.ZodSchema<T>,
):
  | { success: true; data: T }
  | { success: false; errors: Record<string, string[]> } {
  try {
    const searchParams = request.nextUrl.searchParams;
    const params = Object.fromEntries(searchParams.entries());
    const data = schema.parse(params);
    return { success: true, data };
  } catch (error) {
    if (error instanceof ZodError) {
      return { success: false, errors: formatZodErrors(error) };
    }
    throw error;
  }
}

/**
 * Validate data against a Zod schema (generic validation)
 *
 * @param data - The data to validate
 * @param schema - The Zod schema to validate against
 * @returns Success object with validated data or failure object with errors
 *
 * @example
 * const result = validateData(userData, userSchema)
 * if (!result.success) {
 *   console.error('Validation failed:', result.errors)
 * }
 */
export function validateData<T>(
  data: unknown,
  schema: z.ZodSchema<T>,
):
  | { success: true; data: T }
  | { success: false; errors: Record<string, string[]> } {
  try {
    const validData = schema.parse(data);
    return { success: true, data: validData };
  } catch (error) {
    if (error instanceof ZodError) {
      return { success: false, errors: formatZodErrors(error) };
    }
    throw error;
  }
}

/**
 * Create a standardized validation error response
 *
 * @param errors - The formatted validation errors
 * @param message - Optional custom error message
 * @returns Next.js JSON response with 400 status
 *
 * @example
 * return validationErrorResponse(errors)
 * // Response: { error: "Validation failed", details: { ... } }
 */
export function validationErrorResponse(
  errors: Record<string, string[]>,
  message = "Validation failed",
) {
  return NextResponse.json(
    {
      error: message,
      details: errors,
    },
    { status: 400 },
  );
}

/**
 * Get the first error message from validation errors (for simple displays)
 *
 * @param errors - The formatted validation errors
 * @returns The first error message found
 *
 * @example
 * const firstError = getFirstError(errors)
 * // "Invalid email address"
 */
export function getFirstError(errors: Record<string, string[]>): string {
  const firstField = Object.keys(errors)[0];
  return errors[firstField]?.[0] || "Validation error";
}

/**
 * Combine multiple validation results
 * Returns success only if all validations pass
 *
 * @param results - Array of validation results
 * @returns Combined validation result
 *
 * @example
 * const bodyValidation = await validateRequestBody(request, schema1)
 * const queryValidation = validateQueryParams(request, schema2)
 * const combined = combineValidations([bodyValidation, queryValidation])
 * if (!combined.success) {
 *   return validationErrorResponse(combined.errors)
 * }
 */
export function combineValidations<T extends Record<string, unknown>>(
  results: Array<
    | { success: true; data: T }
    | { success: false; errors: Record<string, string[]> }
  >,
):
  | { success: true; data: T }
  | { success: false; errors: Record<string, string[]> } {
  const allErrors: Record<string, string[]> = {};
  let combinedData: T = {} as T;

  for (const result of results) {
    if (!result.success) {
      Object.assign(allErrors, result.errors);
    } else {
      combinedData = { ...combinedData, ...result.data };
    }
  }

  if (Object.keys(allErrors).length > 0) {
    return { success: false, errors: allErrors };
  }

  return { success: true, data: combinedData };
}
