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 data against a Zod schema
 * Throws an error if validation fails
 *
 * @param schema - The Zod schema to validate against
 * @param data - The data to validate
 * @returns The validated and typed data
 *
 * @example
 * try {
 *   const validUser = validateData(userSchema, userData)
 *   // Use validUser (type-safe)
 * } catch (error) {
 *   console.error('Validation failed:', error.message)
 * }
 */
export function validateData<T>(schema: z.ZodSchema<T>, data: unknown): T {
  return schema.parse(data);
}

/**
 * Safe validation that returns a result object instead of throwing
 *
 * @param schema - The Zod schema to validate against
 * @param data - The data to validate
 * @returns Success object with validated data or failure object with errors
 *
 * @example
 * const result = safeValidate(userSchema, userData)
 * if (!result.success) {
 *   Alert.alert('Validation Error', getFirstError(result.errors))
 *   return
 * }
 * const { email, password } = result.data
 */
export function safeValidate<T>(
  schema: z.ZodSchema<T>,
  data: unknown,
):
  | { 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;
  }
}

/**
 * Get first error message for display in UI
 *
 * @param errors - The formatted validation errors
 * @returns The first error message found
 *
 * @example
 * const firstError = getFirstError(errors)
 * Alert.alert('Error', firstError)
 */
export function getFirstError(errors: Record<string, string[]>): string {
  const firstField = Object.keys(errors)[0];
  return errors[firstField]?.[0] || "Validation error";
}

/**
 * Get all error messages as a flat array
 *
 * @param errors - The formatted validation errors
 * @returns Array of all error messages
 *
 * @example
 * const allErrors = getAllErrors(errors)
 * allErrors.forEach(err => console.error(err))
 */
export function getAllErrors(errors: Record<string, string[]>): string[] {
  return Object.values(errors).flat();
}

/**
 * Get errors for a specific field
 *
 * @param errors - The formatted validation errors
 * @param fieldPath - The field path (e.g., "email", "address.city")
 * @returns Array of error messages for the field, or empty array if none
 *
 * @example
 * const emailErrors = getFieldErrors(errors, 'email')
 * if (emailErrors.length > 0) {
 *   setEmailErrorText(emailErrors[0])
 * }
 */
export function getFieldErrors(
  errors: Record<string, string[]>,
  fieldPath: string,
): string[] {
  return errors[fieldPath] || [];
}

/**
 * Check if a specific field has validation errors
 *
 * @param errors - The formatted validation errors
 * @param fieldPath - The field path to check
 * @returns True if the field has errors
 *
 * @example
 * const hasEmailError = hasFieldError(errors, 'email')
 * <TextInput error={hasEmailError} />
 */
export function hasFieldError(
  errors: Record<string, string[]>,
  fieldPath: string,
): boolean {
  return fieldPath in errors && errors[fieldPath].length > 0;
}

/**
 * Format validation errors for display in a form
 * Returns an object suitable for form error state
 *
 * @param errors - The formatted validation errors
 * @returns Object with field paths as keys and first error message as value
 *
 * @example
 * const formErrors = formatFormErrors(errors)
 * setErrors(formErrors)
 * // { email: "Invalid email address", password: "Password too short" }
 */
export function formatFormErrors(
  errors: Record<string, string[]>,
): Record<string, string> {
  const formatted: Record<string, string> = {};

  for (const [field, messages] of Object.entries(errors)) {
    if (messages.length > 0) {
      formatted[field] = messages[0];
    }
  }

  return formatted;
}

/**
 * Validate form data before submission
 * Useful for client-side form validation
 *
 * @param schema - The Zod schema to validate against
 * @param formData - The form data object
 * @returns Validation result with formatted errors for forms
 *
 * @example
 * const handleSubmit = () => {
 *   const result = validateForm(goalSchema, {
 *     title: goalTitle,
 *     description: goalDescription,
 *     targetValue: parseFloat(targetValue),
 *   })
 *
 *   if (!result.success) {
 *     setFormErrors(result.formErrors)
 *     Alert.alert('Validation Error', result.message)
 *     return
 *   }
 *
 *   // Submit result.data
 * }
 */
export function validateForm<T>(
  schema: z.ZodSchema<T>,
  formData: unknown,
):
  | {
      success: true;
      data: T;
      formErrors?: never;
      message?: never;
    }
  | {
      success: false;
      data?: never;
      formErrors: Record<string, string>;
      message: string;
    } {
  const result = safeValidate(schema, formData);

  if (result.success) {
    return { success: true, data: result.data };
  }

  return {
    success: false,
    formErrors: formatFormErrors(result.errors),
    message: getFirstError(result.errors),
  };
}
