package convert

import (
	"reflect"
	"strings"

	"github.com/aquasecurity/trivy/pkg/iac/types"
)

type metadataProvider interface {
	GetMetadata() types.Metadata
}

var metadataInterface = reflect.TypeFor[metadataProvider]()

func StructToRego(inputValue reflect.Value) map[string]any {

	// make sure we have a struct literal
	for inputValue.Type().Kind() == reflect.Pointer || inputValue.Type().Kind() == reflect.Interface {
		if inputValue.IsNil() {
			return nil
		}
		inputValue = inputValue.Elem()
	}
	if inputValue.Type().Kind() != reflect.Struct {
		panic("not a struct")
	}

	output := make(map[string]any, inputValue.NumField())

	for i := 0; i < inputValue.NumField(); i++ {
		field := inputValue.Field(i)
		typ := inputValue.Type().Field(i)
		name := typ.Name

		if !typ.IsExported() || field.Interface() == nil {
			continue
		}

		if _, ok := field.Interface().(types.Metadata); ok && name == "Metadata" {
			continue
		}

		val := anonymousToRego(reflect.ValueOf(field.Interface()))

		if val == nil {
			continue
		}

		output[strings.ToLower(name)] = val
	}

	if inputValue.Type().Implements(metadataInterface) {
		returns := inputValue.MethodByName("GetMetadata").Call(nil)
		if metadata, ok := returns[0].Interface().(types.Metadata); ok {
			output["__defsec_metadata"] = metadata.ToRego()
		}
	} else {
		metaVal := inputValue.FieldByName("Metadata")
		if metaVal.Kind() == reflect.Struct {
			if meta, ok := metaVal.Interface().(types.Metadata); ok {
				output["__defsec_metadata"] = meta.ToRego()
			}
		}

	}

	return output
}
