func nodeToTableRow(node *graph.Node, rset graph.RelationshipSet, namePrefix string, showGroupFn func(kind string) bool) metav1.TableRow {
	var name, ready, status, age string
	var relationships interface{}
	switch {
	case len(node.Kind) == 0:
		name = node.Name
	case len(node.Group) > 0 && showGroupFn(node.Kind):
		name = fmt.Sprintf("%s%s.%s/%s", namePrefix, node.Kind, node.Group, node.Name)
	default:
		name = fmt.Sprintf("%s%s/%s", namePrefix, node.Kind, node.Name)
	}
	switch {
	case node.Group == corev1.GroupName && node.Kind == "Event":
		ready, status, _ = getEventCoreReadyStatus(node.Unstructured)
	case node.Group == corev1.GroupName && node.Kind == "Pod":
		ready, status, _ = getPodReadyStatus(node.Unstructured)
	case node.Group == corev1.GroupName && node.Kind == "ReplicationController":
		ready, status, _ = getReplicationControllerReadyStatus(node.Unstructured)
	case node.Group == appsv1.GroupName && node.Kind == "DaemonSet":
		ready, status, _ = getDaemonSetReadyStatus(node.Unstructured)
	case node.Group == appsv1.GroupName && node.Kind == "Deployment":
		ready, status, _ = getDeploymentReadyStatus(node.Unstructured)
	case node.Group == appsv1.GroupName && node.Kind == "ReplicaSet":
		ready, status, _ = getReplicaSetReadyStatus(node.Unstructured)
	case node.Group == appsv1.GroupName && node.Kind == "StatefulSet":
		ready, status, _ = getStatefulSetReadyStatus(node.Unstructured)
	case node.Group == policyv1.GroupName && node.Kind == "PodDisruptionBudget":
		ready, status, _ = getPodDisruptionBudgetReadyStatus(node.Unstructured)
	case node.Group == apiregistrationv1.GroupName && node.Kind == "APIService":
		ready, status, _ = getAPIServiceReadyStatus(node.Unstructured)
	case node.Group == eventsv1.GroupName && node.Kind == "Event":
		ready, status, _ = getEventReadyStatus(node.Unstructured)
	case node.Group == storagev1.GroupName && node.Kind == "VolumeAttachment":
		ready, status, _ = getVolumeAttachmentReadyStatus(node.Unstructured)
	case node.Unstructured != nil:
		ready, status, _ = getObjectReadyStatus(node.Unstructured)
	}
	if len(ready) == 0 {
		ready = cellNotApplicable
	}
	if node.Unstructured != nil {
		age = translateTimestampSince(node.GetCreationTimestamp())
	}
	relationships = []string{}
	if rset != nil {
		relationships = rset.List()
	}
	return metav1.TableRow{
		Object: runtime.RawExtension{Object: node.DeepCopyObject()},
		Cells: []interface{}{
			name,
			ready,
			status,
			age,
			relationships,
		},
	}
}
nodeToTableRow converts the provided node into a table row.
nolint:funlen,gocognit,goconst
package printers
import (
	"fmt"
	"sort"
	"strings"
	"time"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	eventsv1 "k8s.io/api/events/v1"
	policyv1 "k8s.io/api/policy/v1"
	storagev1 "k8s.io/api/storage/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	unstructuredv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/duration"
	"k8s.io/client-go/util/jsonpath"
	apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
	"github.com/tohjustin/kube-lineage/internal/graph"
)
const (
	cellUnknown       = "<unknown>"
	cellNotApplicable = "-"
)
var (
	objectColumnDefinitions = []metav1.TableColumnDefinition{
		{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
		{Name: "Ready", Type: "string", Description: "The readiness state of this object."},
		{Name: "Status", Type: "string", Description: "The status of this object."},
		{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
		{Name: "Relationships", Type: "array", Description: "The relationships this object has with its parent.", Priority: -1},
	}
	objectReadyReasonJSONPath = newJSONPath("reason", "{.status.conditions[?(@.type==\"Ready\")].reason}")
	objectReadyStatusJSONPath = newJSONPath("status", "{.status.conditions[?(@.type==\"Ready\")].status}")
)
func createShowGroupFn(nodeMap graph.NodeMap, showGroup bool, maxDepth uint) func(string) bool {
	if showGroup {
		return func(_ string) bool {
			return true
		}
	}
	kindToGroupSetMap := map[string](map[string]struct{}){}
	for _, node := range nodeMap {
		if maxDepth != 0 && node.Depth > maxDepth {
			continue
		}
		if _, ok := kindToGroupSetMap[node.Kind]; !ok {
			kindToGroupSetMap[node.Kind] = map[string]struct{}{}
		}
		kindToGroupSetMap[node.Kind][node.Group] = struct{}{}
	}
	return func(kind string) bool {
		return len(kindToGroupSetMap[kind]) > 1
	}
}
func createShowNamespaceFn(nodeMap graph.NodeMap, showNamespace bool, maxDepth uint) func(schema.GroupKind) bool {
	showNS := showNamespace || shouldShowNamespace(nodeMap, maxDepth)
	if !showNS {
		return func(_ schema.GroupKind) bool {
			return false
		}
	}
	clusterScopeGKSet := map[schema.GroupKind]struct{}{}
	for _, node := range nodeMap {
		if maxDepth != 0 && node.Depth > maxDepth {
			continue
		}
		gk := node.GroupVersionKind().GroupKind()
		if !node.Namespaced {
			clusterScopeGKSet[gk] = struct{}{}
		}
	}
	return func(gk schema.GroupKind) bool {
		_, isClusterScopeGK := clusterScopeGKSet[gk]
		return !isClusterScopeGK
	}
}
func shouldShowNamespace(nodeMap graph.NodeMap, maxDepth uint) bool {
	nsSet := map[string]struct{}{}
	for _, node := range nodeMap {
		if maxDepth != 0 && node.Depth > maxDepth {
			continue
		}
		ns := node.Namespace
		if _, ok := nsSet[ns]; !ok {
			nsSet[ns] = struct{}{}
		}
	}
	return len(nsSet) > 1
}
func newJSONPath(name, jsonPath string) *jsonpath.JSONPath {
	jp := jsonpath.New(name).AllowMissingKeys(true)
	if err := jp.Parse(jsonPath); err != nil {
		panic(err)
	}
	return jp
}
func getNestedString(data map[string]interface{}, jp *jsonpath.JSONPath) (string, error) {
	values, err := jp.FindResults(data)
	if err != nil {
		return "", err
	}
	strValues := []string{}
	for arrIx := range values {
		for valIx := range values[arrIx] {
			strValues = append(strValues, fmt.Sprintf("%v", values[arrIx][valIx].Interface()))
		}
	}
	str := strings.Join(strValues, ",")
	return str, nil
}
func getObjectReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	data := u.UnstructuredContent()
	ready, err := getNestedString(data, objectReadyStatusJSONPath)
	if err != nil {
		return "", "", err
	}
	status, err := getNestedString(data, objectReadyReasonJSONPath)
	if err != nil {
		return ready, "", err
	}
	return ready, status, nil
}
func getAPIServiceReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var apisvc apiregistrationv1.APIService
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &apisvc)
	if err != nil {
		return "", "", err
	}
	var ready, status string
	for _, condition := range apisvc.Status.Conditions {
		if condition.Type == apiregistrationv1.Available {
			ready = string(condition.Status)
			if condition.Status != apiregistrationv1.ConditionTrue {
				status = condition.Reason
			}
		}
	}
	return ready, status, nil
}
func getDaemonSetReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var ds appsv1.DaemonSet
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &ds)
	if err != nil {
		return "", "", err
	}
	desiredReplicas := ds.Status.DesiredNumberScheduled
	readyReplicas := ds.Status.NumberReady
	ready := fmt.Sprintf("%d/%d", readyReplicas, desiredReplicas)
	return ready, "", nil
}
func getDeploymentReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var deploy appsv1.Deployment
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &deploy)
	if err != nil {
		return "", "", err
	}
	desiredReplicas := deploy.Status.Replicas
	readyReplicas := deploy.Status.ReadyReplicas
	ready := fmt.Sprintf("%d/%d", readyReplicas, desiredReplicas)
	return ready, "", nil
}
func getEventCoreReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var status string
	var ev corev1.Event
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &ev)
	if err != nil {
		return "", "", err
	}
	if ev.Count > 1 {
		status = fmt.Sprintf("%s: %s (x%d)", ev.Reason, ev.Message, ev.Count)
	} else {
		status = fmt.Sprintf("%s: %s", ev.Reason, ev.Message)
	}
	return "", status, nil
}
func getEventReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var status string
	var ev eventsv1.Event
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &ev)
	if err != nil {
		return "", "", err
	}
	if ev.DeprecatedCount > 1 {
		status = fmt.Sprintf("%s: %s (x%d)", ev.Reason, ev.Note, ev.DeprecatedCount)
	} else {
		status = fmt.Sprintf("%s: %s", ev.Reason, ev.Note)
	}
	return "", status, nil
}
func getPodReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var pod corev1.Pod
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &pod)
	if err != nil {
		return "", "", err
	}
	totalContainers := len(pod.Spec.Containers)
	readyContainers := 0
	reason := string(pod.Status.Phase)
	if len(pod.Status.Reason) > 0 {
		reason = pod.Status.Reason
	}
	initializing := false
	for i := range pod.Status.InitContainerStatuses {
		container := pod.Status.InitContainerStatuses[i]
		state := container.State
		switch {
		case state.Terminated != nil && state.Terminated.ExitCode == 0:
			continue
		case state.Terminated != nil && len(state.Terminated.Reason) > 0:
			reason = state.Terminated.Reason
		case state.Terminated != nil && len(state.Terminated.Reason) == 0 && state.Terminated.Signal != 0:
			reason = fmt.Sprintf("Signal:%d", state.Terminated.Signal)
		case state.Terminated != nil && len(state.Terminated.Reason) == 0 && state.Terminated.Signal == 0:
			reason = fmt.Sprintf("ExitCode:%d", state.Terminated.ExitCode)
		case state.Waiting != nil && len(state.Waiting.Reason) > 0 && state.Waiting.Reason != "PodInitializing":
			reason = state.Waiting.Reason
		default:
			reason = fmt.Sprintf("%d/%d", i, len(pod.Spec.InitContainers))
		}
		reason = fmt.Sprintf("Init:%s", reason)
		initializing = true
		break
	}
	if !initializing {
		hasRunning := false
		for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- {
			container := pod.Status.ContainerStatuses[i]
			state := container.State
			switch {
			case state.Terminated != nil && len(state.Terminated.Reason) > 0:
				reason = state.Terminated.Reason
			case state.Terminated != nil && len(state.Terminated.Reason) == 0 && state.Terminated.Signal != 0:
				reason = fmt.Sprintf("Signal:%d", state.Terminated.Signal)
			case state.Terminated != nil && len(state.Terminated.Reason) == 0 && state.Terminated.Signal == 0:
				reason = fmt.Sprintf("ExitCode:%d", state.Terminated.ExitCode)
			case state.Waiting != nil && len(state.Waiting.Reason) > 0:
				reason = state.Waiting.Reason
			case state.Running != nil && container.Ready:
				hasRunning = true
				readyContainers++
			}
		}
		if reason == "Completed" && hasRunning {
			reason = "NotReady"
			for _, condition := range pod.Status.Conditions {
				if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue {
					reason = "Running"
				}
			}
		}
	}
	if pod.DeletionTimestamp != nil {
		if pod.Status.Reason == "NodeLost" {
			reason = "Unknown"
		} else {
			reason = "Terminating"
		}
	}
	ready := fmt.Sprintf("%d/%d", readyContainers, totalContainers)
	return ready, reason, nil
}
func getPodDisruptionBudgetReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var pdb policyv1.PodDisruptionBudget
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &pdb)
	if err != nil {
		return "", "", err
	}
	var status string
	for _, condition := range pdb.Status.Conditions {
		if condition.ObservedGeneration == pdb.Generation {
			if condition.Type == policyv1.DisruptionAllowedCondition {
				status = condition.Reason
			}
		}
	}
	return "", status, nil
}
func getReplicaSetReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var rs appsv1.ReplicaSet
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &rs)
	if err != nil {
		return "", "", err
	}
	desiredReplicas := rs.Status.Replicas
	readyReplicas := rs.Status.ReadyReplicas
	ready := fmt.Sprintf("%d/%d", readyReplicas, desiredReplicas)
	return ready, "", nil
}
func getReplicationControllerReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var rc corev1.ReplicationController
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &rc)
	if err != nil {
		return "", "", err
	}
	desiredReplicas := rc.Status.Replicas
	readyReplicas := rc.Status.ReadyReplicas
	ready := fmt.Sprintf("%d/%d", readyReplicas, desiredReplicas)
	return ready, "", nil
}
func getStatefulSetReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var sts appsv1.StatefulSet
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &sts)
	if err != nil {
		return "", "", err
	}
	desiredReplicas := sts.Status.Replicas
	readyReplicas := sts.Status.ReadyReplicas
	ready := fmt.Sprintf("%d/%d", readyReplicas, desiredReplicas)
	return ready, "", nil
}
func getVolumeAttachmentReadyStatus(u *unstructuredv1.Unstructured) (string, string, error) {
	var va storagev1.VolumeAttachment
	err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &va)
	if err != nil {
		return "", "", err
	}
	var ready, status string
	if va.Status.Attached {
		ready = "True"
	} else {
		ready = "False"
	}
	var errTime time.Time
	if err := va.Status.AttachError; err != nil {
		status = err.Message
		errTime = err.Time.Time
	}
	if err := va.Status.DetachError; err != nil {
		if err.Time.After(errTime) {
			status = err.Message
		}
	}
	return ready, status, nil
}