import { FormikErrors, FormikValues } from 'formik';

export type ErrorMessage = {
  key: string;
  message: string;
};

/**
 * Parse Formik errors object into a flat array of error messages
 * @param errors - Formik errors object
 * @param parentKey - Parent key for nested objects (used internally for recursion)
 * @returns Array of error messages
 */
export function parseFormikErrors<T>(
  errors: FormikErrors<T>,
  parentKey: string = ''
): ErrorMessage[] {
  const errorList: ErrorMessage[] = [];

  Object.keys(errors).forEach((key) => {
    const value = errors[key as keyof typeof errors];
    const fullKey = parentKey ? `${parentKey}.${key}` : key;

    if (typeof value === 'string') {
      // Direct error message
      errorList.push({ key: fullKey, message: value });
    } else if (Array.isArray(value)) {
      // Array of errors
      value.forEach((item, index) => {
        if (typeof item === 'string') {
          errorList.push({ key: `${fullKey}[${index}]`, message: item });
        } else if (item && typeof item === 'object') {
          // Nested object in array
          const nestedErrors = parseFormikErrors(
            item as FormikErrors<unknown>,
            `${fullKey}[${index}]`
          );
          errorList.push(...nestedErrors);
        }
      });
    } else if (value && typeof value === 'object') {
      // Nested object
      const nestedErrors = parseFormikErrors(
        value as FormikErrors<unknown>,
        fullKey
      );
      errorList.push(...nestedErrors);
    }
  });

  return errorList;
}

/**
 * Get unique error messages from Formik errors
 * @param errors - Formik errors object
 * @returns Array of unique error messages
 */
export function getUniqueFormikErrors<T>(errors: FormikErrors<T>): string[] {
  const errorList = parseFormikErrors(errors);
  return Array.from(new Set(errorList.map((e) => e.message)));
}

/**
 * Get all error messages from Formik errors
 * @param errors - Formik errors object
 * @returns Array of error messages
 */
export function getAllFormikErrors<T>(errors: FormikErrors<T>): ErrorMessage[] {
  return parseFormikErrors(errors);
}

/**
 * Check if a value is considered "filled" (not empty)
 * @param value - Value to check
 * @returns True if value is filled, false otherwise
 */
function isValueFilled(value: unknown): boolean {
  // Check for null or undefined
  if (value === null || value === undefined) {
    return false;
  }

  // Check for empty string
  if (typeof value === 'string' && value.trim() === '') {
    return false;
  }

  // Check for empty array
  if (Array.isArray(value) && value.length === 0) {
    return false;
  }

  // Check for empty object (but not Date or other special objects)
  if (
    typeof value === 'object' &&
    !Array.isArray(value) &&
    !(value instanceof Date) &&
    Object.keys(value).length === 0
  ) {
    return false;
  }

  return true;
}

/**
 * Count the number of filled (non-empty) values in Formik values object
 * @param values - Formik values object
 * @returns Number of filled values
 * @example
 * const values = {
 *   name: 'John',
 *   email: '',
 *   age: null,
 *   tags: ['tag1', 'tag2'],
 *   emptyArray: [],
 * };
 * getFilledFormikValuesCount(values); // Returns 2 (name and tags)
 */
export function getFilledFormikValuesCount<T extends FormikValues>(
  values: T
): number {
  let count = 0;

  Object.keys(values).forEach((key) => {
    const value = values[key];
    if (isValueFilled(value)) {
      count++;
    }
  });

  return count;
}
