{{ reserveImport "context"  }}
{{ reserveImport "fmt"  }}
{{ reserveImport "io"  }}
{{ reserveImport "strconv"  }}
{{ reserveImport "time"  }}
{{ reserveImport "sync"  }}
{{ reserveImport "sync/atomic" }}
{{ reserveImport "errors"  }}
{{ reserveImport "bytes"  }}
{{ reserveImport "embed"  }}

{{ reserveImport "github.com/vektah/gqlparser/v2" "gqlparser" }}
{{ reserveImport "github.com/vektah/gqlparser/v2/ast" }}
{{ reserveImport "github.com/99designs/gqlgen/graphql" }}
{{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }}

{{ if eq .Config.Exec.Layout "single-file" }}
	// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.
	func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
		return &executableSchema{
		 	schema: cfg.Schema,
			resolvers: cfg.Resolvers,
			directives: cfg.Directives,
			complexity: cfg.Complexity,
		}
	}

	type Config struct {
		Schema    *ast.Schema
		Resolvers  ResolverRoot
		Directives DirectiveRoot
		Complexity ComplexityRoot
	}

	type ResolverRoot interface {
	{{- range $object := .Objects -}}
		{{ if $object.HasResolvers -}}
			{{ucFirst $object.Name}}() {{ucFirst $object.Name}}Resolver
		{{ end }}
	{{- end }}
	{{- range $object := .Inputs -}}
	{{ if $object.HasResolvers -}}
		{{ucFirst $object.Name}}() {{ucFirst $object.Name}}Resolver
	{{ end }}
{{- end }}
}

	type DirectiveRoot struct {
	{{ range $directive := .Directives }}
		{{- $directive.Declaration }}
	{{ end }}
	}

	type ComplexityRoot struct {
	{{- if not .Config.OmitComplexity }}
	{{ range $object := .Objects }}
		{{ if not $object.IsReserved -}}
			{{ ucFirst $object.Name }} struct {
			{{ range $_, $fields := $object.UniqueFields }}
				{{- $field := index $fields 0 -}}
				{{ if not $field.IsReserved -}}
					{{ $field.GoFieldName }} {{ $field.ComplexitySignature }}
				{{ end }}
			{{- end }}
			}
		{{- end }}
	{{ end }}
	{{- end }}
	}
{{ end }}

{{ range $object := .Objects -}}
	{{ if $object.HasResolvers }}
		type {{ucFirst $object.Name}}Resolver interface {
		{{ range $field := $object.Fields -}}
			{{- if $field.IsResolver }}
				{{- $field.GoFieldName}}{{ $field.ShortResolverDeclaration }}
			{{- end }}
		{{ end }}
		}
	{{- end }}
{{- end }}

{{ range $object := .Inputs -}}
	{{ if $object.HasResolvers }}
		type {{$object.Name}}Resolver interface {
		{{ range $field := $object.Fields -}}
			{{- if $field.IsResolver }}
				{{- $field.GoFieldName}}{{ $field.ShortResolverDeclaration }}
			{{- end }}
		{{ end }}
		}
	{{- end }}
{{- end }}

{{ if eq .Config.Exec.Layout "single-file" }}
	type executableSchema struct {
		schema    *ast.Schema
		resolvers  ResolverRoot
		directives DirectiveRoot
		complexity ComplexityRoot
	}

	func (e *executableSchema) Schema() *ast.Schema {
		if e.schema != nil {
        		return e.schema
		}
		return parsedSchema
	}

	func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) {
		ec := executionContext{nil, e, 0, 0, nil}
		_ = ec
		{{ if not .Config.OmitComplexity -}}
		switch typeName + "." + field {
		{{ range $object := .Objects }}
			{{ if not $object.IsReserved }}
				{{ range $_, $fields := $object.UniqueFields }}
					{{- $len := len $fields }}
					{{- range $i, $field := $fields }}
						{{- $last := eq (add $i 1) $len }}
						{{- if not $field.IsReserved }}
							{{- if eq $i 0 }}case {{ end }}"{{$object.Name}}.{{$field.Name}}"{{ if not $last }},{{ else }}:
								if e.complexity.{{ucFirst $object.Name}}.{{$field.GoFieldName}} == nil {
									break
								}
								{{ if $field.Args }}
									args, err := ec.{{ $field.ArgsFunc }}(context.TODO(),rawArgs)
									if err != nil {
										return 0, false
									}
								{{ end }}
								return e.complexity.{{ucFirst $object.Name}}.{{$field.GoFieldName}}(childComplexity{{if $field.Args}}, {{$field.ComplexityArgs}} {{ end }}), true
							{{ end }}
						{{- end }}
					{{- end }}
				{{ end }}
			{{ end }}
		{{ end }}
		}
		{{- end }}
		return 0, false
	}

	func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
		rc := graphql.GetOperationContext(ctx)
		ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)}
		inputUnmarshalMap := graphql.BuildUnmarshalerMap(
			{{- range $input := .Inputs -}}
				{{ if not $input.HasUnmarshal }}
					ec.unmarshalInput{{ $input.Name }},
				{{- end }}
			{{- end }}
		)
		first := true

		switch rc.Operation.Operation {
		{{- if .QueryRoot }} case ast.Query:
			return func(ctx context.Context) *graphql.Response {
				var response graphql.Response
				var data graphql.Marshaler
				if first {
					first = false
					ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
					{{ if .Directives.LocationDirectives "QUERY" -}}
						data = ec._queryMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
							return ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet), nil
						})
					{{- else -}}
						data = ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet)
					{{- end }}
				} else {
					if atomic.LoadInt32(&ec.pendingDeferred) > 0 {
						result := <-ec.deferredResults
						atomic.AddInt32(&ec.pendingDeferred, -1)
						data = result.Result
						response.Path = result.Path
						response.Label = result.Label
						response.Errors = result.Errors
					} else {
						return nil
					}
				}
				var buf bytes.Buffer
				data.MarshalGQL(&buf)
				response.Data = buf.Bytes()
				if atomic.LoadInt32(&ec.deferred) > 0 {
					hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0
					response.HasNext = &hasNext
				}

				return &response
			}
		{{ end }}

		{{- if .MutationRoot }} case ast.Mutation:
			return func(ctx context.Context) *graphql.Response {
				if !first { return nil }
				first = false
				ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
				{{ if .Directives.LocationDirectives "MUTATION" -}}
					data := ec._mutationMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
						return ec._{{.MutationRoot.Name}}(ctx, rc.Operation.SelectionSet), nil
					})
				{{- else -}}
					data := ec._{{.MutationRoot.Name}}(ctx, rc.Operation.SelectionSet)
				{{- end }}
				var buf bytes.Buffer
				data.MarshalGQL(&buf)

				return &graphql.Response{
					Data:       buf.Bytes(),
				}
			}
		{{ end }}

		{{- if .SubscriptionRoot }} case ast.Subscription:
			{{ if .Directives.LocationDirectives "SUBSCRIPTION" -}}
				next := ec._subscriptionMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
					return ec._{{.SubscriptionRoot.Name}}(ctx, rc.Operation.SelectionSet),nil
				})
			{{- else -}}
				next := ec._{{.SubscriptionRoot.Name}}(ctx, rc.Operation.SelectionSet)
			{{- end }}

			var buf bytes.Buffer
			return func(ctx context.Context) *graphql.Response {
				buf.Reset()
				data := next(ctx)

				if data == nil {
					return nil
				}
				data.MarshalGQL(&buf)

				return &graphql.Response{
					Data:       buf.Bytes(),
				}
			}
		{{ end }}
		default:
			return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation"))
		}
	}

	type executionContext struct {
		*graphql.OperationContext
		*executableSchema
		deferred        int32
		pendingDeferred int32
		deferredResults chan graphql.DeferredResult
	}

	func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) {
		atomic.AddInt32(&ec.pendingDeferred, 1)
		go func () {
			ctx := graphql.WithFreshResponseContext(dg.Context)
			dg.FieldSet.Dispatch(ctx)
			ds := graphql.DeferredResult{
				Path:   dg.Path,
				Label:  dg.Label,
				Result: dg.FieldSet,
				Errors: graphql.GetErrors(ctx),
			}
			// null fields should bubble up
			if dg.FieldSet.Invalids > 0 {
				ds.Result = graphql.Null
			}
			ec.deferredResults <- ds
		}()
	}

	func (ec *executionContext) introspectSchema() (*introspection.Schema, error) {
		if ec.DisableIntrospection {
			return nil, errors.New("introspection disabled")
		}
		return introspection.WrapSchema(ec.Schema()), nil
	}

	func (ec *executionContext) introspectType(name string) (*introspection.Type, error) {
		if ec.DisableIntrospection {
			return nil, errors.New("introspection disabled")
		}
		return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil
	}

	{{if .HasEmbeddableSources }}
	//go:embed{{- range $source := .AugmentedSources }}{{if $source.Embeddable}} {{$source.RelativePath|quote}}{{end}}{{- end }}
	var sourcesFS embed.FS

	func sourceData(filename string) string {
		data, err := sourcesFS.ReadFile(filename)
		if err != nil {
			panic(fmt.Sprintf("codegen problem: %s not available", filename))
		}
		return string(data)
	}
	{{- end }}

	var sources = []*ast.Source{
	{{- range $source := .AugmentedSources }}
		{Name: {{$source.RelativePath|quote}}, Input: {{if (not $source.Embeddable)}}{{$source.Source|rawQuote}}{{else}}sourceData({{$source.RelativePath|quote}}){{end}}, BuiltIn: {{$source.BuiltIn}}},
	{{- end }}
	}
	var parsedSchema = gqlparser.MustLoadSchema(sources...)
{{ end }}
