import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
import { ZodString, ZodLiteral, ZodEnum, ZodNativeEnum, any, ZodNumber, ZodBoolean, ZodDate, ZodBigInt, ZodArray, ZodObject, ZodEffects, ZodOptional, ZodDefault, ZodCatch, ZodIntersection, ZodUnion, ZodDiscriminatedUnion, ZodTuple, ZodNullable, ZodPipeline, ZodLazy, lazy, ZodAny } from 'zod';

/**
 * Helpers for coercing string value
 * Modify the value only if it's a string, otherwise return the value as-is
 */
function coerceString(value, transform) {
  if (typeof value !== 'string') {
    return value;
  }
  if (value === '') {
    return undefined;
  }
  if (typeof transform !== 'function') {
    return value;
  }
  return transform(value);
}

/**
 * Helpers for coercing file
 * Modify the value only if it's a file, otherwise return the value as-is
 */
function coerceFile(file) {
  if (typeof File !== 'undefined' && file instanceof File && file.name === '' && file.size === 0) {
    return undefined;
  }
  return file;
}

/**
 * A file schema is usually defined as `z.instanceof(File)`
 * which is implemented based on ZodAny with `superRefine`
 * Check the `instanceOfType` function on zod for more info
 */
function isFileSchema(schema) {
  if (typeof File === 'undefined') {
    return false;
  }
  return schema._def.effect.type === 'refinement' && schema.innerType() instanceof ZodAny && schema.safeParse(new File([], '')).success && !schema.safeParse('').success;
}

/**
 * Reconstruct the provided schema with additional preprocessing steps
 * This coerce empty values to undefined and transform strings to the correct type
 */
function enableTypeCoercion(type) {
  var cache = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Map();
  var result = cache.get(type);

  // Return the cached schema if it's already processed
  // This is to prevent infinite recursion caused by z.lazy()
  if (result) {
    return result;
  }
  var schema = type;
  if (type instanceof ZodString || type instanceof ZodLiteral || type instanceof ZodEnum || type instanceof ZodNativeEnum) {
    schema = any().transform(value => coerceString(value)).pipe(type);
  } else if (type instanceof ZodNumber) {
    schema = any().transform(value => coerceString(value, text => text.trim() === '' ? Number.NaN : Number(text))).pipe(type);
  } else if (type instanceof ZodBoolean) {
    schema = any().transform(value => coerceString(value, text => text === 'on' ? true : text)).pipe(type);
  } else if (type instanceof ZodDate) {
    schema = any().transform(value => coerceString(value, timestamp => {
      var date = new Date(timestamp);

      // z.date() does not expose a quick way to set invalid_date error
      // This gets around it by returning the original string if it's invalid
      // See https://github.com/colinhacks/zod/issues/1526
      if (isNaN(date.getTime())) {
        return timestamp;
      }
      return date;
    })).pipe(type);
  } else if (type instanceof ZodBigInt) {
    schema = any().transform(value => coerceString(value, BigInt)).pipe(type);
  } else if (type instanceof ZodArray) {
    schema = any().transform(value => {
      // No preprocess needed if the value is already an array
      if (Array.isArray(value)) {
        return value;
      }
      if (typeof value === 'undefined' || typeof coerceFile(coerceString(value)) === 'undefined') {
        return [];
      }

      // Wrap it in an array otherwise
      return [value];
    }).pipe(new ZodArray(_objectSpread2(_objectSpread2({}, type._def), {}, {
      type: enableTypeCoercion(type.element, cache)
    })));
  } else if (type instanceof ZodObject) {
    var _shape = Object.fromEntries(Object.entries(type.shape).map(_ref => {
      var [key, def] = _ref;
      return [key,
      // @ts-expect-error see message above
      enableTypeCoercion(def, cache)];
    }));
    schema = new ZodObject(_objectSpread2(_objectSpread2({}, type._def), {}, {
      shape: () => _shape
    }));
  } else if (type instanceof ZodEffects) {
    if (isFileSchema(type)) {
      schema = any().transform(value => coerceFile(value)).pipe(type);
    } else {
      schema = new ZodEffects(_objectSpread2(_objectSpread2({}, type._def), {}, {
        schema: enableTypeCoercion(type.innerType(), cache)
      }));
    }
  } else if (type instanceof ZodOptional) {
    schema = any().transform(value => coerceFile(coerceString(value))).pipe(new ZodOptional(_objectSpread2(_objectSpread2({}, type._def), {}, {
      innerType: enableTypeCoercion(type.unwrap(), cache)
    })));
  } else if (type instanceof ZodDefault) {
    schema = any().transform(value => coerceFile(coerceString(value))).pipe(new ZodDefault(_objectSpread2(_objectSpread2({}, type._def), {}, {
      innerType: enableTypeCoercion(type.removeDefault(), cache)
    })));
  } else if (type instanceof ZodCatch) {
    schema = new ZodCatch(_objectSpread2(_objectSpread2({}, type._def), {}, {
      innerType: enableTypeCoercion(type.removeCatch(), cache)
    }));
  } else if (type instanceof ZodIntersection) {
    schema = new ZodIntersection(_objectSpread2(_objectSpread2({}, type._def), {}, {
      left: enableTypeCoercion(type._def.left, cache),
      right: enableTypeCoercion(type._def.right, cache)
    }));
  } else if (type instanceof ZodUnion) {
    schema = new ZodUnion(_objectSpread2(_objectSpread2({}, type._def), {}, {
      options: type.options.map(option => enableTypeCoercion(option, cache))
    }));
  } else if (type instanceof ZodDiscriminatedUnion) {
    schema = new ZodDiscriminatedUnion(_objectSpread2(_objectSpread2({}, type._def), {}, {
      options: type.options.map(option => enableTypeCoercion(option, cache)),
      optionsMap: new Map(Array.from(type.optionsMap.entries()).map(_ref2 => {
        var [discriminator, option] = _ref2;
        return [discriminator, enableTypeCoercion(option, cache)];
      }))
    }));
  } else if (type instanceof ZodTuple) {
    schema = new ZodTuple(_objectSpread2(_objectSpread2({}, type._def), {}, {
      items: type.items.map(item => enableTypeCoercion(item, cache))
    }));
  } else if (type instanceof ZodNullable) {
    schema = new ZodNullable(_objectSpread2(_objectSpread2({}, type._def), {}, {
      innerType: enableTypeCoercion(type.unwrap(), cache)
    }));
  } else if (type instanceof ZodPipeline) {
    schema = new ZodPipeline(_objectSpread2(_objectSpread2({}, type._def), {}, {
      in: enableTypeCoercion(type._def.in, cache),
      out: enableTypeCoercion(type._def.out, cache)
    }));
  } else if (type instanceof ZodLazy) {
    schema = lazy(() => enableTypeCoercion(type.schema, cache));
  }
  if (type !== schema) {
    cache.set(type, schema);
  }
  return schema;
}

export { coerceFile, coerceString, enableTypeCoercion, isFileSchema };
