package task

import (
	"context"
	"fmt"
	"strings"

	vmoperatorv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"

	agentv2 "github.com/tidbcloud/observability/agent-control-server/api/v2"
	"github.com/tidbcloud/observability/agent-control-server/controllers/sharedpoolagent/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"
)

type ServerlessVMPodScrapeTask struct {
	*reconciler.BaseTask[*agentv2.SharedPoolAgent]
	vmpodscrapeNames []string
}

func NewServerlessVMPodScrapeTask(ctx *reconciler.Context[*agentv2.SharedPoolAgent]) *ServerlessVMPodScrapeTask {
	return &ServerlessVMPodScrapeTask{
		BaseTask: &reconciler.BaseTask[*agentv2.SharedPoolAgent]{Context: ctx},
	}
}

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

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

func (t *ServerlessVMPodScrapeTask) Create(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}
	return deploy.PanicOrDone(t.create(ctx, cli))
}

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

func (t *ServerlessVMPodScrapeTask) Delete(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}
	vmpodscrapes, err := t.listVmpodscrapes(ctx, cli)
	if err != nil {
		return deploy.Panic(err)
	}
	for _, vmpodscrape := range vmpodscrapes {
		if err := t.deleteVmpodscrape(ctx, cli, vmpodscrape.Name); err != nil {
			return deploy.Panic(err)
		}
	}
	return deploy.Done()
}

func (t *ServerlessVMPodScrapeTask) PostDelete(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}
	vmpodscrapes, err := t.listVmpodscrapes(ctx, cli)
	if err != nil {
		return deploy.Panic(err)
	}
	if len(vmpodscrapes) > 0 {
		return deploy.Panic(fmt.Errorf("still have %d vmpodscrapes remaining", len(vmpodscrapes)))
	}
	return deploy.Done()
}

func (t *ServerlessVMPodScrapeTask) create(ctx context.Context, cli client.Client) error {
	var vmpodscrapeNames []string
	for name, tmpl := range t.Templates {
		if !strings.HasPrefix(name, "sharedpool/jobs/") {
			continue
		}
		if err := agentbuild.CreateOrUpdateVMPodScrape(ctx, cli, func() (*vmoperatorv1beta1.VMPodScrape, error) {
			v, err := agentbuild.BuildVMPodScrape(tmpl, map[string]string{
				"Namespace": t.buildNamespace(),
			})
			if err != nil {
				return nil, err
			}
			vmpodscrapeNames = append(vmpodscrapeNames, v.Name)
			return v, nil
		}); err != nil {
			return err
		}
	}
	t.vmpodscrapeNames = vmpodscrapeNames
	return nil
}

func (t *ServerlessVMPodScrapeTask) listVmpodscrapes(ctx context.Context, cli client.Client) ([]vmoperatorv1beta1.VMPodScrape, error) {
	vmpodscrapes := &vmoperatorv1beta1.VMPodScrapeList{}
	err := cli.List(ctx, vmpodscrapes, client.InNamespace(t.buildNamespace()),
		client.MatchingLabels{consts.LabelKeyVMPodScrapeAgent: consts.AgentSharedPool},
	)
	if err != nil {
		if meta.IsNoMatchError(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("failed to list vmpodscrapes: %w", err)
	}
	return vmpodscrapes.Items, nil
}

func (t *ServerlessVMPodScrapeTask) deleteVmpodscrape(ctx context.Context, cli client.Client, name string) error {
	err := cli.Delete(ctx, &vmoperatorv1beta1.VMPodScrape{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: t.buildNamespace(),
		},
	})
	if client.IgnoreNotFound(err) == nil {
		return nil
	}
	if meta.IsNoMatchError(err) {
		return nil
	}
	return err
}

func (t *ServerlessVMPodScrapeTask) buildNamespace() string {
	return consts.ServerlessObservabilityNamespace
}
