// Licensed Materials - Property of IBM
// (c) Copyright IBM Corporation 2018, 2019. All Rights Reserved.
// Note to U.S. Government Users Restricted Rights:
// Use, duplication or disclosure restricted by GSA ADP Schedule
// Contract with IBM Corp.

package accesspolicy

import (
	"github.com/golang/glog"
	apv1alpha1 "github.com/open-cluster-management/hcm-compliance/pkg/apis/accesspolicy/v1alpha1"
	apLister "github.com/open-cluster-management/hcm-compliance/pkg/client/listers/accesspolicy/v1alpha1"
	"github.com/open-cluster-management/hcm-compliance/pkg/common"
	"github.com/open-cluster-management/seed-sdk/pkg/context"
	resv1 "github.com/open-cluster-management/seed-sdk/pkg/types/apis/resource/v1"
	corev1 "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
)

var (
	// AccessPolicyLister allows lookup of AccessPolicies
	AccessPolicyLister apLister.AccessPolicyLister
)

const name = "accesspolicy"
var myFinalizerNames   = []string{name + common.Finalizer, common.OldFinalizer}

// Reconcile handles AccessPolicy Spec changes
func Reconcile(ctx context.Context, obj *apv1alpha1.AccessPolicy) error {
	glog.Infof("accesspolicy.Reconcile(%s.%s) called", obj.GetNamespace(), obj.ObjectMeta.Name)
	err := resv1.EnsureFinalizerAndPut(ctx, obj, name + common.Finalizer)
	if err != nil {
		glog.Errorf("Error ensuring finalizer %v\n", err)
	}

	HandleAccessPolicy(ctx, obj)

	return nil
}

// Finalizer removes the finalizer name
func Finalizer(ctx context.Context, obj *apv1alpha1.AccessPolicy) error {
	glog.V(3).Infof("Deleting resource %s.%s", obj.GetNamespace(), obj.ObjectMeta.Name)
	common.RemoveFinalizerAndPut(ctx, obj, myFinalizerNames)
	//resv1.RemoveFinalizerAndPut(ctx, obj, name + common.Finalizer)
	return nil
}

// ReconcileService handles Service pod selector changes that impact AccessPolicies
func ReconcileService(ctx context.Context, svc *corev1.Service) error {
	glog.V(3).Infof("accesspolicy.ReconcileService(%s.%s) called", svc.GetNamespace(), svc.ObjectMeta.Name)

	aps, err := AccessPolicyLister.List(labels.Everything())
	if err != nil {
		glog.Infof("error listing access policies: %v", err)
		return nil
	}

	for _, ap := range aps {
		glog.V(4).Infof("available AccessPolicy: %s.%s", ap.GetNamespace(), ap.ObjectMeta.Name)
	}

	return nil
}

// ReconcileNetworkPolicy handles NetworkPolicy changes that impact AccessPolicies
func ReconcileNetworkPolicy(ctx context.Context, obj *networkingv1.NetworkPolicy) error {
	glog.Infof("accesspolicy.ReconcileNetworkPolicy(%s) called", obj.ObjectMeta.Name)

	glog.Infof("accesspolicy.ReconcileNetworkPolicy has owner: %v", obj.GetOwnerReferences())
	glog.Infof("accesspolicy.ReconcileNetworkPolicy has controller: %v", metav1.GetControllerOf(obj))
	// if ownerRef := obj.GetOwnerReferences(); ownerRef != nil {
	if ownerRef := metav1.GetControllerOf(obj); ownerRef != nil {
		// If this object is not owned by a AccessPolicy, we do not do anything more with it
		if ownerRef.Kind != "AccessPolicy" {
			return nil
		}

		ap, err := AccessPolicyLister.AccessPolicies(obj.GetNamespace()).Get(ownerRef.Name)
		if err != nil {
			glog.V(4).Infof("ignoring orphaned AccessPolicy object '%s' of '%s'", obj.GetSelfLink(), ownerRef.Name)
			return nil
		}

		glog.V(4).Infof("Identified related AccessPolicy: %s.%s", ap.GetNamespace(), ap.ObjectMeta.Name)
		Reconcile(ctx, ap)
		return nil
	}
	return nil
}

// HandleAccessPolicy will parse the access policy and figure out the actions needed to enforce it.
func HandleAccessPolicy(ctx context.Context, obj *apv1alpha1.AccessPolicy) error {
	glog.V(3).Info("accesspolicy.HandleAccessPolicy() called")

	getPoliciesMapper(ctx).reconcileAccessPolicy(obj)

	return nil
}
