package json

import (
  "fmt"
  "strings"

  shimast "github.com/microsoft/typescript-go/shim/ast"
  shimchecker "github.com/microsoft/typescript-go/shim/checker"
  shimprinter "github.com/microsoft/typescript-go/shim/printer"
  nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
  nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
  nativeprogrammers "github.com/samchon/typia/packages/typia/native/core/programmers"
  nativehelpers "github.com/samchon/typia/packages/typia/native/core/programmers/helpers"
  nativeinternal "github.com/samchon/typia/packages/typia/native/core/programmers/internal"
  nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
  schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
)

type jsonStringifyProgrammerNamespace struct{}

var JsonStringifyProgrammer = jsonStringifyProgrammerNamespace{}

type JsonStringifyProgrammer_DecomposeProps struct {
  Validated bool
  Context   nativecontext.ITypiaContext
  Functor   *nativehelpers.FunctionProgrammer
  Type      *shimchecker.Type
  Name      *string
}

const jsonStringifyProgrammer_PREFIX = "_s"
const jsonStringifyProgrammer_CHECKER_PREFIX = "_i"
const jsonStringifyProgrammer_CHECKER_PREFIX_VALIDATED = "_si"

var jsonStringifyProgrammer_factory = shimast.NewNodeFactory(shimast.NodeFactoryHooks{})

func (jsonStringifyProgrammerNamespace) Decompose(props JsonStringifyProgrammer_DecomposeProps) nativeinternal.FeatureProgrammer_IDecomposed {
  checkerPrefix := jsonStringifyProgrammer_checker_prefix(props.Validated)
  config := jsonStringifyProgrammer_configure(struct {
    Context   nativecontext.ITypiaContext
    Functor   *nativehelpers.FunctionProgrammer
    Validated bool
  }{
    Context:   props.Context,
    Functor:   props.Functor,
    Validated: props.Validated,
  })
  config.Addition = func(collection *schemametadata.MetadataCollection) []*shimast.Node {
    return nativeprogrammers.IsProgrammer.Write_function_statements(nativeprogrammers.IsProgrammer_WriteFunctionStatementsProps{
      Context:    props.Context,
      Functor:    props.Functor,
      Collection: collection,
      Prefix:     checkerPrefix,
    })
  }
  composed := nativeinternal.FeatureProgrammer.Compose(nativeinternal.FeatureProgrammer_ComposeProps{
    Context: props.Context,
    Config:  config,
    Functor: props.Functor,
    Type:    props.Type,
    Name:    props.Name,
  })
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  return nativeinternal.FeatureProgrammer_IDecomposed{
    Functions:  composed.Functions,
    Statements: composed.Statements,
    Arrow: f.NewArrowFunction(
      nil,
      nil,
      f.NewNodeList(composed.Parameters),
      composed.Response,
      nil,
      f.NewToken(shimast.KindEqualsGreaterThanToken),
      composed.Body,
    ),
  }
}

func (jsonStringifyProgrammerNamespace) Write(props nativecontext.IProgrammerProps) *shimast.Node {
  functor := nativehelpers.NewFunctionProgrammer(jsonStringifyProgrammer_method_text(props.Modulo), props.Context.Emit)
  result := JsonStringifyProgrammer.Decompose(JsonStringifyProgrammer_DecomposeProps{
    Context:   props.Context,
    Functor:   functor,
    Type:      props.Type,
    Name:      props.Name,
    Validated: false,
  })
  return nativeinternal.FeatureProgrammer.WriteDecomposed(nativeinternal.FeatureProgrammer_WriteDecomposedProps{
    Modulo:  props.Modulo,
    Functor: functor,
    Result:  result,
  })
}

func jsonStringifyProgrammer_write_array_functions(props struct {
  Context    nativecontext.ITypiaContext
  Config     nativeinternal.FeatureProgrammer_IConfig
  Functor    *nativehelpers.FunctionProgrammer
  Collection *schemametadata.MetadataCollection
}) []*shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  output := []*shimast.Node{}
  for i, typ := range props.Collection.Arrays() {
    if typ.Recursive == false {
      continue
    }
    output = append(output, nativefactories.StatementFactory.Constant(nativefactories.StatementFactory_ConstantProps{
      Name: fmt.Sprintf("%sa%d", props.Config.Prefix, i),
      Value: f.NewArrowFunction(
        nil,
        nil,
        f.NewNodeList(nativeinternal.FeatureProgrammer.ParameterDeclarations(nativeinternal.FeatureProgrammer_ParameterDeclarationsProps{
          Config: nativeinternal.FeatureProgrammer_ParameterConfig{Path: props.Config.Path, Trace: props.Config.Trace, Visited: props.Functor.Visited()},
          Type:   nativefactories.TypeFactory.Keyword("any", props.Context.Emit),
          Input:  f.NewIdentifier("input"),
        })),
        nativefactories.TypeFactory.Keyword("any", props.Context.Emit),
        nil,
        f.NewToken(shimast.KindEqualsGreaterThanToken),
        nativeinternal.FeatureProgrammer.VisitGuardSerialize(
          nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "a", i),
          jsonStringifyProgrammer_circular_thrower(props.Context, props.Functor),
          jsonStringifyProgrammer_decode_array_inline(jsonStringifyProgrammer_decodeArrayProps{
            Context: props.Context,
            Config:  props.Config,
            Functor: props.Functor,
            Input:   f.NewIdentifier("input"),
            Array: schemametadata.MetadataArray_create(schemametadata.MetadataArray{
              Type: typ,
              Tags: [][]schemametadata.IMetadataTypeTag{},
            }),
            Explore: nativeinternal.FeatureProgrammer_IExplore{
              Tracable: props.Config.Trace,
              Source:   "function",
              From:     "array",
              Postfix:  "",
            },
          }),
          props.Context.Emit,
        ),
      ),
    }, props.Context.Emit))
  }
  return output
}

func jsonStringifyProgrammer_write_tuple_functions(props struct {
  Context    nativecontext.ITypiaContext
  Config     nativeinternal.FeatureProgrammer_IConfig
  Functor    *nativehelpers.FunctionProgrammer
  Collection *schemametadata.MetadataCollection
  Validated  bool
}) []*shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  output := []*shimast.Node{}
  for i, tuple := range props.Collection.Tuples() {
    if tuple.Recursive == false {
      continue
    }
    output = append(output, nativefactories.StatementFactory.Constant(nativefactories.StatementFactory_ConstantProps{
      Name: fmt.Sprintf("%st%d", props.Config.Prefix, i),
      Value: f.NewArrowFunction(
        nil,
        nil,
        f.NewNodeList(nativeinternal.FeatureProgrammer.ParameterDeclarations(nativeinternal.FeatureProgrammer_ParameterDeclarationsProps{
          Config: nativeinternal.FeatureProgrammer_ParameterConfig{Path: props.Config.Path, Trace: props.Config.Trace, Visited: props.Functor.Visited()},
          Type:   nativefactories.TypeFactory.Keyword("any", props.Context.Emit),
          Input:  f.NewIdentifier("input"),
        })),
        nativefactories.TypeFactory.Keyword("any", props.Context.Emit),
        nil,
        f.NewToken(shimast.KindEqualsGreaterThanToken),
        nativeinternal.FeatureProgrammer.VisitGuardSerialize(
          nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "t", i),
          jsonStringifyProgrammer_circular_thrower(props.Context, props.Functor),
          jsonStringifyProgrammer_decode_tuple_inline(jsonStringifyProgrammer_decodeTupleInlineProps{
            Context:   props.Context,
            Config:    props.Config,
            Functor:   props.Functor,
            Input:     f.NewIdentifier("input"),
            Tuple:     tuple,
            Explore:   nativeinternal.FeatureProgrammer_IExplore{Tracable: props.Config.Trace, Source: "function", From: "array", Postfix: ""},
            Validated: props.Validated,
          }),
          props.Context.Emit,
        ),
      ),
    }, props.Context.Emit))
  }
  return output
}

func jsonStringifyProgrammer_decode(props struct {
  Context   nativecontext.ITypiaContext
  Config    nativeinternal.FeatureProgrammer_IConfig
  Functor   *nativehelpers.FunctionProgrammer
  Input     *shimast.Node
  Metadata  *schemametadata.MetadataSchema
  Explore   nativeinternal.FeatureProgrammer_IExplore
  Validated bool
}) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  if props.Metadata.Any {
    return jsonStringifyProgrammer_wrap_required(struct {
      Context    nativecontext.ITypiaContext
      Input      *shimast.Node
      Metadata   *schemametadata.MetadataSchema
      Explore    nativeinternal.FeatureProgrammer_IExplore
      Expression *shimast.Node
    }{
      Context:  props.Context,
      Input:    props.Input,
      Metadata: props.Metadata,
      Explore:  props.Explore,
      Expression: jsonStringifyProgrammer_wrap_functional(struct {
        Context    nativecontext.ITypiaContext
        Input      *shimast.Node
        Metadata   *schemametadata.MetadataSchema
        Explore    nativeinternal.FeatureProgrammer_IExplore
        Expression *shimast.Node
      }{
        Context:  props.Context,
        Input:    props.Input,
        Metadata: props.Metadata,
        Explore:  props.Explore,
        Expression: f.NewCallExpression(
          f.NewIdentifier("JSON.stringify"),
          nil,
          nil,
          f.NewNodeList([]*shimast.Node{props.Input}),
          shimast.NodeFlagsNone,
        ),
      }),
    })
  }

  size := props.Metadata.Size()
  if size == 0 && (props.Metadata.IsRequired() == false || props.Metadata.Nullable) {
    if props.Metadata.IsRequired() == false && props.Metadata.Nullable {
      if props.Explore.From == "array" {
        return f.NewStringLiteral("null", shimast.TokenFlagsNone)
      }
      return nativefactories.ExpressionFactory.Conditional(
        jsonStringifyProgrammer_binary(f.NewKeywordExpression(shimast.KindNullKeyword), shimast.KindEqualsEqualsEqualsToken, props.Input, props.Context.Emit),
        f.NewStringLiteral("null", shimast.TokenFlagsNone),
        f.NewIdentifier("undefined"),
        props.Context.Emit,
      )
    } else if props.Metadata.IsRequired() == false {
      if props.Explore.From == "array" {
        return f.NewStringLiteral("null", shimast.TokenFlagsNone)
      }
      return f.NewIdentifier("undefined")
    }
    return f.NewStringLiteral("null", shimast.TokenFlagsNone)
  }

  unions := []jsonStringifyProgrammer_IUnion{}
  if props.Metadata.Escaped != nil {
    metadata := props.Metadata.Escaped.Returns
    isDate := props.Metadata.Escaped.Original.Size() == 1 &&
      len(props.Metadata.Escaped.Original.Natives) != 0 &&
      props.Metadata.Escaped.Original.Natives[0].Name == "Date"
    unions = append(unions, jsonStringifyProgrammer_IUnion{
      Type: "resolved",
      Is: func() *shimast.Node {
        if isDate {
          return nativeiterate.Check_native(nativeiterate.Check_nativeProps{
            Name:  "Date",
            Input: props.Input,
          })
        }
        return nativeprogrammers.IsProgrammer.Decode_to_json(struct {
          Input     *shimast.Expression
          CheckNull bool
        }{Input: props.Input, CheckNull: false})
      },
      Value: func() *shimast.Node {
        next := props
        next.Metadata = metadata
        resolved := jsonStringifyProgrammer_decode_to_json(next)
        if isDate {
          return resolved
        }
        // The arm's own test is `typeof input.toJSON === "function"`, but an arm
        // test is not always emitted — a sole arm needs no choice, and a final
        // arm is the else — while this value *calls* `toJSON`. Leaving the call
        // to be guarded by the arm meant a value whose `toJSON` is not callable
        // threw `input.toJSON is not a function` from inside the serializer,
        // including under `isStringify` and `validateStringify`, whose contract
        // is to answer rather than throw (samchon/typia#2271).
        //
        // The guard therefore belongs to the call. `JSON.stringify` ignores a
        // non-callable `toJSON` and serializes the object itself, and the
        // metadata already carries that shape, so the fallback describes the
        // original rather than inventing an answer. `Date` keeps the plain form:
        // its arm is `instanceof Date`, and a real Date's `toJSON` is callable.
        original := props
        original.Metadata = props.Metadata.Escaped.Original
        return nativefactories.ExpressionFactory.Conditional(
          nativeprogrammers.IsProgrammer.Decode_to_json(struct {
            Input     *shimast.Expression
            CheckNull bool
          }{Input: props.Input, CheckNull: false}),
          resolved,
          jsonStringifyProgrammer_decode(original),
          props.Context.Emit,
        )
      },
    })
  } else if len(props.Metadata.Functions) != 0 {
    unions = append(unions, jsonStringifyProgrammer_IUnion{
      Type: "functional",
      Is: func() *shimast.Node {
        return nativeprogrammers.IsProgrammer.Decode_functional(props.Input)
      },
      Value: func() *shimast.Node {
        return jsonStringifyProgrammer_decode_functional(props.Explore, props.Context.Emit)
      },
    })
  }

  if len(props.Metadata.Templates) != 0 && nativehelpers.AtomicPredicator.Template(props.Metadata) {
    partial := schemametadata.MetadataSchema_initialize()
    partial.Atomics = append(partial.Atomics, schemametadata.MetadataAtomic_create(schemametadata.MetadataAtomic{
      Type: "string",
      Tags: [][]schemametadata.IMetadataTypeTag{},
    }))
    unions = append(unions, jsonStringifyProgrammer_IUnion{
      Type: "template literal",
      Is: func() *shimast.Node {
        next := props
        next.Metadata = partial
        return nativeprogrammers.IsProgrammer.Decode(nativeprogrammers.IsProgrammer_DecodeProps{
          Context:  next.Context,
          Functor:  next.Functor,
          Metadata: next.Metadata,
          Input:    next.Input,
          Explore:  jsonStringifyProgrammer_checker_explore(next.Explore),
          Prefix:   jsonStringifyProgrammer_checker_prefix(next.Validated),
        })
      },
      Value: func() *shimast.Node {
        next := props
        return jsonStringifyProgrammer_decode_atomic(struct {
          Context   nativecontext.ITypiaContext
          Input     *shimast.Node
          Type      string
          Explore   nativeinternal.FeatureProgrammer_IExplore
          Validated bool
        }{Context: next.Context, Input: next.Input, Type: "string", Explore: next.Explore, Validated: next.Validated})
      },
    })
  }

  for _, constant := range props.Metadata.Constants {
    constant := constant
    if nativehelpers.AtomicPredicator.RuntimeConstant(struct {
      Metadata *schemametadata.MetadataSchema
      Name     string
    }{Metadata: props.Metadata, Name: constant.Type}) == false {
      continue
    }
    if constant.Type != "string" {
      unions = append(unions, jsonStringifyProgrammer_IUnion{
        Type: "atomic",
        Is: func() *shimast.Node {
          partial := schemametadata.MetadataSchema_initialize()
          partial.Atomics = append(partial.Atomics, schemametadata.MetadataAtomic_create(schemametadata.MetadataAtomic{
            Type: constant.Type,
            Tags: [][]schemametadata.IMetadataTypeTag{},
          }))
          return nativeprogrammers.IsProgrammer.Decode(nativeprogrammers.IsProgrammer_DecodeProps{
            Context:  props.Context,
            Functor:  props.Functor,
            Metadata: partial,
            Input:    props.Input,
            Explore:  jsonStringifyProgrammer_checker_explore(props.Explore),
            Prefix:   jsonStringifyProgrammer_checker_prefix(props.Validated),
          })
        },
        Value: func() *shimast.Node {
          return jsonStringifyProgrammer_decode_atomic(struct {
            Context   nativecontext.ITypiaContext
            Input     *shimast.Node
            Type      string
            Explore   nativeinternal.FeatureProgrammer_IExplore
            Validated bool
          }{Context: props.Context, Input: props.Input, Type: constant.Type, Explore: props.Explore, Validated: props.Validated})
        },
      })
    } else if len(props.Metadata.Templates) == 0 {
      values := make([]string, 0, len(constant.Values))
      for _, value := range constant.Values {
        if str, ok := value.Value.(string); ok {
          values = append(values, str)
        }
      }
      unions = append(unions, jsonStringifyProgrammer_IUnion{
        Type: "const string",
        Is: func() *shimast.Node {
          partial := schemametadata.MetadataSchema_initialize()
          partial.Atomics = append(partial.Atomics, schemametadata.MetadataAtomic_create(schemametadata.MetadataAtomic{
            Type: "string",
            Tags: [][]schemametadata.IMetadataTypeTag{},
          }))
          return nativeprogrammers.IsProgrammer.Decode(nativeprogrammers.IsProgrammer_DecodeProps{
            Context:  props.Context,
            Functor:  props.Functor,
            Metadata: partial,
            Input:    props.Input,
            Explore:  jsonStringifyProgrammer_checker_explore(props.Explore),
            Prefix:   jsonStringifyProgrammer_checker_prefix(props.Validated),
          })
        },
        Value: func() *shimast.Node {
          return jsonStringifyProgrammer_decode_constant_string(struct {
            Context   nativecontext.ITypiaContext
            Functor   *nativehelpers.FunctionProgrammer
            Input     *shimast.Node
            Values    []string
            Explore   nativeinternal.FeatureProgrammer_IExplore
            Validated bool
          }{Context: props.Context, Functor: props.Functor, Input: props.Input, Values: values, Explore: props.Explore, Validated: props.Validated})
        },
      })
    }
  }

  for _, atomic := range props.Metadata.Atomics {
    atomic := atomic
    if nativehelpers.AtomicPredicator.RuntimeAtomic(struct {
      Metadata *schemametadata.MetadataSchema
      Name     string
    }{Metadata: props.Metadata, Name: atomic.Type}) {
      unions = append(unions, jsonStringifyProgrammer_IUnion{
        Type: "atomic",
        Is: func() *shimast.Node {
          partial := schemametadata.MetadataSchema_initialize()
          partial.Atomics = append(partial.Atomics, atomic)
          return nativeprogrammers.IsProgrammer.Decode(nativeprogrammers.IsProgrammer_DecodeProps{
            Context:  props.Context,
            Functor:  props.Functor,
            Metadata: partial,
            Input:    props.Input,
            Explore:  jsonStringifyProgrammer_checker_explore(props.Explore),
            Prefix:   jsonStringifyProgrammer_checker_prefix(props.Validated),
          })
        },
        Value: func() *shimast.Node {
          return jsonStringifyProgrammer_decode_atomic(struct {
            Context   nativecontext.ITypiaContext
            Input     *shimast.Node
            Type      string
            Explore   nativeinternal.FeatureProgrammer_IExplore
            Validated bool
          }{Context: props.Context, Input: props.Input, Type: atomic.Type, Explore: props.Explore, Validated: props.Validated})
        },
      })
    }
  }

  for _, tuple := range props.Metadata.Tuples {
    tuple := tuple
    unions = append(unions, jsonStringifyProgrammer_IUnion{
      Type: "tuple",
      Is: func() *shimast.Node {
        partial := schemametadata.MetadataSchema_initialize()
        partial.Tuples = append(partial.Tuples, tuple)
        return nativeprogrammers.IsProgrammer.Decode(nativeprogrammers.IsProgrammer_DecodeProps{
          Context:  props.Context,
          Functor:  props.Functor,
          Metadata: partial,
          Input:    props.Input,
          Explore:  jsonStringifyProgrammer_checker_explore(props.Explore),
          Prefix:   jsonStringifyProgrammer_checker_prefix(props.Validated),
        })
      },
      Value: func() *shimast.Node {
        return jsonStringifyProgrammer_decode_tuple(jsonStringifyProgrammer_decodeTupleProps{
          Context:   props.Context,
          Config:    props.Config,
          Functor:   props.Functor,
          Input:     props.Input,
          Tuple:     tuple,
          Explore:   props.Explore,
          Validated: props.Validated,
        })
      },
    })
  }

  if len(props.Metadata.Arrays) != 0 {
    value := func() *shimast.Node {
      if len(props.Metadata.Arrays) == 1 {
        explore := props.Explore
        explore.From = "array"
        return jsonStringifyProgrammer_decode_array(jsonStringifyProgrammer_decodeArrayProps{
          Context: props.Context,
          Config:  props.Config,
          Functor: props.Functor,
          Input:   props.Input,
          Array:   props.Metadata.Arrays[0],
          Explore: explore,
        })
      }
      if jsonStringifyProgrammer_some_arrays(props.Metadata.Arrays, func(elem *schemametadata.MetadataArray) bool {
        return elem.Type.Value.Any
      }) {
        return f.NewCallExpression(
          f.NewIdentifier("JSON.stringify"),
          nil,
          nil,
          f.NewNodeList([]*shimast.Node{props.Input}),
          shimast.NodeFlagsNone,
        )
      }
      explore := props.Explore
      explore.From = "array"
      return jsonStringifyProgrammer_explore_arrays(jsonStringifyProgrammer_exploreArraysProps{
        Context:   props.Context,
        Config:    props.Config,
        Functor:   props.Functor,
        Input:     props.Input,
        Arrays:    props.Metadata.Arrays,
        Explore:   explore,
        Validated: props.Validated,
      })
    }
    unions = append(unions, jsonStringifyProgrammer_IUnion{
      Type: "array",
      Is: func() *shimast.Node {
        return nativefactories.ExpressionFactory.IsArray(props.Input, props.Context.Emit)
      },
      Value: value,
    })
  }

  for _, native := range props.Metadata.Natives {
    native := native
    unions = append(unions, jsonStringifyProgrammer_IUnion{
      Type: "object",
      Is: func() *shimast.Node {
        return nativeiterate.Check_native(nativeiterate.Check_nativeProps{Name: native.Name, Input: props.Input})
      },
      Value: func() *shimast.Node {
        if nativehelpers.AtomicPredicator.Native(native.Name) {
          return jsonStringifyProgrammer_decode_atomic(struct {
            Context   nativecontext.ITypiaContext
            Input     *shimast.Node
            Type      string
            Explore   nativeinternal.FeatureProgrammer_IExplore
            Validated bool
          }{Context: props.Context, Input: props.Input, Type: strings.ToLower(native.Name), Explore: props.Explore, Validated: props.Validated})
        }
        return f.NewStringLiteral("{}", shimast.TokenFlagsNone)
      },
    })
  }

  if len(props.Metadata.Sets) != 0 {
    unions = append(unions, jsonStringifyProgrammer_IUnion{
      Type: "object",
      Is: func() *shimast.Node {
        return nativefactories.ExpressionFactory.IsInstanceOf("Set", props.Input, props.Context.Emit)
      },
      Value: func() *shimast.Node {
        return f.NewStringLiteral("{}", shimast.TokenFlagsNone)
      },
    })
  }
  if len(props.Metadata.Maps) != 0 {
    unions = append(unions, jsonStringifyProgrammer_IUnion{
      Type: "object",
      Is: func() *shimast.Node {
        return nativefactories.ExpressionFactory.IsInstanceOf("Map", props.Input, props.Context.Emit)
      },
      Value: func() *shimast.Node {
        return f.NewStringLiteral("{}", shimast.TokenFlagsNone)
      },
    })
  }
  if len(props.Metadata.Objects) != 0 {
    unions = append(unions, jsonStringifyProgrammer_IUnion{
      Type: "object",
      Is: func() *shimast.Node {
        return nativefactories.ExpressionFactory.IsObject(nativefactories.ExpressionFactory_IsObjectProps{
          CheckNull: true,
          CheckArray: jsonStringifyProgrammer_some_objects(props.Metadata.Objects, func(object *schemametadata.MetadataObject) bool {
            return jsonStringifyProgrammer_every_properties(object.Type.Properties, func(prop *schemametadata.MetadataProperty) bool {
              return !prop.Key.IsSoleLiteral() || !prop.Value.IsRequired()
            })
          }),
          Input: props.Input,
        }, props.Context.Emit)
      },
      Value: func() *shimast.Node {
        explore := props.Explore
        explore.From = "object"
        return jsonStringifyProgrammer_explore_objects(jsonStringifyProgrammer_exploreObjectsProps{
          Context:  props.Context,
          Config:   props.Config,
          Functor:  props.Functor,
          Input:    props.Input,
          Metadata: props.Metadata,
          Explore:  explore,
        })
      },
    })
  }

  wrapper := func(output *shimast.Node) *shimast.Node {
    return jsonStringifyProgrammer_wrap_required(struct {
      Context    nativecontext.ITypiaContext
      Input      *shimast.Node
      Metadata   *schemametadata.MetadataSchema
      Explore    nativeinternal.FeatureProgrammer_IExplore
      Expression *shimast.Node
    }{
      Context:  props.Context,
      Input:    props.Input,
      Metadata: props.Metadata,
      Explore:  props.Explore,
      Expression: jsonStringifyProgrammer_wrap_nullable(struct {
        Context    nativecontext.ITypiaContext
        Input      *shimast.Node
        Metadata   *schemametadata.MetadataSchema
        Expression *shimast.Node
      }{Context: props.Context, Input: props.Input, Metadata: props.Metadata, Expression: output}),
    })
  }
  if len(unions) == 0 {
    return f.NewCallExpression(
      f.NewIdentifier("JSON.stringify"),
      nil,
      nil,
      f.NewNodeList([]*shimast.Node{props.Input}),
      shimast.NodeFlagsNone,
    )
  }
  if len(unions) == 1 {
    return wrapper(unions[0].Value())
  }
  return wrapper(f.NewCallExpression(
    f.NewArrowFunction(
      nil,
      nil,
      f.NewNodeList(nil),
      nil,
      nil,
      f.NewToken(shimast.KindEqualsGreaterThanToken),
      jsonStringifyProgrammer_iterate(jsonStringifyProgrammer_iterateProps{
        Context:  props.Context,
        Functor:  props.Functor,
        Input:    props.Input,
        Expected: props.Metadata.GetDisplayName(),
        Unions:   unions,
      }),
    ),
    nil,
    nil,
    nil,
    shimast.NodeFlagsNone,
  ))
}

func jsonStringifyProgrammer_decode_object(props struct {
  Functor *nativehelpers.FunctionProgrammer
  Input   *shimast.Node
  Object  *schemametadata.MetadataObjectType
  Explore nativeinternal.FeatureProgrammer_IExplore
}) *shimast.Node {
  return nativeinternal.FeatureProgrammer.Decode_object(nativeinternal.FeatureProgrammer_DecodeObjectProps{
    Config:  nativeinternal.FeatureProgrammer_DecodeObjectConfig{Trace: false, Path: false, Prefix: jsonStringifyProgrammer_PREFIX, Visited: props.Functor.Visited()},
    Functor: props.Functor,
    Object:  props.Object,
    Input:   props.Input,
    Explore: props.Explore,
  })
}

type jsonStringifyProgrammer_decodeArrayProps struct {
  Context nativecontext.ITypiaContext
  Config  nativeinternal.FeatureProgrammer_IConfig
  Functor *nativehelpers.FunctionProgrammer
  Input   *shimast.Node
  Array   *schemametadata.MetadataArray
  Explore nativeinternal.FeatureProgrammer_IExplore
}

func jsonStringifyProgrammer_decode_array(props jsonStringifyProgrammer_decodeArrayProps) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  if props.Array.Type.Recursive {
    index := 0
    if props.Array.Type.Index != nil {
      index = *props.Array.Type.Index
    }
    return f.NewCallExpression(
      f.NewIdentifier(props.Functor.UseLocal(fmt.Sprintf("%sa%d", props.Config.Prefix, index))),
      nil,
      nil,
      f.NewNodeList(nativeinternal.FeatureProgrammer.ArgumentsArray(nativeinternal.FeatureProgrammer_ArgumentsArrayProps{
        Config:  nativeinternal.FeatureProgrammer_ArgumentsArrayConfig{Path: props.Config.Path, Trace: props.Config.Trace, Visited: props.Functor.Visited()},
        Input:   props.Input,
        Explore: jsonStringifyProgrammer_explore_with(props.Explore, "function", "array"),
      })),
      shimast.NodeFlagsNone,
    )
  }
  return jsonStringifyProgrammer_decode_array_inline(props)
}

func jsonStringifyProgrammer_decode_array_inline(props jsonStringifyProgrammer_decodeArrayProps) *shimast.Node {
  return nativeinternal.FeatureProgrammer.Decode_array(nativeinternal.FeatureProgrammer_DecodeArrayProps{
    Config: nativeinternal.FeatureProgrammer_DecodeArrayConfig{
      Trace:   props.Config.Trace,
      Path:    props.Config.Path,
      Decoder: props.Config.Decoder,
      Prefix:  props.Config.Prefix,
    },
    Functor: props.Functor,
    Combiner: func(next struct {
      Input *shimast.Expression
      Arrow *shimast.Node
    }) *shimast.Node {
      return nativehelpers.StringifyJoiner.Array(nativehelpers.StringifyJoiner_ArrayProps{
        Context: props.Context,
        Input:   next.Input,
        Arrow:   next.Arrow,
        Emit:    props.Context.Emit,
      })
    },
    Array:   props.Array,
    Input:   props.Input,
    Explore: props.Explore,
  })
}

type jsonStringifyProgrammer_decodeTupleProps struct {
  Context   nativecontext.ITypiaContext
  Config    nativeinternal.FeatureProgrammer_IConfig
  Functor   *nativehelpers.FunctionProgrammer
  Input     *shimast.Node
  Tuple     *schemametadata.MetadataTuple
  Explore   nativeinternal.FeatureProgrammer_IExplore
  Validated bool
}

func jsonStringifyProgrammer_decode_tuple(props jsonStringifyProgrammer_decodeTupleProps) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  if props.Tuple.Type.Recursive {
    index := 0
    if props.Tuple.Type.Index != nil {
      index = *props.Tuple.Type.Index
    }
    return f.NewCallExpression(
      f.NewIdentifier(props.Functor.UseLocal(fmt.Sprintf("%st%d", props.Config.Prefix, index))),
      nil,
      nil,
      f.NewNodeList(nativeinternal.FeatureProgrammer.ArgumentsArray(nativeinternal.FeatureProgrammer_ArgumentsArrayProps{
        Config:  nativeinternal.FeatureProgrammer_ArgumentsArrayConfig{Path: props.Config.Path, Trace: props.Config.Trace, Visited: props.Functor.Visited()},
        Explore: jsonStringifyProgrammer_explore_with(props.Explore, "function", props.Explore.From),
        Input:   props.Input,
      })),
      shimast.NodeFlagsNone,
    )
  }
  return jsonStringifyProgrammer_decode_tuple_inline(jsonStringifyProgrammer_decodeTupleInlineProps{
    Context:   props.Context,
    Config:    props.Config,
    Functor:   props.Functor,
    Input:     props.Input,
    Tuple:     props.Tuple.Type,
    Explore:   props.Explore,
    Validated: props.Validated,
  })
}

type jsonStringifyProgrammer_decodeTupleInlineProps struct {
  Context   nativecontext.ITypiaContext
  Config    nativeinternal.FeatureProgrammer_IConfig
  Functor   *nativehelpers.FunctionProgrammer
  Input     *shimast.Node
  Tuple     *schemametadata.MetadataTupleType
  Explore   nativeinternal.FeatureProgrammer_IExplore
  Validated bool
}

func jsonStringifyProgrammer_decode_tuple_inline(props jsonStringifyProgrammer_decodeTupleInlineProps) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  elements := []*shimast.Node{}
  for index, elem := range props.Tuple.Elements {
    if elem.Rest != nil {
      continue
    }
    explore := props.Explore
    explore.From = "array"
    if len(props.Explore.Postfix) != 0 {
      explore.Postfix = fmt.Sprintf("%s[%d]\"", nativeiterate.Postfix_of_tuple_export(props.Explore.Postfix), index)
    } else {
      explore.Postfix = fmt.Sprintf("\"[%d]\"", index)
    }
    code := jsonStringifyProgrammer_decode(struct {
      Context   nativecontext.ITypiaContext
      Config    nativeinternal.FeatureProgrammer_IConfig
      Functor   *nativehelpers.FunctionProgrammer
      Input     *shimast.Node
      Metadata  *schemametadata.MetadataSchema
      Explore   nativeinternal.FeatureProgrammer_IExplore
      Validated bool
    }{
      Context:   props.Context,
      Config:    props.Config,
      Functor:   props.Functor,
      Input:     f.NewElementAccessExpression(props.Input, nil, nativefactories.ExpressionFactory.Number(index, props.Context.Emit), shimast.NodeFlagsNone),
      Metadata:  elem,
      Explore:   explore,
      Validated: props.Validated,
    })
    if nativehelpers.StringifyJoiner_ResultUndefinable(elem) {
      // A tuple slot holds its position, so an element that serializes to
      // nothing is `null` rather than omitted; without this its text was the
      // literal `undefined` (#2253).
      code = f.NewCallExpression(
        jsonStringifyProgrammer_internal(props.Context, "jsonStringifyElement"),
        nil,
        nil,
        f.NewNodeList([]*shimast.Node{code}),
        shimast.NodeFlagsNone,
      )
    }
    elements = append(elements, code)
  }
  var rest *shimast.Node
  if len(props.Tuple.Elements) != 0 {
    last := props.Tuple.Elements[len(props.Tuple.Elements)-1]
    if last.Rest != nil {
      explore := props.Explore
      start := len(props.Tuple.Elements) - 1
      explore.Start = &start
      code := jsonStringifyProgrammer_decode(struct {
        Context   nativecontext.ITypiaContext
        Config    nativeinternal.FeatureProgrammer_IConfig
        Functor   *nativehelpers.FunctionProgrammer
        Input     *shimast.Node
        Metadata  *schemametadata.MetadataSchema
        Explore   nativeinternal.FeatureProgrammer_IExplore
        Validated bool
      }{
        Context: props.Context,
        Config:  props.Config,
        Functor: props.Functor,
        Input: f.NewCallExpression(
          nativefactories.IdentifierFactory.Access(props.Context.Emit, props.Input, "slice"),
          nil,
          nil,
          f.NewNodeList([]*shimast.Node{nativefactories.ExpressionFactory.Number(start, props.Context.Emit)}),
          shimast.NodeFlagsNone,
        ),
        Metadata:  nativeiterate.Wrap_metadata_rest_tuple_export(last.Rest),
        Explore:   explore,
        Validated: props.Validated,
      })
      rest = f.NewCallExpression(
        jsonStringifyProgrammer_internal(props.Context, "jsonStringifyRest"),
        nil,
        nil,
        f.NewNodeList([]*shimast.Node{code}),
        shimast.NodeFlagsNone,
      )
    }
  }
  return nativehelpers.StringifyJoiner.Tuple(nativehelpers.StringifyJoiner_TupleProps{
    Elements: elements,
    Rest:     rest,
    Emit:     props.Context.Emit,
  })
}

func jsonStringifyProgrammer_decode_atomic(props struct {
  Context   nativecontext.ITypiaContext
  Input     *shimast.Node
  Type      string
  Explore   nativeinternal.FeatureProgrammer_IExplore
  Validated bool
}) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  input := props.Input
  if props.Type == "string" {
    return f.NewCallExpression(
      jsonStringifyProgrammer_internal(props.Context, "jsonStringifyString"),
      nil,
      nil,
      f.NewNodeList([]*shimast.Node{props.Input}),
      shimast.NodeFlagsNone,
    )
  } else if props.Type == "number" {
    if !(props.Validated && nativehelpers.OptionPredicator.Finite(props.Context.Options)) {
      input = f.NewCallExpression(
        jsonStringifyProgrammer_internal(props.Context, "jsonStringifyNumber"),
        nil,
        nil,
        f.NewNodeList([]*shimast.Node{props.Input}),
        shimast.NodeFlagsNone,
      )
    }
  }
  return f.NewCallExpression(
    f.NewIdentifier("String"),
    nil,
    nil,
    f.NewNodeList([]*shimast.Node{input}),
    shimast.NodeFlagsNone,
  )
}

func jsonStringifyProgrammer_decode_constant_string(props struct {
  Context   nativecontext.ITypiaContext
  Functor   *nativehelpers.FunctionProgrammer
  Input     *shimast.Node
  Values    []string
  Explore   nativeinternal.FeatureProgrammer_IExplore
  Validated bool
}) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  if jsonStringifyProgrammer_every_strings(props.Values, func(value string) bool {
    return nativehelpers.StringifyPredicator.Require_escape(value) == false
  }) {
    return jsonStringifyProgrammer_binary(
      jsonStringifyProgrammer_binary(
        f.NewStringLiteral("\"", shimast.TokenFlagsNone),
        shimast.KindPlusToken,
        props.Input,
        props.Context.Emit,
      ),
      shimast.KindPlusToken,
      f.NewStringLiteral("\"", shimast.TokenFlagsNone),
      props.Context.Emit,
    )
  }
  return jsonStringifyProgrammer_decode_atomic(struct {
    Context   nativecontext.ITypiaContext
    Input     *shimast.Node
    Type      string
    Explore   nativeinternal.FeatureProgrammer_IExplore
    Validated bool
  }{Context: props.Context, Input: props.Input, Type: "string", Explore: props.Explore, Validated: props.Validated})
}

func jsonStringifyProgrammer_decode_to_json(props struct {
  Context   nativecontext.ITypiaContext
  Config    nativeinternal.FeatureProgrammer_IConfig
  Functor   *nativehelpers.FunctionProgrammer
  Input     *shimast.Node
  Metadata  *schemametadata.MetadataSchema
  Explore   nativeinternal.FeatureProgrammer_IExplore
  Validated bool
}) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  next := props
  next.Input = f.NewCallExpression(
    nativefactories.IdentifierFactory.Access(props.Context.Emit, props.Input, "toJSON"),
    nil,
    nil,
    f.NewNodeList(nil),
    shimast.NodeFlagsNone,
  )
  return jsonStringifyProgrammer_decode(next)
}

func jsonStringifyProgrammer_decode_functional(explore nativeinternal.FeatureProgrammer_IExplore, emit *shimprinter.EmitContext) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, emit)
  if explore.From == "array" {
    return f.NewStringLiteral("null", shimast.TokenFlagsNone)
  }
  return f.NewIdentifier("undefined")
}

type jsonStringifyProgrammer_exploreObjectsProps struct {
  Context  nativecontext.ITypiaContext
  Config   nativeinternal.FeatureProgrammer_IConfig
  Functor  *nativehelpers.FunctionProgrammer
  Input    *shimast.Node
  Metadata *schemametadata.MetadataSchema
  Explore  nativeinternal.FeatureProgrammer_IExplore
}

func jsonStringifyProgrammer_explore_objects(props jsonStringifyProgrammer_exploreObjectsProps) *shimast.Node {
  if len(props.Metadata.Objects) == 1 {
    return jsonStringifyProgrammer_decode_object(struct {
      Functor *nativehelpers.FunctionProgrammer
      Input   *shimast.Node
      Object  *schemametadata.MetadataObjectType
      Explore nativeinternal.FeatureProgrammer_IExplore
    }{
      Functor: props.Functor,
      Input:   props.Input,
      Object:  props.Metadata.Objects[0].Type,
      Explore: props.Explore,
    })
  }
  index := 0
  if props.Metadata.Union_index != nil {
    index = *props.Metadata.Union_index
  }
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  return f.NewCallExpression(
    f.NewIdentifier(props.Functor.UseLocal(fmt.Sprintf("%su%d", jsonStringifyProgrammer_PREFIX, index))),
    nil,
    nil,
    f.NewNodeList(nativeinternal.FeatureProgrammer.ArgumentsArray(nativeinternal.FeatureProgrammer_ArgumentsArrayProps{
      Config:  nativeinternal.FeatureProgrammer_ArgumentsArrayConfig{Path: props.Config.Path, Trace: props.Config.Trace, Visited: props.Functor.Visited()},
      Input:   props.Input,
      Explore: props.Explore,
    })),
    shimast.NodeFlagsNone,
  )
}

type jsonStringifyProgrammer_exploreArraysProps struct {
  Context   nativecontext.ITypiaContext
  Config    nativeinternal.FeatureProgrammer_IConfig
  Functor   *nativehelpers.FunctionProgrammer
  Input     *shimast.Node
  Arrays    []*schemametadata.MetadataArray
  Explore   nativeinternal.FeatureProgrammer_IExplore
  Validated bool
}

func jsonStringifyProgrammer_explore_arrays(props jsonStringifyProgrammer_exploreArraysProps) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  return jsonStringifyProgrammer_explore_array_like_union_types(jsonStringifyProgrammer_exploreArrayLikeUnionTypesProps[*schemametadata.MetadataArray]{
    Context: props.Context,
    Config:  props.Config,
    Functor: props.Functor,
    Factory: func(next jsonStringifyProgrammer_arrayLikeFactoryProps[*schemametadata.MetadataArray]) *shimast.Node {
      return nativehelpers.UnionExplorer.Array(nativehelpers.UnionExplorer_ArrayProps{
        Emit: props.Context.Emit,
        Config: nativehelpers.UnionExplorer_ArrayLikeConfig{
          Checker: func(v nativehelpers.UnionExplorer_ArrayLikeCheckerProps) *shimast.Node {
            return nativeprogrammers.IsProgrammer.Decode(nativeprogrammers.IsProgrammer_DecodeProps{
              Context:  props.Context,
              Functor:  props.Functor,
              Metadata: v.Definition.(*schemametadata.MetadataSchema),
              Input:    v.Input,
              Explore:  jsonStringifyProgrammer_checker_explore(v.Explore),
              Prefix:   jsonStringifyProgrammer_checker_prefix(props.Validated),
            })
          },
          Decoder: func(v nativehelpers.UnionExplorer_ArrayLikeDecoderProps) *shimast.Node {
            return jsonStringifyProgrammer_decode_array(jsonStringifyProgrammer_decodeArrayProps{
              Context: props.Context,
              Config:  props.Config,
              Functor: props.Functor,
              Input:   v.Input,
              Array:   v.Definition.(*schemametadata.MetadataArray),
              Explore: jsonStringifyProgrammer_feature_explore(v.Explore),
            })
          },
          Empty:   f.NewStringLiteral("[]", shimast.TokenFlagsNone),
          Success: f.NewKeywordExpression(shimast.KindTrueKeyword),
          Failure: func(v nativehelpers.UnionExplorer_ArrayLikeFailureProps) *shimast.Node {
            return jsonStringifyProgrammer_create_throw_error(jsonStringifyProgrammer_throwProps{
              Context:  props.Context,
              Functor:  props.Functor,
              Expected: v.Expected,
              Input:    v.Input,
            })
          },
        },
        Parameters: next.Parameters,
        Input:      next.Input,
        Arrays:     next.Definitions,
        Explore:    next.Explore,
      })
    },
    Input:    props.Input,
    Elements: props.Arrays,
    Explore:  props.Explore,
  })
}

type jsonStringifyProgrammer_arrayLikeFactoryProps[T any] struct {
  Parameters  []*shimast.Node
  Input       *shimast.Node
  Definitions []T
  Explore     nativeinternal.FeatureProgrammer_IExplore
}

type jsonStringifyProgrammer_exploreArrayLikeUnionTypesProps[T interface {
  *schemametadata.MetadataArray | *schemametadata.MetadataTuple
}] struct {
  Context  nativecontext.ITypiaContext
  Config   nativeinternal.FeatureProgrammer_IConfig
  Functor  *nativehelpers.FunctionProgrammer
  Factory  func(next jsonStringifyProgrammer_arrayLikeFactoryProps[T]) *shimast.Node
  Input    *shimast.Node
  Elements []T
  Explore  nativeinternal.FeatureProgrammer_IExplore
}

func jsonStringifyProgrammer_explore_array_like_union_types[T interface {
  *schemametadata.MetadataArray | *schemametadata.MetadataTuple
}](props jsonStringifyProgrammer_exploreArrayLikeUnionTypesProps[T]) *shimast.Node {
  arrow := func(next struct {
    Parameters []*shimast.Node
    Explore    nativeinternal.FeatureProgrammer_IExplore
    Input      *shimast.Node
  }) *shimast.Node {
    return props.Factory(jsonStringifyProgrammer_arrayLikeFactoryProps[T]{
      Definitions: props.Elements,
      Parameters:  next.Parameters,
      Input:       next.Input,
      Explore:     next.Explore,
    })
  }
  arrayExplore := props.Explore
  arrayExplore.Source = "function"
  arrayExplore.From = "array"
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  return f.NewCallExpression(
    f.NewIdentifier(props.Functor.EmplaceUnion(props.Config.Prefix, jsonStringifyProgrammer_array_like_names(props.Elements), func() *shimast.Node {
      explore := arrayExplore
      explore.Postfix = ""
      return arrow(struct {
        Parameters []*shimast.Node
        Explore    nativeinternal.FeatureProgrammer_IExplore
        Input      *shimast.Node
      }{
        Parameters: nativeinternal.FeatureProgrammer.ParameterDeclarations(nativeinternal.FeatureProgrammer_ParameterDeclarationsProps{
          Config: nativeinternal.FeatureProgrammer_ParameterConfig{Path: props.Config.Path, Trace: props.Config.Trace, Visited: props.Functor.Visited()},
          Type:   nativefactories.TypeFactory.Keyword("any", props.Context.Emit),
          Input:  f.NewIdentifier("input"),
        }),
        Explore: explore,
        Input:   f.NewIdentifier("input"),
      })
    })),
    nil,
    nil,
    f.NewNodeList(nativeinternal.FeatureProgrammer.ArgumentsArray(nativeinternal.FeatureProgrammer_ArgumentsArrayProps{
      Config:  nativeinternal.FeatureProgrammer_ArgumentsArrayConfig{Path: props.Config.Path, Trace: props.Config.Trace, Visited: props.Functor.Visited()},
      Explore: arrayExplore,
      Input:   props.Input,
    })),
    shimast.NodeFlagsNone,
  )
}

func jsonStringifyProgrammer_wrap_required(props struct {
  Context    nativecontext.ITypiaContext
  Input      *shimast.Node
  Metadata   *schemametadata.MetadataSchema
  Explore    nativeinternal.FeatureProgrammer_IExplore
  Expression *shimast.Node
}) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  if props.Metadata.IsRequired() && props.Metadata.Any == false {
    return props.Expression
  }
  alternate := f.NewIdentifier("undefined")
  if props.Explore.From == "array" {
    alternate = f.NewStringLiteral("null", shimast.TokenFlagsNone)
  }
  return nativefactories.ExpressionFactory.Conditional(
    jsonStringifyProgrammer_binary(f.NewIdentifier("undefined"), shimast.KindExclamationEqualsEqualsToken, props.Input, props.Context.Emit),
    props.Expression,
    alternate,
    props.Context.Emit,
  )
}

func jsonStringifyProgrammer_wrap_nullable(props struct {
  Context    nativecontext.ITypiaContext
  Input      *shimast.Node
  Metadata   *schemametadata.MetadataSchema
  Expression *shimast.Node
}) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  if props.Metadata.Nullable == false {
    return props.Expression
  }
  return nativefactories.ExpressionFactory.Conditional(
    jsonStringifyProgrammer_binary(f.NewKeywordExpression(shimast.KindNullKeyword), shimast.KindExclamationEqualsEqualsToken, props.Input, props.Context.Emit),
    props.Expression,
    f.NewStringLiteral("null", shimast.TokenFlagsNone),
    props.Context.Emit,
  )
}

func jsonStringifyProgrammer_wrap_functional(props struct {
  Context    nativecontext.ITypiaContext
  Input      *shimast.Node
  Metadata   *schemametadata.MetadataSchema
  Explore    nativeinternal.FeatureProgrammer_IExplore
  Expression *shimast.Node
}) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  if len(props.Metadata.Functions) == 0 {
    return props.Expression
  }
  return nativefactories.ExpressionFactory.Conditional(
    jsonStringifyProgrammer_binary(f.NewStringLiteral("function", shimast.TokenFlagsNone), shimast.KindExclamationEqualsEqualsToken, nativefactories.ValueFactory.TYPEOF(props.Input), props.Context.Emit),
    props.Expression,
    jsonStringifyProgrammer_decode_functional(props.Explore, props.Context.Emit),
    props.Context.Emit,
  )
}

type jsonStringifyProgrammer_iterateProps struct {
  Context  nativecontext.ITypiaContext
  Functor  *nativehelpers.FunctionProgrammer
  Input    *shimast.Node
  Unions   []jsonStringifyProgrammer_IUnion
  Expected string
}

func jsonStringifyProgrammer_iterate(props jsonStringifyProgrammer_iterateProps) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  statements := []*shimast.Node{}
  for _, union := range props.Unions {
    statements = append(statements, f.NewIfStatement(
      union.Is(),
      f.NewReturnStatement(union.Value()),
      nil,
    ))
  }
  statements = append(statements, jsonStringifyProgrammer_create_throw_error(jsonStringifyProgrammer_throwProps{
    Context:  props.Context,
    Functor:  props.Functor,
    Input:    props.Input,
    Expected: props.Expected,
  }))
  return f.NewBlock(f.NewNodeList(statements), true)
}

func jsonStringifyProgrammer_configure(props struct {
  Context   nativecontext.ITypiaContext
  Functor   *nativehelpers.FunctionProgrammer
  Validated bool
}) nativeinternal.FeatureProgrammer_IConfig {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  checkerPrefix := jsonStringifyProgrammer_checker_prefix(props.Validated)
  var config nativeinternal.FeatureProgrammer_IConfig
  config = nativeinternal.FeatureProgrammer_IConfig{
    Types: nativeinternal.FeatureProgrammer_IConfig_ITypes{
      Input: func(t *shimchecker.Type, name *string) *shimast.TypeNode {
        if name != nil {
          return f.NewTypeReferenceNode(f.NewIdentifier(*name), nil)
        }
        return f.NewTypeReferenceNode(f.NewIdentifier(nativefactories.TypeFactory.GetFullName(nativefactories.TypeFactory_GetFullNameProps{
          Checker: props.Context.Checker,
          Type:    t,
        })), nil)
      },
      Output: func(t *shimchecker.Type, name *string) *shimast.TypeNode {
        return nativefactories.TypeFactory.Keyword("string", props.Context.Emit)
      },
    },
    Prefix:      jsonStringifyProgrammer_PREFIX,
    Trace:       false,
    Path:        false,
    Initializer: jsonStringifyProgrammer_initializer,
    Visited:     props.Functor.Visited,
    VisitGuard: func(next nativeinternal.FeatureProgrammer_VisitGuardProps) *shimast.Node {
      return nativeinternal.FeatureProgrammer.VisitGuardSerialize(
        next.Key,
        jsonStringifyProgrammer_circular_thrower(props.Context, props.Functor),
        next.Body,
        props.Context.Emit,
      )
    },
    Decoder: func(next nativeinternal.FeatureProgrammer_DecoderProps) *shimast.Node {
      return jsonStringifyProgrammer_decode(struct {
        Context   nativecontext.ITypiaContext
        Config    nativeinternal.FeatureProgrammer_IConfig
        Functor   *nativehelpers.FunctionProgrammer
        Input     *shimast.Node
        Metadata  *schemametadata.MetadataSchema
        Explore   nativeinternal.FeatureProgrammer_IExplore
        Validated bool
      }{
        Context:   props.Context,
        Functor:   props.Functor,
        Config:    config,
        Metadata:  next.Metadata,
        Input:     next.Input,
        Explore:   next.Explore,
        Validated: props.Validated,
      })
    },
    Objector: nativeinternal.FeatureProgrammer_IConfig_IObjector{
      Checker: func(next nativeinternal.FeatureProgrammer_ObjectorCheckerProps) *shimast.Node {
        return nativeprogrammers.IsProgrammer.Decode(nativeprogrammers.IsProgrammer_DecodeProps{
          Context:  props.Context,
          Functor:  props.Functor,
          Metadata: next.Metadata,
          Input:    next.Input,
          Explore:  jsonStringifyProgrammer_checker_explore(next.Explore),
          Prefix:   checkerPrefix,
        })
      },
      Decoder: func(next nativeinternal.FeatureProgrammer_ObjectorDecoderProps) *shimast.Node {
        return jsonStringifyProgrammer_decode_object(struct {
          Functor *nativehelpers.FunctionProgrammer
          Input   *shimast.Node
          Object  *schemametadata.MetadataObjectType
          Explore nativeinternal.FeatureProgrammer_IExplore
        }{Functor: props.Functor, Input: next.Input, Object: next.Object, Explore: next.Explore})
      },
      Joiner: func(next nativeinternal.FeatureProgrammer_ObjectorJoinerProps) *shimast.Node {
        return nativehelpers.StringifyJoiner.Object(nativehelpers.StringifyJoiner_ObjectProps{
          Context: props.Context,
          Entries: next.Entries,
        })
      },
      Unionizer: func(next nativeinternal.FeatureProgrammer_ObjectorUnionizerProps) *shimast.Node {
        return nativeiterate.Decode_union_object(nativeiterate.Decode_union_objectProps{
          Checker: func(v nativeiterate.Decode_union_object_next) *shimast.Node {
            return nativeprogrammers.IsProgrammer.Decode_object(nativeprogrammers.IsProgrammer_DecodeObjectProps{
              Context: props.Context,
              Functor: props.Functor,
              Input:   v.Input,
              Object:  v.Object,
              Explore: jsonStringifyProgrammer_feature_explore(v.Explore),
              Prefix:  checkerPrefix,
            })
          },
          Decoder: func(v nativeiterate.Decode_union_object_next) *shimast.Node {
            return jsonStringifyProgrammer_decode_object(struct {
              Functor *nativehelpers.FunctionProgrammer
              Input   *shimast.Node
              Object  *schemametadata.MetadataObjectType
              Explore nativeinternal.FeatureProgrammer_IExplore
            }{Functor: props.Functor, Input: v.Input, Object: v.Object, Explore: jsonStringifyProgrammer_feature_explore(v.Explore)})
          },
          Success: func(exp *shimast.Expression) *shimast.Node {
            return exp
          },
          Escaper: func(v nativeiterate.Decode_union_object_escape) *shimast.Node {
            return jsonStringifyProgrammer_create_throw_error(jsonStringifyProgrammer_throwProps{
              Context:  props.Context,
              Functor:  props.Functor,
              Expected: v.Expected,
              Input:    v.Input,
            })
          },
          Objects: next.Objects,
          Explore: next.Explore,
          Input:   next.Input,
        })
      },
      Failure: func(next nativeinternal.FeatureProgrammer_ObjectorFailureProps) *shimast.Node {
        return jsonStringifyProgrammer_create_throw_error(jsonStringifyProgrammer_throwProps{
          Context:  props.Context,
          Functor:  props.Functor,
          Expected: next.Expected,
          Input:    next.Input,
        })
      },
    },
    Generator: nativeinternal.FeatureProgrammer_IConfig_IGenerator{
      Arrays: func(collection *schemametadata.MetadataCollection) []*shimast.Node {
        return jsonStringifyProgrammer_write_array_functions(struct {
          Context    nativecontext.ITypiaContext
          Config     nativeinternal.FeatureProgrammer_IConfig
          Functor    *nativehelpers.FunctionProgrammer
          Collection *schemametadata.MetadataCollection
        }{Context: props.Context, Config: config, Functor: props.Functor, Collection: collection})
      },
      Tuples: func(collection *schemametadata.MetadataCollection) []*shimast.Node {
        return jsonStringifyProgrammer_write_tuple_functions(struct {
          Context    nativecontext.ITypiaContext
          Config     nativeinternal.FeatureProgrammer_IConfig
          Functor    *nativehelpers.FunctionProgrammer
          Collection *schemametadata.MetadataCollection
          Validated  bool
        }{Context: props.Context, Config: config, Functor: props.Functor, Collection: collection, Validated: props.Validated})
      },
    },
  }
  return config
}

func jsonStringifyProgrammer_initializer(props nativeinternal.FeatureProgrammer_InitializerProps) nativeinternal.FeatureProgrammer_InitializerOutput {
  result := nativefactories.JsonMetadataFactory.Analyze(nativefactories.JsonMetadataFactory_IProps{
    Method:  props.Functor.Method,
    Checker: props.Context.Checker,
    Type:    props.Type,
  })
  if nativeinternal.FeatureProgrammer.CollectionRecursive(result.Collection) {
    props.Functor.SetVisited(true)
  }
  return nativeinternal.FeatureProgrammer_InitializerOutput{
    Collection: result.Collection,
    Metadata:   result.Metadata,
  }
}

// jsonStringifyProgrammer_circular_thrower raises the cycle error used by the
// on-stack visit guard: JSON cannot represent circular references, so meeting
// a value again while it is still being serialized must fail fast instead of
// overflowing the stack.
func jsonStringifyProgrammer_circular_thrower(context nativecontext.ITypiaContext, functor *nativehelpers.FunctionProgrammer) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, context.Emit)
  return f.NewCallExpression(
    jsonStringifyProgrammer_internal(context, "throwTypeGuardError"),
    nil,
    f.NewNodeList(nil),
    f.NewNodeList([]*shimast.Node{
      f.NewObjectLiteralExpression(
        f.NewNodeList([]*shimast.Node{
          f.NewPropertyAssignment(nil, nativefactories.IdentifierFactory.Identifier("method", context.Emit), nil, nil, f.NewStringLiteral(functor.Method, shimast.TokenFlagsNone)),
          f.NewPropertyAssignment(nil, nativefactories.IdentifierFactory.Identifier("expected", context.Emit), nil, nil, f.NewStringLiteral("non-circular reference", shimast.TokenFlagsNone)),
          f.NewPropertyAssignment(nil, nativefactories.IdentifierFactory.Identifier("value", context.Emit), nil, nil, f.NewIdentifier("input")),
        }),
        true,
      ),
    }),
    shimast.NodeFlagsNone,
  )
}

type jsonStringifyProgrammer_throwProps struct {
  Context  nativecontext.ITypiaContext
  Functor  *nativehelpers.FunctionProgrammer
  Expected string
  Input    *shimast.Node
}

func jsonStringifyProgrammer_create_throw_error(props jsonStringifyProgrammer_throwProps) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, props.Context.Emit)
  return f.NewExpressionStatement(
    f.NewCallExpression(
      jsonStringifyProgrammer_internal(props.Context, "throwTypeGuardError"),
      nil,
      f.NewNodeList(nil),
      f.NewNodeList([]*shimast.Node{
        f.NewObjectLiteralExpression(
          f.NewNodeList([]*shimast.Node{
            f.NewPropertyAssignment(nil, nativefactories.IdentifierFactory.Identifier("method", props.Context.Emit), nil, nil, f.NewStringLiteral(props.Functor.Method, shimast.TokenFlagsNone)),
            f.NewPropertyAssignment(nil, nativefactories.IdentifierFactory.Identifier("expected", props.Context.Emit), nil, nil, f.NewStringLiteral(props.Expected, shimast.TokenFlagsNone)),
            f.NewPropertyAssignment(nil, nativefactories.IdentifierFactory.Identifier("value", props.Context.Emit), nil, nil, props.Input),
          }),
          true,
        ),
      }),
      shimast.NodeFlagsNone,
    ),
  )
}

type jsonStringifyProgrammer_IUnion struct {
  Type  string
  Is    func() *shimast.Node
  Value func() *shimast.Node
}

func jsonStringifyProgrammer_binary(left *shimast.Node, operator shimast.Kind, right *shimast.Node, emit *shimprinter.EmitContext) *shimast.Node {
  f := nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, emit)
  return f.NewBinaryExpression(nil, left, nil, f.NewToken(operator), right)
}

func jsonStringifyProgrammer_internal(context nativecontext.ITypiaContext, name string) *shimast.Node {
  if importer := context.Importer; importer != nil {
    return importer.Internal(name)
  }
  return nativecontext.EmitFactoryOf(jsonStringifyProgrammer_factory, context.Emit).NewIdentifier(name)
}

func jsonStringifyProgrammer_method_text(modulo *shimast.Node) string {
  return nativehelpers.ModuloMethodText(modulo)
}

func jsonStringifyProgrammer_checker_prefix(validated bool) string {
  if validated {
    return jsonStringifyProgrammer_CHECKER_PREFIX_VALIDATED
  }
  return jsonStringifyProgrammer_CHECKER_PREFIX
}

func jsonStringifyProgrammer_feature_explore(input any) nativeinternal.FeatureProgrammer_IExplore {
  switch v := input.(type) {
  case nativeinternal.FeatureProgrammer_IExplore:
    return v
  case *nativeinternal.FeatureProgrammer_IExplore:
    return *v
  default:
    return nativeinternal.FeatureProgrammer_IExplore{}
  }
}

func jsonStringifyProgrammer_checker_explore(input any) nativeinternal.CheckerProgrammer_IExplore {
  v := jsonStringifyProgrammer_feature_explore(input)
  return nativeinternal.CheckerProgrammer_IExplore{
    Tracable: v.Tracable,
    Source:   v.Source,
    From:     v.From,
    Postfix:  v.Postfix,
    Start:    v.Start,
  }
}

func jsonStringifyProgrammer_explore_with(explore nativeinternal.FeatureProgrammer_IExplore, source string, from string) nativeinternal.FeatureProgrammer_IExplore {
  explore.Source = source
  explore.From = from
  return explore
}

func jsonStringifyProgrammer_some_arrays(values []*schemametadata.MetadataArray, pred func(*schemametadata.MetadataArray) bool) bool {
  for _, value := range values {
    if pred(value) {
      return true
    }
  }
  return false
}

func jsonStringifyProgrammer_some_objects(values []*schemametadata.MetadataObject, pred func(*schemametadata.MetadataObject) bool) bool {
  for _, value := range values {
    if pred(value) {
      return true
    }
  }
  return false
}

func jsonStringifyProgrammer_every_properties(values []*schemametadata.MetadataProperty, pred func(*schemametadata.MetadataProperty) bool) bool {
  for _, value := range values {
    if pred(value) == false {
      return false
    }
  }
  return true
}

func jsonStringifyProgrammer_every_strings(values []string, pred func(string) bool) bool {
  for _, value := range values {
    if pred(value) == false {
      return false
    }
  }
  return true
}

func jsonStringifyProgrammer_array_like_names[T interface {
  *schemametadata.MetadataArray | *schemametadata.MetadataTuple
}](values []T) string {
  names := make([]string, 0, len(values))
  for _, value := range values {
    switch typed := any(value).(type) {
    case *schemametadata.MetadataArray:
      names = append(names, typed.Type.Name)
    case *schemametadata.MetadataTuple:
      names = append(names, typed.Type.Name)
    }
  }
  return strings.Join(names, " | ")
}

func jsonStringifyProgrammer_merge_functions(groups ...map[string]*shimast.Node) map[string]*shimast.Node {
  output := map[string]*shimast.Node{}
  for _, group := range groups {
    for key, value := range group {
      output[key] = value
    }
  }
  return output
}
