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

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

package certmanager

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

	"github.com/golingon/lingon/pkg/kube"
	"github.com/golingon/lingoneks/meta"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

var CM = Core()

func Core() Meta {
	ver := "1.12.2"
	tag := "v" + ver
	name := "cert-manager"
	ns := name
	ctrlPort := 9402

	m := meta.Metadata{
		Name:      name,
		Namespace: name,
		Instance:  name,
		Component: "controller",
		PartOf:    name,
		Version:   ver,
		ManagedBy: "lingon",
		Img: meta.ContainerImg{
			Registry: "quay.io", Image: "jetstack/cert-manager-controller",
			Tag: tag,
		},
	}

	cainjName := name + "-cainjector"
	cai := meta.Metadata{
		Name:      cainjName,
		Namespace: ns, Instance: cainjName, Component: "cainjector",
		PartOf: name, Version: ver, ManagedBy: "lingon",
		Img: meta.ContainerImg{
			Registry: "quay.io", Image: "jetstack/cert-manager-cainjector",
			Tag: tag,
		},
	}

	whName := name + "-webhook"
	wh := meta.Metadata{
		Name:      whName,
		Namespace: ns, Instance: whName, Component: "webhook",
		PartOf: name, Version: ver, ManagedBy: "lingon",
		Img: meta.ContainerImg{
			Registry: "quay.io", Image: "jetstack/cert-manager-webhook",
			Tag: tag,
		},
	}

	me := Meta{
		ACMEImg: meta.ContainerImg{
			Registry: "quay.io", Image: "jetstack/cert-manager-acmesolver",
			Tag: tag,
		},

		Controller:   m,
		ControllerSA: m.ServiceAccount(),
		ControllerPort: meta.NetPort{
			Container: corev1.ContainerPort{
				Name: "http-metrics", ContainerPort: int32(ctrlPort),
			},
			Service: corev1.ServicePort{
				Name: "tcp-prometheus-servicemonitor", Port: int32(ctrlPort),
				TargetPort: intstr.FromInt(ctrlPort),
			},
		},

		CAInj:   cai,
		CAInjSA: cai.ServiceAccount(),

		Webhook:   wh,
		WebhookSA: wh.ServiceAccount(),
		WebhookPort: meta.NetPort{
			Container: corev1.ContainerPort{
				Name: "https", ContainerPort: int32(10250),
			},
			Service: corev1.ServicePort{
				Name: "https", Port: int32(443),
				TargetPort: intstr.FromString("https"),
			},
		},
	}

	return me
}

type Meta struct {
	ACMEImg        meta.ContainerImg
	Controller     meta.Metadata
	ControllerPort meta.NetPort
	ControllerSA   *corev1.ServiceAccount

	CAInj   meta.Metadata
	CAInjSA *corev1.ServiceAccount

	Webhook     meta.Metadata
	WebhookPort meta.NetPort
	WebhookSA   *corev1.ServiceAccount
}

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

// CertManager contains kubernetes manifests
type CertManager struct {
	kube.App
	CaInjector
	Webhook
	Controller
}

// New creates a new CertManager
func New() *CertManager {
	return &CertManager{
		CaInjector: CaInjector{
			CaInjectorCR:                 CainjectorCR,
			CaInjectorCRB:                CaInjectorCRB,
			CaInjectorDeploy:             CaInjectorDeploy,
			CaInjectorLeaderelectionRB:   CaInjectorLeaderElectionRB,
			CaInjectorLeaderelectionRole: CaInjectorLeaderElectionRole,
			CaInjectorSA:                 CM.CAInjSA,
		},
		Controller: Controller{
			ControllerApproveIoCR:                   ControllerApproveIoCR,
			ControllerApproveIoCRB:                  ControllerApproveIoCRB,
			ControllerCertificatesCR:                ControllerCertificatesCR,
			ControllerCertificatesCRB:               ControllerCertificatesCRB,
			ControllerCertificateSigningRequestsCR:  ControllerCertificateSigningRequestsCR,
			ControllerCertificateSigningRequestsCRB: ControllerCertificateSigningRequestsCRB,
			ControllerChallengesCR:                  ControllerChallengesCR,
			ControllerChallengesCRB:                 ControllerChallengesCRB,
			ControllerClusterIssuersCR:              ControllerClusterIssuersCR,
			ControllerClusterIssuersCRB:             ControllerClusterIssuersCRB,
			ControllerIngressShimCR:                 ControllerIngressShimCR,
			ControllerIngressShimCRB:                ControllerIngressShimCRB,
			ControllerIssuersCR:                     ControllerIssuersCR,
			ControllerIssuersCRB:                    ControllerIssuersCRB,
			ControllerOrdersCR:                      ControllerOrdersCR,
			ControllerOrdersCRB:                     ControllerOrdersCRB,
			ControllerDeploy:                        ControllerDeploy,
			ViewCR:                                  ViewCR,
			EditCR:                                  EditCR,
			LeaderElectionRB:                        LeaderElectionRB,
			LeaderElectionRole:                      LeaderElectionRole,
			PDB:                                     PDB,
			SA:                                      CM.ControllerSA,
			SVC:                                     ControllerSVC,
			ServiceMonitor:                          ServiceMonitor,
		},
		Webhook: Webhook{
			WebhookCM:                      WebhookCM,
			WebhookDeploy:                  WebhookDeploy,
			WebhookDynamicServingRB:        WebhookDynamicServingRB,
			WebhookDynamicServingRole:      WebhookDynamicServingRole,
			WebhookMutatingWC:              WebhookMutatingWC,
			WebhookSA:                      CM.WebhookSA,
			WebhookSVC:                     WebhookSVC,
			WebhookSubjectAccessReviewsCR:  WebhookSubjectAccessReviewsCR,
			WebhookSubjectAccessReviewsCRB: WebhookSubjectAccessReviewsCRB,
			WebhookValidatingWC:            WebhookValidatingWC,
		},
	}
}

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

// Export exports the kubernetes objects to YAML files in the given directory
func (a *CertManager) 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
}
