import { isEqual } from 'lodash-es'
import * as utils from '../../../utils'
import type { IZodType, IZodDefault, ZodDefaultDef, ParseInput, ParseReturnType } from '../../typings'
import { ZodBaseTypeImpl } from '../basetype'

export class ZodDefaultImpl<T extends IZodType = IZodType>
  extends ZodBaseTypeImpl<utils.types.NoUndefined<T['_output']>, ZodDefaultDef<T>, T['_input'] | undefined>
  implements IZodDefault<T>
{
  _parse(input: ParseInput): ParseReturnType<this['_output']> {
    const { ctx } = this._processInputParams(input)
    let data = ctx.data
    if (ctx.parsedType === 'undefined') {
      data = this._def.defaultValue()
    }
    return this._def.innerType._parse({
      data,
      path: ctx.path,
      parent: ctx,
    })
  }

  removeDefault() {
    return this._def.innerType
  }

  dereference(defs: Record<string, IZodType>): IZodType {
    return new ZodDefaultImpl({
      ...this._def,
      innerType: this._def.innerType.dereference(defs),
    })
  }

  getReferences(): string[] {
    return this._def.innerType.getReferences()
  }

  clone(): IZodDefault<T> {
    return new ZodDefaultImpl({
      ...this._def,
      innerType: this._def.innerType.clone() as T,
    })
  }

  isEqual(schema: IZodType): boolean {
    if (!(schema instanceof ZodDefaultImpl)) return false
    return (
      this._def.innerType.isEqual(schema._def.innerType) &&
      isEqual(this._def.defaultValue(), schema._def.defaultValue())
    )
  }

  unwrap() {
    return this._def.innerType
  }

  naked() {
    return this._def.innerType.naked()
  }

  mandatory(): IZodDefault<IZodType> {
    return new ZodDefaultImpl({
      ...this._def,
      innerType: this._def.innerType.mandatory(),
    })
  }
}
