package watch_graph //nolint:staticcheck

import (
	"errors"
	"fmt"
	"strings"

	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
	fluxkustomizev1 "github.com/fluxcd/pkg/apis/kustomize"
	fluxmetav1 "github.com/fluxcd/pkg/apis/meta"
	sourcev1 "github.com/fluxcd/source-controller/api/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

func (vx *wgObject2vertex) collectFluxCDKustomizeControllerKustomizationV1Arcs(obj *unstructured.Unstructured) *Error {
	o, err := unstructuredToTyped[kustomizev1.Kustomization](obj)
	if err != nil {
		return err
	}

	collectEach(o.Spec.DependsOn, ReferenceArcType, func(t *fluxmetav1.NamespacedObjectReference, arcType ArcType) {
		vx.setArcTo(
			fluxcdKustomizeControllerKustomizationV1GVK.Group,
			fluxcdKustomizeControllerKustomizationV1GVK.Kind,
			t.Name,
			t.Namespace,
			ArcAttrs{},
			arcType,
		)
	})

	if o.Spec.Decryption != nil && o.Spec.Decryption.SecretRef != nil {
		vx.setArcToSecret(o.Spec.Decryption.SecretRef.Name, ArcAttrs{}, ReferenceArcType)
	}

	if o.Spec.KubeConfig != nil {
		if o.Spec.KubeConfig.ConfigMapRef != nil {
			vx.setArcToConfigMap(o.Spec.KubeConfig.ConfigMapRef.Name, ArcAttrs{}, ReferenceArcType)
		}
		if o.Spec.KubeConfig.SecretRef != nil {
			vx.setArcToSecret(o.Spec.KubeConfig.SecretRef.Name, ArcAttrs{}, ReferenceArcType)
		}
	}

	if o.Spec.PostBuild != nil {
		collectEach(o.Spec.PostBuild.SubstituteFrom, ReferenceArcType, func(t *kustomizev1.SubstituteReference, arcType ArcType) {
			switch t.Kind {
			case "Secret":
				vx.setArcToSecret(t.Name, ArcAttrs{}, arcType)
			case "ConfigMap":
				vx.setArcToConfigMap(t.Name, ArcAttrs{}, arcType)
			default:
				vx.appendObjectProcessingWarning(fmt.Sprintf("post build substitution reference in Kustomization has unhandled kind %q", t.Kind))
			}
		})
	}

	collectEach(o.Spec.HealthChecks, ReferenceArcType, func(t *fluxmetav1.NamespacedObjectKindReference, arcType ArcType) {
		gv, err := schema.ParseGroupVersion(t.APIVersion)
		if err != nil {
			vx.appendObjectProcessingWarning(err.Error())
			return
		}

		vx.setArcTo(gv.Group, t.Kind, t.Name, t.Namespace, ArcAttrs{}, arcType)
	})

	collectEach(o.Spec.Patches, ReferenceArcType, func(t *fluxkustomizev1.Patch, arcType ArcType) {
		if t.Target != nil {
			vx.setArcTo(t.Target.Group, t.Target.Kind, t.Target.Name, t.Target.Namespace, ArcAttrs{}, arcType)
		}
	})

	if o.Status.Inventory != nil {
		collectEach(o.Status.Inventory.Entries, ReferenceArcType, func(t *kustomizev1.ResourceRef, arcType ArcType) {
			namespace, name, group, kind, err := parseResourceRefID(t.ID)
			if err != nil {
				vx.appendObjectProcessingWarning(err.Error())
				return
			}

			vx.setArcTo(group, kind, name, namespace, ArcAttrs{}, arcType)
		})
	}

	group := sourcev1.GroupVersion.Group
	if v := o.Spec.SourceRef.APIVersion; v != "" {
		gv, err := schema.ParseGroupVersion(v)
		if err != nil {
			vx.appendObjectProcessingWarning(fmt.Sprintf("unable to parse FluxCD Kustomization sourceRef.apiVersion %q, using default %q as group instead: %v", o.Spec.SourceRef.APIVersion, group, err))
			return nil
		}
		group = gv.Group
	}
	vx.setArcTo(group, o.Spec.SourceRef.Kind, o.Spec.SourceRef.Name, o.Spec.SourceRef.Namespace, ArcAttrs{}, ReferenceArcType)

	return nil
}

// parseResourceRefID parses the FluxCD kustomize resource reference id
//
// From the docs of kustomizev1.ResourceRef:
// ID is the string representation of the Kubernetes resource object's metadata,
// in the format '<namespace>_<name>_<group>_<kind>'.
func parseResourceRefID(id string) (string, string, string, string, error) {
	parts := strings.SplitN(id, "_", 4)
	if len(parts) != 4 {
		return "", "", "", "", errors.New("resource ref id has invalid format, expecting '<namespace>_<name>_<group>_<kind>")
	}
	return parts[0], parts[1], parts[2], parts[3], nil
}
