// Copyright (c) 2023 Volvo Car Corporation
// SPDX-License-Identifier: Apache-2.0

// Code generated by lingon. EDIT AS MUCH AS YOU LIKE.

package policy

import (
	"context"
	"errors"
	"os"
	"os/exec"

	"github.com/golingon/lingon/pkg/kube"
	ku "github.com/golingon/lingon/pkg/kubeutil"
	"github.com/golingon/lingoneks/meta"
	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
	appsv1 "k8s.io/api/apps/v1"
	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

var W = Core()

func Core() Meta {
	name := "sigstore-controller"
	ns := "sigstore"
	ver := "0.8.0"

	m := meta.Metadata{
		Name:      name,
		Namespace: ns,
		Instance:  "sigstore-controller-policy",
		Component: "webhook",
		PartOf:    "sigstore",
		Version:   ver,
		ManagedBy: "lingon",
		Img: meta.ContainerImg{
			Registry: "ghcr.io",
			Image:    "sigstore/policy-controller/policy-controller",
			Sha:      "e91bcd954394b414d3b80adfc2cefdae84dd7985fb938a895471eb34aac57744",
			// Tag:      "",
		},
	}
	return Meta{
		Metadata: m,
		HomeDir: ku.VolumeAndMount{
			VolumeMount: corev1.VolumeMount{
				MountPath: "/home/nonroot",
				Name:      "writable-home-dir",
			},
			VolumeSource: corev1.VolumeSource{},
		},
		SA: m.ServiceAccount(),
		MainPort: meta.NetPort{
			Container: corev1.ContainerPort{
				Name: "https", ContainerPort: 8443,
				Protocol: corev1.ProtocolTCP,
			},
			Service: corev1.ServicePort{
				Name:       "https",
				Port:       443,
				TargetPort: intstr.FromString("https"),
			},
		},
		Metrics: meta.NetPort{
			Container: corev1.ContainerPort{
				Name: "metrics", ContainerPort: 9090,
				Protocol: corev1.ProtocolTCP,
			},
			Service: corev1.ServicePort{
				Name: "metrics", Port: 9090, Protocol: corev1.ProtocolTCP,
			},
		},
	}
}

type Meta struct {
	meta.Metadata
	SA       *corev1.ServiceAccount
	HomeDir  ku.VolumeAndMount
	MainPort meta.NetPort
	Metrics  meta.NetPort
}

var WebhookCertsSecrets = &corev1.Secret{
	TypeMeta:   ku.TypeSecretV1,
	ObjectMeta: W.ObjectMeta(),
	Data:       nil,
}

const cp = "control-plane"

// validate the struct implements the interface
var _ kube.Exporter = (*PolicyController)(nil)

// PolicyController contains kubernetes manifests
type PolicyController struct {
	kube.App

	ConfigCM                 *corev1.ConfigMap
	ConfigImagePoliciesCM    *corev1.ConfigMap
	ConfigSigstoreKeysCM     *corev1.ConfigMap
	LeasesCleanupJOBS        *batchv1.Job
	SigstoreCleanupRB        *rbacv1.RoleBinding
	SigstoreCleanupRole      *rbacv1.Role
	CR                       *rbacv1.ClusterRole
	CRB                      *rbacv1.ClusterRoleBinding
	SigstoreWebhookCleanupSA *corev1.ServiceAccount
	Deploy                   *appsv1.Deployment
	ConfigLoggingCM          *corev1.ConfigMap
	MetricsSVC               *corev1.Service
	RB                       *rbacv1.RoleBinding
	Role                     *rbacv1.Role
	SA                       *corev1.ServiceAccount
	WebhookCertsSecrets      *corev1.Secret
	SVC                      *corev1.Service

	DefaultingClusterImagePolicySigstoreDevMutatingWC   *admissionregistrationv1.MutatingWebhookConfiguration
	MutatingPolicySigstoreDevMWC                        *admissionregistrationv1.MutatingWebhookConfiguration
	ValidatingPolicySigstoreDevVWC                      *admissionregistrationv1.ValidatingWebhookConfiguration
	ValidatingClusterImagePolicySigstoreDevValidatingWC *admissionregistrationv1.ValidatingWebhookConfiguration
}

// New creates a new PolicyController
func New() *PolicyController {
	return &PolicyController{
		ConfigCM:              ConfigCM,
		ConfigImagePoliciesCM: ConfigImagePoliciesCM,
		ConfigSigstoreKeysCM:  ConfigSigstoreKeysCM,
		CR:                    CR,
		CRB:                   CRB,
		Deploy:                Deploy,
		ConfigLoggingCM:       ConfigLoggingCM,
		MetricsSVC:            MetricsSVC,
		RB:                    RB,
		Role:                  Role,
		SA:                    W.SA,
		WebhookCertsSecrets:   WebhookCertsSecrets,
		SVC:                   SVC,

		MutatingPolicySigstoreDevMWC:                        MutatingPolicySigstoreDevMWC,
		ValidatingPolicySigstoreDevVWC:                      ValidatingPolicySigstoreDevVWC,
		ValidatingClusterImagePolicySigstoreDevValidatingWC: ValidatingClusterImagePolicyVWC,
		DefaultingClusterImagePolicySigstoreDevMutatingWC:   DefaultingClusterImagePolicyMWC,

		// SigstoreWebhookCleanupSA: SigstoreWebhookCleanupSA,
		// LeasesCleanupJOBS:        LeasesCleanupJOBS,
		// SigstoreCleanupRB:        SigstoreCleanupRB,
		// SigstoreCleanupRole:      SigstoreCleanupRole,
	}
}

// Apply applies the kubernetes objects to the cluster
func (a *PolicyController) Apply(ctx context.Context) error {
	return Apply(ctx, a)
}

// Export exports the kubernetes objects to YAML files in the given directory
func (a *PolicyController) Export(dir string) error {
	return kube.Export(a, kube.WithExportOutputDirectory(dir))
}

// Apply applies the kubernetes objects contained in Exporter to the cluster
func Apply(ctx context.Context, km kube.Exporter) error {
	cmd := exec.CommandContext(ctx, "kubectl", "apply", "-f", "-")
	cmd.Env = os.Environ()        // inherit environment in case we need to use kubectl from a container
	stdin, err := cmd.StdinPipe() // pipe to pass data to kubectl
	if err != nil {
		return err
	}

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	go func() {
		defer func() {
			err = errors.Join(err, stdin.Close())
		}()
		if errEW := kube.Export(
			km,
			kube.WithExportWriter(stdin),
			kube.WithExportAsSingleFile("stdin"),
		); errEW != nil {
			err = errors.Join(err, errEW)
		}
	}()

	if errS := cmd.Start(); errS != nil {
		return errors.Join(err, errS)
	}

	// waits for the command to exit and waits for any copying
	// to stdin or copying from stdout or stderr to complete
	return errors.Join(err, cmd.Wait())
}

// P converts T to *T, useful for basic types
func P[T any](t T) *T {
	return &t
}
