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

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

package vmk8s

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

	vmo "github.com/VictoriaMetrics/operator/api/operator/v1"
	"github.com/golingon/lingon/pkg/kube"
	ku "github.com/golingon/lingon/pkg/kubeutil"
	corev1 "k8s.io/api/core/v1"
)

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

const (
	namespace          = "monitoring"
	version            = "1.91.2"
	appName            = "victoriametrics"
	VMSinglePort       = 8429
	VMAlertManagerPort = 9093
)

var d = func(i int) string { return fmt.Sprintf("%d", i) }

// Vmk8S contains kubernetes manifests
//
// the original documentation is https://github.com/VictoriaMetrics/helm-charts/tree/master/charts/victoria-metrics-k8s-stack

type Vmk8S struct {
	kube.App

	// Operator         *Operator
	Grafana          *Grafana
	KubeStateMetrics *KubeStateMetrics
	NodeExporter     *NodeExporter
	VMAlertManager   *VMAlertManager

	MonKubeScheduler  *MonKubeScheduler
	MonAPIServer      *MonAPIServer
	MonCoreDNS        *MonCoreDNS
	MonETCD           *MonETCD
	MonKubeController *MonKubeController
	MonKubeProxy      *MonKubeProxy
	VicMet            *VicMet

	CadvisorNodeScrape *vmo.VMNodeScrape
	ProbesNodeScrape   *vmo.VMNodeScrape
	KubeletNodeScrape  *vmo.VMNodeScrape

	K8SRules *K8SRules

	DashboardK8SGlobalCM     *corev1.ConfigMap
	DashboardK8SNamespacesCM *corev1.ConfigMap
	DashboardK8SPodsCM       *corev1.ConfigMap
	DashboardK8sNodes        *corev1.ConfigMap

	DashboardVMOperatorCM *corev1.ConfigMap
	// DashboardBackupManagerCM   *corev1.ConfigMap
	DashboardAgentCM           *corev1.ConfigMap
	DashboardVictoriaMetricsCM *corev1.ConfigMap

	DashboardNatsCM   *corev1.ConfigMap
	DashboardNatsJSCM *corev1.ConfigMap

	DashboardKarpenterPerfCM           *corev1.ConfigMap
	DashboardKarpenterControllersCM    *corev1.ConfigMap
	DashboardKarpenterCtrlAllocationCM *corev1.ConfigMap
	DashboardKarpenterCapacityCM       *corev1.ConfigMap
}

type Vmk8sOpts func(s *Vmk8S) *Vmk8S

// New creates a new Vmk8S
func New(opts ...Vmk8sOpts) *Vmk8S {
	v := &Vmk8S{
		// Operator:         NewOperator(),
		Grafana:          NewGrafana(),
		KubeStateMetrics: NewKubeStateMetrics(),
		NodeExporter:     NewNodeExporter(),
		VMAlertManager:   NewVMAlertManager(),

		MonAPIServer:      NewMonAPIServer(),
		MonKubeProxy:      NewMonKubeProxy(),
		MonKubeScheduler:  NewMonKubeScheduler(),
		MonKubeController: NewMonKubeController(),
		MonCoreDNS:        NewMonCoreDNS(),
		MonETCD:           NewMonETCD(),
		VicMet:            NewVicMet(),

		K8SRules: NewK8SRules(),

		CadvisorNodeScrape: CadvisorNodeScrape,
		ProbesNodeScrape:   ProbesNodeScrape,
		KubeletNodeScrape:  KubeletNodeScrape,

		DashboardK8SGlobalCM:     DashboardK8SGlobalCM,
		DashboardK8SNamespacesCM: DashboardK8SNamespacesCM,
		DashboardK8SPodsCM:       DashboardK8SPodsCM,
		DashboardK8sNodes:        DashboardK8sNodes,

		DashboardVMOperatorCM:      DashboardVMOperator,
		DashboardVictoriaMetricsCM: DashboardVictoriaMetricsCM,
		// DashboardBackupManagerCM:   DashboardBackupManagerCM,
		DashboardAgentCM: DashboardAgentCM,

		DashboardNatsCM:   DashboardNatsCM,
		DashboardNatsJSCM: DashboardNatsJSCM,

		DashboardKarpenterCapacityCM:       DashboardKarpenterCapacityCM,
		DashboardKarpenterCtrlAllocationCM: DashboardKarpenterCtrlAllocationCM,
		DashboardKarpenterPerfCM:           DashboardKarpenterPerfCM,
		DashboardKarpenterControllersCM:    DashboardKarpenterControllersCM,
	}
	for _, o := range opts {
		v = o(v)
	}

	return v
}

var VictoriaMetricsSA = ku.ServiceAccount(
	Single.Name, Single.Namespace, Single.Labels(), nil,
)

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

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