// https://github.com/99designs/gqlgen/issues/2281#issuecomment-1506561381
package main

import (
	"fmt"
	"os"

	"github.com/99designs/gqlgen/api"
	"github.com/99designs/gqlgen/codegen"
	"github.com/99designs/gqlgen/codegen/config"
)

func main() {
	cfg, err := config.LoadConfigFromDefaultLocations()
	if err != nil {
		fmt.Fprintln(os.Stderr, "failed to load config", err.Error())
		os.Exit(2)
	}

	err = api.Generate(cfg,
		api.PrependPlugin(&resolverDirective{}),
		api.AddPlugin(&fieldDirectiveFix{}),
	)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(3)
	}
}

type fieldDirectiveFix struct {
}

func (fieldDirectiveFix) Name() string {
	return "Fix Directive hook called with wrong object"
}

func (fieldDirectiveFix) GenerateCode(cfg *codegen.Data) error {
	for _, input := range cfg.Inputs {
		for _, field := range input.Fields {
			if field.GoFieldType == codegen.GoFieldVariable {
				directiveMap := make(map[string]int, len(field.TypeReference.Definition.Directives)+len(field.Object.Directives))
				for _, v := range field.TypeReference.Definition.Directives {
					directiveMap[v.Name]++
				}

				directive := make([]*codegen.Directive, 0, len(field.Directives))
				for _, v := range field.Directives {
					if count := directiveMap[v.Name]; count > 0 {
						directiveMap[v.Name] = count - 1
						fmt.Printf("Ignore field %s{%s} directive: @%s\n", input.Name, field.Name, v.Name)
						continue
					}

					directive = append(directive, v)
				}

				field.Directives = directive
			}
		}
	}

	return nil
}

type resolverDirective struct {
}

func (resolverDirective) Name() string {
	return "Resolver directive support"
}

func (resolverDirective) GenerateCode(cfg *codegen.Data) error {
	const directiveName = "resolver"

	for _, obj := range cfg.Objects {
		for _, field := range obj.Fields {
			if field.FieldDefinition.Directives.ForName(directiveName) != nil {
				fmt.Printf("Add resolver for field %s{%s}\n", obj.Name, field.Name)
				field.IsResolver = true

				// TODO
				// field.FieldDefinition.Directives = removeDirective(field.FieldDefinition.Directives, directiveName)
			}
		}
	}

	return nil
}

// func removeDirective(directives ast.DirectiveList, name string) ast.DirectiveList {
// 	return slices.DeleteFunc(directives, func(directive *ast.Directive) bool {
// 		return directive.Name == name
// 	})
// }