package kubernetes

import (
	"context"
	"strings"

	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	metaTypes "k8s.io/apimachinery/pkg/types"

	libk8s "github.com/ckotzbauer/libk8soci/pkg/kubernetes"
)

type owner struct {
	kind string
	name string
}

type KubeClient struct {
	Client *libk8s.KubeClient
	pods   []corev1.Pod
	owners map[string]owner
}

type ContainerInfo struct {
	Namespace     string
	OwnerName     string
	OwnerKind     string
	PodName       string
	PodUID        metaTypes.UID
	ContainerName string
}

func NewClient() *KubeClient {
	return &KubeClient{Client: libk8s.NewClient(), owners: make(map[string]owner)}
}

func (client *KubeClient) loadPods() error {
	list, err := client.Client.Client.CoreV1().Pods("").List(context.Background(), meta.ListOptions{})

	if err != nil {
		logrus.WithError(err).Error("ListPods errored!")
		return err
	}

	client.pods = list.Items
	return nil
}

func (client *KubeClient) GetContainersWithImage(imageID string) ([]ContainerInfo, error) {
	if client.pods == nil {
		err := client.loadPods()

		if err != nil {
			return nil, err
		}
	}

	infos := make([]ContainerInfo, 0)

	for _, p := range client.pods {
		ref := p.Namespace + "/" + p.Name
		owner, ok := client.owners[ref]
		if !ok {
			owner = client.getOwner(p)
		}
		client.owners[ref] = owner

		statuses := p.Status.ContainerStatuses
		statuses = append(statuses, p.Status.InitContainerStatuses...)
		statuses = append(statuses, p.Status.EphemeralContainerStatuses...)

		for _, c := range statuses {
			fixedImageID := strings.ReplaceAll(c.ImageID, "docker-pullable://", "")
			if fixedImageID == imageID {
				infos = append(infos, ContainerInfo{
					Namespace:     p.Namespace,
					PodName:       p.Name,
					PodUID:        p.UID,
					OwnerName:     owner.name,
					OwnerKind:     owner.kind,
					ContainerName: c.Name,
				})
			}
		}
	}

	return infos, nil
}

func (client *KubeClient) getOwner(pod corev1.Pod) owner {
	ownerKind, ownerName := getOwnerKindAndName(pod.OwnerReferences)
	ctx := context.Background()

	if ownerKind == "ReplicaSet" {
		rs, _ := client.Client.Client.AppsV1().ReplicaSets(pod.Namespace).Get(ctx, ownerName, meta.GetOptions{})
		ownerKind, ownerName = getOwnerKindAndName(rs.OwnerReferences)
		return owner{kind: ownerKind, name: ownerName}
	} else if ownerKind == "Job" {
		job, _ := client.Client.Client.BatchV1().Jobs(pod.Namespace).Get(ctx, ownerName, meta.GetOptions{})
		ownerKind, ownerName = getOwnerKindAndName(job.OwnerReferences)
		if ownerKind == "" && ownerName == "" {
			return owner{kind: "Job", name: job.Name}
		} else {
			return owner{kind: ownerKind, name: ownerName}
		}
	} else if ownerKind != "" {
		// A StatefulSet, DaemonSet or CRD-Operator created the pod
		return owner{kind: ownerKind, name: ownerName}
	} else {
		// Single pod without owner
		return owner{kind: "Pod", name: pod.Name}
	}
}

func getOwnerKindAndName(refs []meta.OwnerReference) (string, string) {
	if len(refs) > 0 {
		return refs[0].Kind, refs[0].Name
	}

	return "", ""
}
