//nolint:dupl
package jennies

import (
	"bytes"
	"fmt"
	"go/format"
	"path/filepath"

	"github.com/grafana/codejen"

	"github.com/grafana/grafana-app-sdk/codegen"
	"github.com/grafana/grafana-app-sdk/codegen/templates"
	"github.com/grafana/grafana-app-sdk/resource"
)

type CodecGenerator struct {
	// This flag exists for compatibility with thema codegen, which only generates code for the current/latest version of the kind
	OnlyUseCurrentVersion bool

	// GroupByKind determines whether kinds are grouped by GroupVersionKind or just GroupVersion.
	// If GroupByKind is true, generated paths are <kind>/<version>/<file>, instead of the default <version>/<file>.
	// When GroupByKind is false, the generated Codec type names are prefixed with the kind name,
	// i.e. FooJSONCodec for kind.Name()="Foo"
	GroupByKind bool
}

func (*CodecGenerator) JennyName() string {
	return "CodecGenerator"
}

// Generate creates one or more codec go files for the provided Kind
// nolint:dupl
func (c *CodecGenerator) Generate(appManifest codegen.AppManifest) (codejen.Files, error) {
	files := make(codejen.Files, 0, 1)
	for version, kind := range codegen.VersionedKinds(appManifest) {
		if c.OnlyUseCurrentVersion && appManifest.Properties().PreferredVersion != version.Name() {
			continue
		}

		if kind.Scope != string(resource.NamespacedScope) && kind.Scope != string(resource.ClusterScope) {
			return nil, fmt.Errorf("scope '%s' is invalid, must be one of: '%s', '%s'",
				kind.Scope, resource.ClusterScope, resource.NamespacedScope)
		}

		prefix := ""
		if !c.GroupByKind {
			prefix = exportField(kind.Kind)
		}

		b := bytes.Buffer{}
		err := templates.WriteCodec(templates.SchemaMetadata{
			Package:    ToPackageName(version.Name()),
			Group:      appManifest.Properties().Group,
			Version:    version.Name(),
			Kind:       kind.Kind,
			Plural:     kind.PluralMachineName,
			Scope:      kind.Scope,
			FuncPrefix: prefix,
		}, &b)
		if err != nil {
			return nil, err
		}
		formatted, err := format.Source(b.Bytes())
		if err != nil {
			return nil, err
		}
		files = append(files, codejen.File{
			Data:         formatted,
			RelativePath: filepath.Join(GetGeneratedGoTypePath(c.GroupByKind, appManifest.Properties().Group, version.Name(), kind.MachineName), fmt.Sprintf("%s_codec_gen.go", kind.MachineName)),
			From:         []codejen.NamedJenny{c},
		})
	}
	return files, nil
}
