package k8s

import (
	"fmt"
	"time"

	"k8s.io/api/core/v1"
	"k8s.io/api/extensions/v1beta1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
)

const replicaSetResource = "replicasets"

type ReplicaSetStore struct {
	store     *cache.Store
	reflector *cache.Reflector
	stopCh    chan struct{}
}

func NewReplicaSetStore(clientset *kubernetes.Clientset) (*ReplicaSetStore, error) {
	store := cache.NewStore(cache.MetaNamespaceKeyFunc)

	replicatSetListWatcher := cache.NewListWatchFromClient(
		clientset.ExtensionsV1beta1().RESTClient(),
		replicaSetResource,
		v1.NamespaceAll,
		fields.Everything(),
	)

	reflector := cache.NewReflector(
		replicatSetListWatcher,
		&v1beta1.ReplicaSet{},
		store,
		time.Duration(0),
	)

	stopCh := make(chan struct{})

	return &ReplicaSetStore{
		store:     &store,
		reflector: reflector,
		stopCh:    stopCh,
	}, nil
}

func (p *ReplicaSetStore) Run() error {
	return newWatcher(p.reflector, replicaSetResource, p.reflector.ListAndWatch, p.stopCh).run()
}

func (p *ReplicaSetStore) Stop() {
	p.stopCh <- struct{}{}
}

func (p *ReplicaSetStore) GetReplicaSet(key string) (*v1beta1.ReplicaSet, error) {
	item, exists, err := (*p.store).GetByKey(key)

	if err != nil {
		return nil, err
	}
	if !exists {
		return nil, fmt.Errorf("no ReplicaSet exists for name %s", key)
	}
	rs, ok := item.(*v1beta1.ReplicaSet)
	if !ok {
		return nil, fmt.Errorf("%v is not a ReplicaSet", item)
	}
	return rs, nil
}

// GetDeploymentForPod returns pod owner information:
// if the pod's replicaset belongs to a deployment: "namespace/deployment"
// if the pod's replicaset does not belong to a deployment: "namespace/replicaset"
func (p *ReplicaSetStore) GetDeploymentForPod(pod *v1.Pod) (string, error) {
	namespace := pod.Namespace
	if len(pod.GetOwnerReferences()) == 0 {
		return "", fmt.Errorf("Pod %s has no owner", pod.Name)
	}
	parent := pod.GetOwnerReferences()[0]
	if parent.Kind == "ReplicaSet" {
		rsName := namespace + "/" + parent.Name
		rs, err := p.GetReplicaSet(rsName)
		if err != nil {
			return "", err
		}
		if len(rs.GetOwnerReferences()) == 0 {
			return namespace + "/" + rsName, nil
		}
		return namespace + "/" + rs.GetOwnerReferences()[0].Name, nil
	}
	return namespace + "/" + parent.Name, nil
}
