package policy

import (
	"fmt"
	"reflect"

	"github.com/go-logr/logr"
	kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
	kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
	"github.com/nirmata/kyverno/pkg/engine/response"
	"k8s.io/apimachinery/pkg/labels"
)

func (pc *PolicyController) cleanUp(ers []response.EngineResponse) {
	for _, er := range ers {
		if !er.IsSuccessful() {
			continue
		}
		if len(er.PolicyResponse.Rules) == 0 {
			continue
		}
		// clean up after the policy has been corrected
		pc.cleanUpPolicyViolation(er.PolicyResponse)
	}
}

func (pc *PolicyController) cleanUpPolicyViolation(pResponse response.PolicyResponse) {
	logger := pc.log
	// - check if there is violation on resource (label:Selector)
	if pResponse.Resource.Namespace == "" {
		pv, err := getClusterPV(pc.cpvLister, pResponse.Policy, pResponse.Resource.Kind, pResponse.Resource.Name, logger)
		if err != nil {
			logger.Error(err, "failed to get cluster policy violation on policy and resource", "policy", pResponse.Policy, "kind", pResponse.Resource.Kind, "name", pResponse.Resource.Name)
			return
		}

		if reflect.DeepEqual(pv, kyverno.ClusterPolicyViolation{}) {
			return
		}
		if err := pc.pvControl.DeleteClusterPolicyViolation(pv.Name); err != nil {
			logger.Error(err, "failed to delete cluster policy violation", "name", pv.Name)
		} else {
			logger.Info("deleted cluster policy violation", "name", pv.Name)
		}
		return
	}

	// namespace policy violation
	nspv, err := getNamespacedPV(pc.nspvLister, pResponse.Policy, pResponse.Resource.Kind, pResponse.Resource.Namespace, pResponse.Resource.Name, logger)
	if err != nil {
		logger.Error(err, "failed to get namespaced policy violation on policy and resource", "policy", pResponse.Policy, "kind", pResponse.Resource.Kind, "namespace", pResponse.Resource.Namespace, "name", pResponse.Resource.Name)
		return
	}

	if reflect.DeepEqual(nspv, kyverno.PolicyViolation{}) {
		return
	}
	if err := pc.pvControl.DeleteNamespacedPolicyViolation(nspv.Namespace, nspv.Name); err != nil {
		logger.Error(err, "failed to delete cluster policy violation", "name", nspv.Name, "namespace", nspv.Namespace)
	} else {
		logger.Info("deleted namespaced policy violation", "name", nspv.Name, "namespace", nspv.Namespace)
	}
}

// Wont do the claiming of objects, just lookup based on selectors
func getClusterPV(pvLister kyvernolister.ClusterPolicyViolationLister, policyName, rkind, rname string, log logr.Logger) (kyverno.ClusterPolicyViolation, error) {
	var err error
	// Check Violation on resource
	pvs, err := pvLister.List(labels.Everything())
	if err != nil {
		log.Error(err, "failed to list cluster policy violations")
		return kyverno.ClusterPolicyViolation{}, fmt.Errorf("failed to list cluster pv: %v", err)
	}

	for _, pv := range pvs {
		// find a policy on same resource and policy combination
		if pv.Spec.Policy == policyName &&
			pv.Spec.ResourceSpec.Kind == rkind &&
			pv.Spec.ResourceSpec.Name == rname {
			return *pv, nil
		}
	}
	return kyverno.ClusterPolicyViolation{}, nil
}

func getNamespacedPV(nspvLister kyvernolister.PolicyViolationLister, policyName, rkind, rnamespace, rname string, log logr.Logger) (kyverno.PolicyViolation, error) {
	nspvs, err := nspvLister.PolicyViolations(rnamespace).List(labels.Everything())
	if err != nil {
		log.Error(err, "failed to list namespaced policy violation")
		return kyverno.PolicyViolation{}, fmt.Errorf("failed to list namespaced pv: %v", err)
	}

	for _, nspv := range nspvs {
		// find a policy on same resource and policy combination
		if nspv.Spec.Policy == policyName &&
			nspv.Spec.ResourceSpec.Kind == rkind &&
			nspv.Spec.ResourceSpec.Name == rname {
			return *nspv, nil
		}
	}

	return kyverno.PolicyViolation{}, nil
}
