import type { ZodNativeTypeDef, ZodType } from '../../z'
import { Options, Targets } from './Options'
import { JsonSchema7Type, parseDef } from './parseDef'
import { getRefs } from './Refs'

const zodToJsonSchema = <Target extends Targets = 'jsonSchema7'>(
  schema: ZodType<any>,
  options?: Partial<Options<Target>> | string
): (Target extends 'jsonSchema7' ? JsonSchema7Type : object) & {
  $schema?: string
  definitions?: {
    [key: string]: Target extends 'jsonSchema7'
      ? JsonSchema7Type
      : Target extends 'jsonSchema2019-09'
        ? JsonSchema7Type
        : object
  }
} => {
  const refs = getRefs(options)

  const definitions =
    typeof options === 'object' && options.definitions
      ? Object.entries(options.definitions).reduce(
          (acc, [name, schema]) => ({
            ...acc,
            [name]:
              parseDef(
                schema._def as ZodNativeTypeDef,
                {
                  ...refs,
                  currentPath: [...refs.basePath, refs.definitionPath, name],
                },
                true
              ) ?? {},
          }),
          {}
        )
      : undefined

  const name = typeof options === 'string' ? options : options?.name

  const main =
    parseDef(
      schema._def as ZodNativeTypeDef,
      name === undefined
        ? refs
        : {
            ...refs,
            currentPath: [...refs.basePath, refs.definitionPath, name],
          },
      false
    ) ?? {}

  const combined: ReturnType<typeof zodToJsonSchema<Target>> =
    name === undefined
      ? definitions
        ? {
            ...main,
            [refs.definitionPath]: definitions,
          }
        : main
      : {
          $ref: [...(refs.$refStrategy === 'relative' ? [] : refs.basePath), refs.definitionPath, name].join('/'),
          [refs.definitionPath]: {
            ...definitions,
            [name]: main,
          },
        }

  if (refs.target === 'jsonSchema7') {
    combined.$schema = 'http://json-schema.org/draft-07/schema#'
  } else if (refs.target === 'jsonSchema2019-09') {
    combined.$schema = 'https://json-schema.org/draft/2019-09/schema#'
  }

  return combined
}

export { zodToJsonSchema }
