package task

import (
	"context"
	"fmt"

	vmoperatorv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1"

	agentv2 "github.com/tidbcloud/observability/agent-control-server/api/v2"
	"github.com/tidbcloud/observability/agent-control-server/controllers/tidbgroupagent/metadata"
	"github.com/tidbcloud/observability/agent-control-server/pkg/agentbuild"
	"github.com/tidbcloud/observability/agent-control-server/pkg/consts"
	"github.com/tidbcloud/observability/agent-control-server/pkg/deploy"
	"github.com/tidbcloud/observability/agent-control-server/pkg/reconciler"
	"github.com/tidbcloud/observability/agent-control-server/pkg/util"
)

type VMPodScrapeTask struct {
	*reconciler.BaseTask[*agentv2.TiDBGroupAgent]
}

func NewVMPodScrapeTask(ctx *reconciler.Context[*agentv2.TiDBGroupAgent]) *VMPodScrapeTask {
	return &VMPodScrapeTask{
		BaseTask: &reconciler.BaseTask[*agentv2.TiDBGroupAgent]{Context: ctx},
	}
}

func (t *VMPodScrapeTask) Name() string {
	return "VMPodScrape"
}

func (t *VMPodScrapeTask) ClusterMeta() deploy.ClusterMeta {
	return metadata.ClusterMeta(t.Agent)
}

type vmPodScrapeSpec struct {
	tmplKey string
	name    string
}

func (t *VMPodScrapeTask) vmPodScrapeSpecs() []vmPodScrapeSpec {
	specs := []vmPodScrapeSpec{
		{tmplKey: "tidbgroup/jobs/tidb.yaml", name: fmt.Sprintf("db-%s-tidb", t.Agent.Spec.TiDBGroup)},
	}
	if util.GetAnnotation(t.Agent, consts.AnnotationKeyO11yTiDBNodeExporterScrape) == "true" {
		specs = append(specs, vmPodScrapeSpec{
			tmplKey: "tidbgroup/jobs/node-exporter.yaml",
			name:    fmt.Sprintf("db-%s-node-exporter", t.Agent.Spec.ClusterID),
		})
	}
	if !consts.IsTiProxyDeployTypeSharedTiProxy(t.Agent.Annotations) {
		specs = append(specs, vmPodScrapeSpec{
			tmplKey: "tidbgroup/jobs/tiproxy.yaml",
			name:    fmt.Sprintf("db-%s-tiproxy", t.Agent.Spec.ClusterID),
		})
	}
	specs = append(specs, vmPodScrapeSpec{
		tmplKey: "tidbgroup/jobs/cassandra-proxy.yaml",
		name:    fmt.Sprintf("db-%s-cassandra-proxy", t.Agent.Spec.ClusterID),
	})
	return specs
}

func (t *VMPodScrapeTask) buildVMPodScrape(spec vmPodScrapeSpec) (*vmoperatorv1beta1.VMPodScrape, error) {
	return agentbuild.BuildVMPodScrape(t.Templates[spec.tmplKey], map[string]string{
		"Name":          spec.name,
		"Namespace":     t.buildNamespace(),
		"LabelScrapeBy": fmt.Sprintf("tidb%s-o11y-vmagent", t.Agent.Spec.SharedPoolID),
		"TiDBGroup":     t.Agent.Spec.TiDBGroup,
		"TenantID":      t.Agent.Spec.O11y.TenantID,
		"ClusterID":     t.Agent.Spec.ClusterID,
		"VMAccountID":   util.GetAnnotation(t.Agent, consts.AnnotationKeyO11yVMAccountID),
		"VMProjectID":   util.GetAnnotation(t.Agent, consts.AnnotationKeyO11yVMProjectID),
	})
}

func (t *VMPodScrapeTask) Create(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}
	for _, spec := range t.vmPodScrapeSpecs() {
		s := spec
		if err := agentbuild.CreateOrUpdateVMPodScrape(ctx, cli, func() (*vmoperatorv1beta1.VMPodScrape, error) {
			return t.buildVMPodScrape(s)
		}); err != nil {
			return deploy.Panic(err)
		}
	}
	return deploy.Done()
}

func (t *VMPodScrapeTask) PostCreate(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}
	for _, spec := range t.vmPodScrapeSpecs() {
		if err := agentbuild.CheckVMPodScrapeReady(ctx, cli, spec.name, t.buildNamespace()); err != nil {
			return deploy.Panic(err)
		}
	}
	return deploy.Done()
}

func (t *VMPodScrapeTask) Delete(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}
	for _, spec := range t.vmPodScrapeSpecs() {
		if err := agentbuild.DeleteVMPodScrape(ctx, cli, spec.name, t.buildNamespace()); err != nil {
			return deploy.Panic(err)
		}
	}
	return deploy.Done()
}

func (t *VMPodScrapeTask) PostDelete(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}
	for _, spec := range t.vmPodScrapeSpecs() {
		if err := agentbuild.CheckVMPodScrapeDeleted(ctx, cli, spec.name, t.buildNamespace()); err != nil {
			return deploy.Panic(err)
		}
	}
	return deploy.Done()
}

func (t *VMPodScrapeTask) buildNamespace() string {
	return fmt.Sprintf("tidb%s", t.Agent.Spec.SharedPoolID)
}
