package task

import (
	"context"
	"fmt"
	"strings"
	"time"

	"k8s.io/utils/ptr"

	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	corev1 "k8s.io/api/core/v1"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/controllers/common"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	util "github.com/tidbcloud/infra-provider/pkg/util"
)

var _ deploy.Task = &BaseTask{}

type BaseTask struct {
	*Context
}

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

func (t *BaseTask) Category() deploy.TaskCategory {
	return ""
}

func (t *BaseTask) Create(ctx context.Context) deploy.Result {
	return deploy.Done()
}

func (t *BaseTask) PostCreate(ctx context.Context) deploy.Result {
	return deploy.Done()
}

func (t *BaseTask) Delete(ctx context.Context) deploy.Result {
	return deploy.Done()
}

func (t *BaseTask) PostDelete(ctx context.Context) deploy.Result {
	return deploy.Done()
}

// MetricsStartTime will return the `BeginAt` (nil by default). For
// Task whose metrics need to be exported, the `BeginAt` should be
// initialized when the `Create` is being called.
func (t *BaseTask) MetricsStartTime() *time.Time {
	return t.BeginAt
}

func (t *BaseTask) GetResourceTag() map[string]*string {
	return map[string]*string{
		"tenant_id":  ptr.To(t.TenantID),
		"project_id": ptr.To(t.ProjectID),
		"region":     ptr.To(t.GetRegion()),
		"component":  ptr.To(ComponentName),
	}
}

func (t *BaseTask) GetPlainResourceTag() map[string]string {
	return map[string]string{
		"tenant_id":  t.TenantID,
		"project_id": t.ProjectID,
		"region":     t.GetRegion(),
		"component":  ComponentName,
	}
}

func (t *BaseTask) GetMasterName() string {
	return fmt.Sprintf("%s-%s-%s", t.Namer.GetClusterName(), t.DMJob.ObjectMeta.Name, "master")
}

func (t *BaseTask) GetLables() map[string]string {
	labels := map[string]string{
		"cloud.pingcap.com/provider":  t.Config.Provider,
		"cloud.pingcap.com/region":    t.GetRegion(),
		"cloud.pingcap.com/tenant":    t.TenantID,
		"cloud.pingcap.com/project":   t.ProjectID,
		"cloud.pingcap.com/component": ComponentName,
	}
	return labels
}

func (t *BaseTask) GetCommonLabels4Azure() map[string]string {
	return map[string]string{
		cloudv1.ProviderTypeLabel: consts.ProviderTypeAKS,
		cloudv1.RegionLabel:       t.GetRegion(),
		cloudv1.ProjectLabel:      t.ProjectID,
	}
}

func (t *BaseTask) getNodeLables() map[string]string {
	tags := cloudv1.TagsFromLabels(t.DMJob.Labels)
	commonLabels := t.GetLables()
	if t.Config.Provider == consts.ProviderTypeAKS {
		commonLabels = t.GetCommonLabels4Azure()
	}

	commonLabels[common.Component] = ComponentName
	commonLabels[common.UsedBy] = common.CustomerTiDB
	commonLabels[namer.DMJobNameLabelKey] = t.DMJob.Name
	tags = common.ConcatenateTags(tags, commonLabels)

	labels := t.Namer.WithComponent(ComponentName).
		WithNodeGroupSetOwner(util.FnvHash(t.DMJob.ClusterScopeName())).
		Labels()
	tags = common.ConcatenateTags(tags, labels)

	return tags
}

func (t *BaseTask) GetMasterNodeLabels() map[string]string {
	labels := t.getNodeLables()
	labels[namer.DMMemberSpecLabelKey] = t.DMMasterSpec.SpecCode
	labels[namer.DMMasterLabelKey] = "true"
	return labels
}

func (t *BaseTask) GetWorkerNodeLabels() map[string]string {
	labels := t.getNodeLables()
	labels[namer.DMMemberSpecLabelKey] = t.DMWorkerSpec.SpecCode
	labels[namer.DMWorkerLabelKey] = "true"
	return labels
}

func (t *BaseTask) IsDMClusterReady(dmCluster *v1alpha1.DMCluster) bool {

	var isReady bool
	for _, condition := range dmCluster.Status.Conditions {
		if condition.Type == v1alpha1.DMClusterReady && condition.Status == corev1.ConditionTrue {
			isReady = true
			break
		}
	}

	return isReady &&
		dmCluster.Status.Master.Phase == v1alpha1.NormalPhase &&
		dmCluster.Status.Worker.Phase == v1alpha1.NormalPhase
}

func (t *BaseTask) GetImageTag(image string) string {
	var tag string
	if pos := strings.Index(image, ":"); pos != -1 {
		tag = image[pos+1:]
	} else {
		tag = image
	}
	return tag
}
