import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';
import type { FieldError, FieldErrors } from 'react-hook-form';
import type { Resolver } from './types';

// nope-validator doesn't publicly export this shape (and its internal file
// layout has changed across major versions), so it's redeclared here to
// avoid depending on an unstable, non-exported module path.
type ShapeErrors = {
  [key: string]: string | ShapeErrors;
};

const parseErrors = (
  errors: ShapeErrors,
  parsedErrors: FieldErrors = {},
  path = '',
) => {
  return Object.keys(errors).reduce((acc, key) => {
    const _path = path ? `${path}.${key}` : key;
    const error = errors[key];

    if (typeof error === 'string') {
      acc[_path] = {
        message: error,
      } as FieldError;
    } else {
      parseErrors(error, acc, _path);
    }

    return acc;
  }, parsedErrors);
};

/**
 * Creates a resolver for react-hook-form using Nope schema validation
 * @param {NopeSchema} schema - The Nope schema to validate against
 * @param {NopeSchemaOptions} [schemaOptions] - Optional Nope validation options
 * @param {Object} resolverOptions - Additional resolver configuration
 * @param {string} [resolverOptions.mode='async'] - Validation mode
 * @returns {Resolver<NopeSchema>} A resolver function compatible with react-hook-form
 * @example
 * const schema = nope.object({
 *   name: nope.string().required(),
 *   age: nope.number().required()
 * });
 *
 * useForm({
 *   resolver: nopeResolver(schema)
 * });
 */
export const nopeResolver: Resolver =
  (
    schema,
    schemaOptions = {
      abortEarly: false,
    },
  ) =>
  (values, context, options) => {
    const result = schema.validate(values, context, schemaOptions) as
      | ShapeErrors
      | undefined;

    if (result) {
      return { values: {}, errors: toNestErrors(parseErrors(result), options) };
    }

    options.shouldUseNativeValidation && validateFieldsNatively({}, options);

    return { values, errors: {} };
  };
