package resource

import (
	"context"

	imageregistryv1 "github.com/openshift/api/imageregistry/v1"
	"github.com/openshift/cluster-image-registry-operator/pkg/defaults"
	policyv1 "k8s.io/api/policy/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/intstr"
	policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
	policylisters "k8s.io/client-go/listers/policy/v1"
)

var _ Mutator = &generatorPodDisruptionBudget{}

type generatorPodDisruptionBudget struct {
	lister policylisters.PodDisruptionBudgetNamespaceLister
	client policyclient.PolicyV1Interface
	cr     *imageregistryv1.Config
}

func newGeneratorPodDisruptionBudget(lister policylisters.PodDisruptionBudgetNamespaceLister, client policyclient.PolicyV1Interface, cr *imageregistryv1.Config) *generatorPodDisruptionBudget {
	return &generatorPodDisruptionBudget{
		lister: lister,
		client: client,
		cr:     cr,
	}
}

func (gpdb *generatorPodDisruptionBudget) Type() runtime.Object {
	return &policyv1.PodDisruptionBudget{}
}

func (gpdb *generatorPodDisruptionBudget) GetNamespace() string {
	return defaults.ImageRegistryOperatorNamespace
}

func (gpdb *generatorPodDisruptionBudget) GetName() string {
	return "image-registry"
}

func (gpdb *generatorPodDisruptionBudget) expected() (runtime.Object, error) {
	minAvailable := intstr.FromInt(1)
	if gpdb.cr.Spec.Replicas <= 1 {
		minAvailable = intstr.FromInt(0)
	}

	pdb := &policyv1.PodDisruptionBudget{
		ObjectMeta: metav1.ObjectMeta{
			Name:      gpdb.GetName(),
			Namespace: gpdb.GetNamespace(),
		},
		Spec: policyv1.PodDisruptionBudgetSpec{
			MinAvailable: &minAvailable,
			Selector: &metav1.LabelSelector{
				MatchLabels: defaults.DeploymentLabels,
			},
		},
	}

	return pdb, nil
}

func (gpdb *generatorPodDisruptionBudget) Get() (runtime.Object, error) {
	return gpdb.lister.Get(gpdb.GetName())
}

func (gpdb *generatorPodDisruptionBudget) Create() (runtime.Object, error) {
	return commonCreate(gpdb, func(obj runtime.Object) (runtime.Object, error) {
		return gpdb.client.PodDisruptionBudgets(gpdb.GetNamespace()).Create(
			context.TODO(), obj.(*policyv1.PodDisruptionBudget), metav1.CreateOptions{},
		)
	})
}

func (gpdb *generatorPodDisruptionBudget) Update(o runtime.Object) (runtime.Object, bool, error) {
	return commonUpdate(gpdb, o, func(obj runtime.Object) (runtime.Object, error) {
		return gpdb.client.PodDisruptionBudgets(gpdb.GetNamespace()).Update(
			context.TODO(), obj.(*policyv1.PodDisruptionBudget), metav1.UpdateOptions{},
		)
	})
}

func (gpdb *generatorPodDisruptionBudget) Delete(opts metav1.DeleteOptions) error {
	return gpdb.client.PodDisruptionBudgets(gpdb.GetNamespace()).Delete(
		context.TODO(), gpdb.GetName(), opts,
	)
}

func (g *generatorPodDisruptionBudget) Owned() bool {
	return true
}
