package watch_graph //nolint:staticcheck

import (
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

func (vx *wgObject2vertex) collectArcs(obj *unstructured.Unstructured) *Error {
	err := vx.collectOwnerReferencesArcs(obj)
	if err != nil {
		return err
	}
	return vx.collectKindSpecificArcs(obj)
}

func (vx *wgObject2vertex) collectKindSpecificArcs(obj *unstructured.Unstructured) *Error {
	switch obj.GroupVersionKind() {

	// Core
	case podGVK:
		return vx.collectPodV1Arcs(obj)
	case replicationControllerGVK:
		return vx.collectReplicationControllerV1Arcs(obj)
	case serviceAccountGVK:
		return vx.collectServiceAccountV1Arcs(obj)
	case persistentVolumeClaimGVK:
		return vx.collectPersistentVolumeClaimV1Arcs(obj)
	case persistentVolumeGVK:
		return vx.collectPersistentVolumeV1Arcs(obj)

	// Events
	case eventGVK:
		return vx.collectEventV1Arcs(obj)

	// Apps
	case deploymentGVK:
		return vx.collectDeploymentV1Arcs(obj)
	case replicaSetGVK:
		return vx.collectReplicaSetV1Arcs(obj)
	case daemonSetGVK:
		return vx.collectDaemonSetV1Arcs(obj)
	case statefulSetGVK:
		return vx.collectStatefulSetV1Arcs(obj)

	// RBAC
	case clusterRoleBindingGVK:
		return vx.collectClusterRoleBindingV1Arcs(obj)
	case roleBindingGVK:
		return vx.collectRoleBindingV1Arcs(obj)

	// Batch
	case jobGVK:
		return vx.collectJobV1Arcs(obj)
	case cronJobGVK:
		return vx.collectCronJobV1Arcs(obj)

	// Autoscaling
	case hpaV1GVK:
		return vx.collectHorizontalPodAutoscalerV1Arcs(obj)
	case hpaV2GVK:
		return vx.collectHorizontalPodAutoscalerV2Arcs(obj)

	// Networking
	case ingressGVK:
		return vx.collectIngressV1Arcs(obj)

	// Gateway API (networking sig)
	case gatewayGVK:
		return vx.collectGatewayV1Arcs(obj)
	case httpRouteGVK:
		return vx.collectGatewayHTTPRouteV1Arcs(obj)
	case grpcRouteGVK:
		return vx.collectGatewayGRPCRouteV1Arcs(obj)

	// Storage
	case csiStorageCapacityGVK:
		return vx.collectCSIStorageCapacityV1Arcs(obj)
	case volumeAttachmentGVK:
		return vx.collectVolumeAttachmentV1Arcs(obj)

	// FluxCD
	case fluxcdSourceControllerGitRepositoryV1GVK:
		return vx.collectFluxCDSourceControllerGitRepositoryV1Arcs(obj)
	case fluxcdSourceControllerOCIRepositoryV1GVK:
		return vx.collectFluxCDSourceControllerOCIRepositoryV1Arcs(obj)
	case fluxcdSourceControllerHelmRepositoryV1GVK:
		return vx.collectFluxCDSourceControllerHelmRepositoryV1Arcs(obj)
	case fluxcdSourceControllerHelmChartV1GVK:
		return vx.collectFluxCDSourceControllerHelmChartV1Arcs(obj)
	case fluxcdSourceControllerBucketV1GVK:
		return vx.collectFluxCDSourceControllerBucketV1Arcs(obj)

	case fluxcdKustomizeControllerKustomizationV1GVK:
		return vx.collectFluxCDKustomizeControllerKustomizationV1Arcs(obj)
	}
	return nil
}

func (vx *wgObject2vertex) setArcToConfigMap(name string, arcAttrs ArcAttrs, arcType ArcType) { //nolint:unparam
	vx.setArcToGVK(configMapGVK, name, arcAttrs, arcType)
}

func (vx *wgObject2vertex) setArcToSecret(name string, arcAttrs ArcAttrs, arcType ArcType) { //nolint:unparam
	vx.setArcToGVK(secretGVK, name, arcAttrs, arcType)
}

func (vx *wgObject2vertex) setArcToGVK(gvk schema.GroupVersionKind, name string, arcAttrs ArcAttrs, arcType ArcType) {
	vx.setArcTo(gvk.Group, gvk.Kind, name, "", arcAttrs, arcType)
}

func (vx *wgObject2vertex) setArcTo(group, kind, name, namespaceOverride string, arcAttrs ArcAttrs, arcType ArcType) {
	arc2id := ArcToID[vertexGKID, ArcType]{
		To: vertexGKID{
			GK:                schema.GroupKind{Group: group, Kind: kind},
			NamespaceOverride: namespaceOverride,
			Name:              name,
		},
		ArcType: arcType,
	}
	vx.setArc(arc2id, arcAttrs)
}

func (vx *wgObject2vertex) setArc(arc2id ArcToID[vertexGKID, ArcType], arcAttrs ArcAttrs) {
	if vx.arcSet == nil { // allocate the map lazily, only when needed.
		vx.arcSet = make(ArcSetWithData[vertexGKID, ArcType, ArcAttrs])
	}
	vx.arcSet[arc2id] = arcAttrs
}

func collectEach[T any](vals []T, arcType ArcType, f func(*T, ArcType)) {
	for i := range vals {
		f(&vals[i], arcType) // avoid copying, just pass a pointer
	}
}

func collectEachErr[T any](vals []T, arcType ArcType, f func(*T, ArcType) *Error) *Error {
	for i := range vals {
		err := f(&vals[i], arcType) // avoid copying, just pass a pointer
		if err != nil {
			return err
		}
	}
	return nil
}

func unstructuredToTyped[T any](obj *unstructured.Unstructured) (*T, *Error) {
	var o T
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &o)
	if err != nil {
		return nil, &Error{
			Message: err.Error(),
			Code:    InvalidArgument,
		}
	}
	return &o, nil
}
