// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package generate

import (
	"google.golang.org/protobuf/compiler/protogen"
	"google.golang.org/protobuf/types/descriptorpb"
)

func Generate(gen *protogen.Plugin) error {
	for _, file := range gen.Files {
		if !file.Generate {
			continue
		}
		if len(file.Messages) == 0 {
			continue
		}
		filename := file.GeneratedFilenamePrefix + "_deepcopy.gen.go"
		p := gen.NewGeneratedFile(filename, file.GoImportPath)

		protoIdent := protogen.GoIdent{
			GoName:       "Clone",
			GoImportPath: "google.golang.org/protobuf/proto",
		}
		p.P("// Code generated by protoc-gen-deepcopy. DO NOT EDIT.")
		p.P("package ", file.GoPackageName)
		var process func([]*protogen.Message)

		process = func(messages []*protogen.Message) {
			for _, message := range messages {
				// skip maps in protos.
				if message.Desc.Options().(*descriptorpb.MessageOptions).GetMapEntry() {
					continue
				}
				typeName := message.GoIdent.GoName
				// Generate DeepCopyInto() method for this type
				p.P(`// DeepCopyInto supports using `, typeName, ` within kubernetes types, where deepcopy-gen is used.`)
				p.P(`func (in *`, typeName, `) DeepCopyInto(out *`, typeName, `) {`)
				p.P(`proto.Reset(out)`)
				p.P(`proto.Merge(out,`, protoIdent, `(in))`)
				p.P(`}`)

				// Generate DeepCopy() method for this type
				p.P(`// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new `, typeName, `. Required by controller-gen.`)
				p.P(`func (in *`, typeName, `) DeepCopy() *`, typeName, ` {`)
				p.P(`if in == nil { return nil }`)
				p.P(`out := new(`, typeName, `)`)
				p.P(`in.DeepCopyInto(out)`)
				p.P(`return out`)
				p.P(`}`)

				// Generate DeepCopyInterface() method for this type
				p.P(`// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new `, typeName, `. Required by controller-gen.`)
				p.P(`func (in *`, typeName, `) DeepCopyInterface() interface{} {`)
				p.P(`return in.DeepCopy()`)
				p.P(`}`)
				process(message.Messages)
			}
		}
		process(file.Messages)
	}
	return nil
}
