package common

import (
	"context"
	"fmt"
	"time"

	"helm.sh/helm/v3/pkg/chart"
	"helm.sh/helm/v3/pkg/chart/loader"
	appsv1 "k8s.io/api/apps/v1"
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	"k8s.io/apimachinery/pkg/types"

	"github.com/tidbcloud/eks-provider/controllers/network/task"
	"github.com/tidbcloud/eks-provider/controllers/utils"
	"github.com/tidbcloud/infra-provider/pkg/config"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/helm"
	"github.com/tidbcloud/infra-provider/pkg/metrics"
	"github.com/tidbcloud/infra-provider/pkg/util"
)

type EventExporterConfig config.ComponentConfig

// EventExporter is a task to deploy KubernetesEventExporter,
// which collects k8s events and print them to stdout.
// Then, they will be collected by fluent-bit.
type EventExporter struct {
	*task.BaseTask
	config *EventExporterConfig
}

func (c *EventExporterConfig) GetNamespace() string {
	return task.ObservabilityNamespace
}

func (c *EventExporterConfig) GetName() string {
	return "event-exporter"
}

func (c *EventExporterConfig) GetCRDs() ([]*apiextensionsv1.CustomResourceDefinition, error) {
	return nil, nil
}

func (c *EventExporterConfig) ToHelmChart() (result *chart.Chart, err error) {
	result, err = loader.Load(c.ChartPath)
	if err != nil {
		return nil, fmt.Errorf("failed to load helm chart %q: %w", c.ChartPath, err)
	}
	return
}

func (c *EventExporterConfig) ToHelmValues() map[string]interface{} {
	/* The default config is as follows, we can override it by setting the helm values:
	kubeBurst: 60
	kubeQPS: 60
	logFormat: json
	logLevel: error
	receivers:
	- name: dump
	  stdout: {}
	route:
	  routes:
	    - match:
	      - receiver: dump
	throttlePeriod: 300    # During watch events, ignore events older than 5 minutes.
	*/
	return map[string]interface{}{
		"image": map[string]string{
			"repository": c.Image.Repo,
			"tag":        c.Image.Tag,
		},
		"resources": map[string]interface{}{
			"requests": map[string]string{
				"cpu":    "50m",
				"memory": "100Mi",
			},
			"limits": map[string]string{
				"cpu":    "200m",
				"memory": "400Mi",
			},
		},
	}
}

func NewEventExporter(ctx *task.Context) *EventExporter {
	return &EventExporter{
		BaseTask: &task.BaseTask{Context: ctx},
		config: &EventExporterConfig{
			HelmConfig: config.HelmConfig{
				ChartPath: ctx.Config.Network.O11yConfig.EventExporterConfig.HelmChartPath,
			},
			Image: ctx.Config.Network.O11yConfig.EventExporterConfig.Image,
		},
	}
}

var _ helm.Package = &EventExporterConfig{}

func (t *EventExporter) Name() string {
	return "EventExporter"
}

func (t *EventExporter) Create(ctx context.Context) deploy.Result {
	t.BeginAt = util.Pointer(time.Now())
	return deploy.ErrorOrDone(helm.Apply(ctx, t.Remote, t.config))
}

func (t *EventExporter) PostCreate(ctx context.Context) (res deploy.Result) {
	defer func() {
		metrics.UpdateO11yComponentHealthStatus(t.Network, t.config.GetNamespace(), t.Name(), res.IsDoneState())
	}()
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}

	var app appsv1.Deployment
	if err := cli.Get(ctx, types.NamespacedName{
		Namespace: t.config.GetNamespace(),
		Name:      t.config.GetName(),
	}, &app); err != nil {
		return deploy.Error(fmt.Errorf("failed to get EventExporter Deployment: %v", err))
	}

	if utils.IsStatusConditionTrue(app.Status.Conditions, appsv1.DeploymentAvailable) {
		return deploy.Done()
	}

	return deploy.Error(fmt.Errorf("EventExporter is not ready"))
}
