package cluster

import (
	"context"
	"fmt"
	"time"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/eks-provider/api/v1alpha1"
	"github.com/tidbcloud/eks-provider/controllers/tiflow/task"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/namer"
)

const (
	retryInterval = time.Second * 10
)

// NodeGroupGC cleanup all node groups which have different spec-code as the tiflow-executor or tiflow-master.
// during scale-up-down, it's responsible to clean up previous node group
// note: this task only works on CREATE phase, it does NOT clean up node group we're using.
type NodeGroupGC struct {
	task.BaseTask
	*BaseContext
	*v1alpha1.Conditions
	Collector GCInterface
}

type GCInterface interface {
	GC(ctx context.Context, selector *client.MatchingLabelsSelector) deploy.Result
	CheckGCResult(ctx context.Context, selector *client.MatchingLabelsSelector) deploy.Result
}

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

func (t *NodeGroupGC) Create(ctx context.Context) deploy.Result {
	isReady := t.IsReadyForGC()
	if !isReady {
		return t.Retry()
	}

	selector, err := t.generateSelector()
	if err != nil {
		return t.Retry()
	}

	return t.Collector.GC(ctx, selector)
}

func (t *NodeGroupGC) PostCreate(ctx context.Context) deploy.Result {
	isReady := t.IsReadyForGC()
	if !isReady {
		return t.Retry()
	}

	selector, err := t.generateSelector()
	if err != nil {
		return t.Retry()
	}

	return t.Collector.CheckGCResult(ctx, selector)
}

func (t *NodeGroupGC) IsReadyForGC() bool {
	for _, condition := range *t.Conditions {
		if condition.Status != corev1.ConditionTrue {
			t.Logger.Info("condition not ready")
			return false
		}
	}
	return true
}

func (t *NodeGroupGC) generateSelector() (*client.MatchingLabelsSelector, error) {
	noSpecCodeRequirement, err := labels.NewRequirement(
		namer.TiflowSpecLabelKey, selection.NotEquals, []string{t.BaseContext.Namer.GetTiflowSpecLabel()})
	if err != nil {
		t.BaseContext.Logger.Info("label selector not ready")
		return nil, fmt.Errorf("label selector not ready")
	}

	selector := &client.MatchingLabelsSelector{
		Selector: labels.Set(t.BaseContext.Namer.LabelsWithoutSpecLabel()).AsSelector().
			Add(*noSpecCodeRequirement),
	}
	return selector, nil
}

func (t *NodeGroupGC) Retry() deploy.Result {
	return deploy.Error(k8s.RequeueOnlyError).WithRequeuAfter(retryInterval)
}
