// 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 main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/golang/glog"
	"github.com/open-cluster-management/seed-sdk/pkg/controller"
	"github.com/open-cluster-management/seed-sdk/pkg/registry/clientsets"
	"github.com/open-cluster-management/seed-sdk/pkg/registry/config"
	"github.com/open-cluster-management/seed-sdk/pkg/registry/informers"
	"github.com/open-cluster-management/hcm-compliance/pkg/accesspolicy"
	apApi "github.com/open-cluster-management/hcm-compliance/pkg/apis/accesspolicy/v1alpha1"
	apClientset "github.com/open-cluster-management/hcm-compliance/pkg/client/clientset/versioned"
	apInformer "github.com/open-cluster-management/hcm-compliance/pkg/client/informers/externalversions"
	apInternal "github.com/open-cluster-management/hcm-compliance/pkg/client/informers/externalversions/internalinterfaces"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/rest"
)

func main() {
	var err error
	flag.String("kubeconfig", "", "Path to a kube config. Only required if out-of-cluster.")
	reconcileFrequency := flag.Duration("reconcilefrequency", time.Second*10, "time in seconds to reconcile, default is 30 seconds. example: \"30s\", or \"2h45m\". Valid time units are ms, s, m, h")
	// flag.Parse()   // this is done in config.ParseFlagsOrDie()

	k8sConfig := config.ParseFlagsOrDie()

	defer glog.Flush()

	glog.V(3).Infof("Started with cluster name")

	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		cleanup()
	}()

	// Create lister for AccessPolicies; to be used by handlers for Service, NetworkPolicy
	clientset := clientsets.AddClientsetForConfigOrDie("accesspolicy",
		newClientsetForConfig, k8sConfig).(*apClientset.Clientset)
	err = informers.AddFilteredFactory("default", newSharedInformerFactory,
		clientset, time.Duration(10), metav1.NamespaceAll, nil)
	if err != nil {
		panic(err)
	}
	factory := informers.Get("accesspolicy").(apInformer.SharedInformerFactory)
	accesspolicy.AccessPolicyLister = factory.Accesspolicy().V1alpha1().AccessPolicies().Lister()

	// Configure AccessPolicy controller
	err = controller.New().
		ParseFlags().
		Watch("accesspolicies.v1alpha1.accesspolicy.mcm.ibm.com", "AccessPolicy", &apApi.SchemeBuilder).
		From(k8sConfig).
		Reconcile(accesspolicy.Reconcile).
		Finalize(accesspolicy.Finalizer).
		ResyncPeriod(*reconcileFrequency).
		Watch("services.v1.core", "Service", nil).
		Informer(informers.GetKube("kubernetes").Core().V1().Services().Informer()).
		From(k8sConfig).
		Reconcile(accesspolicy.ReconcileService).
		Watch("networkpolicies.v1.networking", "NetworkPolicy", nil).
		Informer(informers.GetKube("kubernetes").Networking().V1().NetworkPolicies().Informer()).
		From(k8sConfig).
		Reconcile(accesspolicy.ReconcileNetworkPolicy).
		Install(apApi.CustomResourceDefinitions).
		Run()
	if err != nil {
		fmt.Print(err)
	}
}

// Clean up logic
func cleanup() {
	fmt.Println("cleaning up...")
	os.Exit(0)
}

// These should be in: pkg/apis/accesspolicy/v1alpha1/zz_generated.crd.go
// When they are, refer to them via:
//    apApi.CustomResourceDefinitions.ClientsetConstructor and
//    apApi.CustomResourceDefinitions.InformerFactoryConstructor
func newClientsetForConfig(config *rest.Config) (clientsets.Interface, error) {
	return apClientset.NewForConfig(config)
}

func newSharedInformerFactory(clientset clientsets.Interface, defaultResync time.Duration, namespace string, listOptions func(*metav1.ListOptions)) informers.SharedInformerFactory {
	tweak := apInternal.TweakListOptionsFunc(listOptions)
	return apInformer.NewFilteredSharedInformerFactory(clientset.(apClientset.Interface), defaultResync, namespace, tweak).(informers.SharedInformerFactory)
}
