/*
Copyright (c) Edgeless Systems GmbH

SPDX-License-Identifier: AGPL-3.0-only
*/

package resources

import (
	"fmt"
	"net"
	"strings"

	"github.com/edgelesssys/constellation/v2/internal/constants"
	"github.com/edgelesssys/constellation/v2/internal/kubernetes"
	"github.com/edgelesssys/constellation/v2/internal/versions"
	"google.golang.org/protobuf/proto"
	apps "k8s.io/api/apps/v1"
	k8s "k8s.io/api/core/v1"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

type VerificationDaemonset struct {
	DaemonSet    apps.DaemonSet
	Service      k8s.Service
	LoadBalancer k8s.Service
}

func NewVerificationDaemonSet(csp, loadBalancerIP string) *VerificationDaemonset {
	var err error
	if strings.Contains(loadBalancerIP, ":") {
		loadBalancerIP, _, err = net.SplitHostPort(loadBalancerIP)
		if err != nil {
			panic(err)
		}
	}
	return &VerificationDaemonset{
		DaemonSet: apps.DaemonSet{
			TypeMeta: meta.TypeMeta{
				APIVersion: "apps/v1",
				Kind:       "DaemonSet",
			},
			ObjectMeta: meta.ObjectMeta{
				Name:      "verification-service",
				Namespace: "kube-system",
				Labels: map[string]string{
					"k8s-app":   "verification-service",
					"component": "verification-service",
				},
			},
			Spec: apps.DaemonSetSpec{
				Selector: &meta.LabelSelector{
					MatchLabels: map[string]string{
						"k8s-app": "verification-service",
					},
				},
				Template: k8s.PodTemplateSpec{
					ObjectMeta: meta.ObjectMeta{
						Labels: map[string]string{
							"k8s-app": "verification-service",
						},
					},
					Spec: k8s.PodSpec{
						Tolerations: []k8s.Toleration{
							{
								Key:      "node-role.kubernetes.io/master",
								Operator: k8s.TolerationOpEqual,
								Value:    "true",
								Effect:   k8s.TaintEffectNoSchedule,
							},
							{
								Key:      "node-role.kubernetes.io/control-plane",
								Operator: k8s.TolerationOpExists,
								Effect:   k8s.TaintEffectNoSchedule,
							},
							{
								Operator: k8s.TolerationOpExists,
								Effect:   k8s.TaintEffectNoExecute,
							},
							{
								Operator: k8s.TolerationOpExists,
								Effect:   k8s.TaintEffectNoSchedule,
							},
						},
						Containers: []k8s.Container{
							{
								Name:  "verification-service",
								Image: versions.VerificationImage,
								Ports: []k8s.ContainerPort{
									{
										Name:          "http",
										ContainerPort: constants.VerifyServicePortHTTP,
									},
									{
										Name:          "grpc",
										ContainerPort: constants.VerifyServicePortGRPC,
									},
								},
								SecurityContext: &k8s.SecurityContext{
									Privileged: func(b bool) *bool { return &b }(true),
								},
								Args: []string{
									fmt.Sprintf("--cloud-provider=%s", csp),
								},
								VolumeMounts: []k8s.VolumeMount{
									{
										Name:      "event-log",
										ReadOnly:  true,
										MountPath: "/sys/kernel/security/",
									},
								},
							},
						},
						Volumes: []k8s.Volume{
							{
								Name: "event-log",
								VolumeSource: k8s.VolumeSource{
									HostPath: &k8s.HostPathVolumeSource{
										Path: "/sys/kernel/security/",
									},
								},
							},
						},
					},
				},
			},
		},
		Service: k8s.Service{
			TypeMeta: meta.TypeMeta{
				APIVersion: "v1",
				Kind:       "Service",
			},
			ObjectMeta: meta.ObjectMeta{
				Name:      "verification-service",
				Namespace: "kube-system",
			},
			Spec: k8s.ServiceSpec{
				Type: k8s.ServiceTypeNodePort,
				Ports: []k8s.ServicePort{
					{
						Name:       "http",
						Protocol:   k8s.ProtocolTCP,
						Port:       constants.VerifyServicePortHTTP,
						TargetPort: intstr.FromInt(constants.VerifyServicePortHTTP),
						NodePort:   constants.VerifyServiceNodePortHTTP,
					},
					{
						Name:       "grpc",
						Protocol:   k8s.ProtocolTCP,
						Port:       constants.VerifyServicePortGRPC,
						TargetPort: intstr.FromInt(constants.VerifyServicePortGRPC),
						NodePort:   constants.VerifyServiceNodePortGRPC,
					},
				},
				Selector: map[string]string{
					"k8s-app": "verification-service",
				},
			},
		},
		LoadBalancer: k8s.Service{
			TypeMeta: meta.TypeMeta{
				APIVersion: "v1",
				Kind:       "Service",
			},
			ObjectMeta: meta.ObjectMeta{
				Name:      "verify",
				Namespace: "kube-system",
			},
			Spec: k8s.ServiceSpec{
				AllocateLoadBalancerNodePorts: proto.Bool(false),
				Type:                          k8s.ServiceTypeLoadBalancer,
				LoadBalancerClass:             proto.String("constellation"),
				ExternalIPs:                   []string{loadBalancerIP},
				Ports: []k8s.ServicePort{
					{
						Name:       "grpc",
						Protocol:   k8s.ProtocolTCP,
						Port:       constants.VerifyServiceNodePortGRPC,
						TargetPort: intstr.FromInt(constants.VerifyServicePortGRPC),
					},
				},
				Selector: map[string]string{
					"k8s-app": "verification-service",
				},
			},
		},
	}
}

func (v *VerificationDaemonset) Marshal() ([]byte, error) {
	return kubernetes.MarshalK8SResources(v)
}