package common

import (
	"context"
	"fmt"
	"time"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/consts"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/helm"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/util"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
)

type DataflowAgent struct {
	*task.BaseTask
	config *dataflowAgentConfig
}

type dataflowAgentConfig struct {
	config.InfraComponentConfig
	lbServiceTags           map[string]string
	overwriteImageTag       string
	overwritePodHostNetwork *bool
}

func (c *dataflowAgentConfig) ToHelmValues() map[string]interface{} {
	values := c.InfraComponentConfig.ToHelmValues()
	if c.overwriteImageTag != "" {
		image := values["image"]
		image.(map[string]interface{})["tag"] = c.overwriteImageTag
	}
	if podHostNetwork := c.overwritePodHostNetwork; podHostNetwork != nil {
		values["podHostNetwork"] = *podHostNetwork
	}
	return values
}

func NewDataflowAgent(ctx *task.Context) *DataflowAgent {
	t := &task.BaseTask{Context: ctx}

	return &DataflowAgent{
		BaseTask: t,
		config: &dataflowAgentConfig{
			InfraComponentConfig: ctx.DPTmplConfig.DataflowAgent,
		},
	}
}

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

func (t *DataflowAgent) Create(ctx context.Context) deploy.Result {
	t.BeginAt = util.Pointer(time.Now())
	tags := util.MergeMaps(controllers.ConvertToCloudTags(t.APPBox.Labels, cloudv1.ProviderTypeEKS), map[string]string{
		consts.TagKeyComponent: "dataflow-agent",
		consts.TagKeyUsedBy:    "dbaas-control-plane-shoot",
	})
	t.config.lbServiceTags = tags
	if tag := t.APPBox.Annotations[v1alpha1.AnnotationKeyOwnImageTagDataflowAgent]; tag != "" {
		t.config.overwriteImageTag = tag
	}
	return deploy.ErrorOrDone(helm.Apply(ctx, t.Remote, t.config))
}

func (t *DataflowAgent) PostCreate(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}
	var service corev1.Service
	if err := cli.Get(ctx, types.NamespacedName{
		Namespace: t.config.GetNamespace(),
		Name:      t.config.GetName(),
	}, &service); err != nil {
		return deploy.Panic(err)
	}
	if service.Spec.Type == corev1.ServiceTypeLoadBalancer {
		if len(service.Status.LoadBalancer.Ingress) < 1 {
			t.Logger.Info("waiting for load balancer to be ready")
			return deploy.Error(fmt.Errorf("waiting for load balancer to be provisioned"))
		}
		var addr string
		switch t.APPBox.Spec.Provider {
		case consts.ProviderTypeEKS:
			addr = service.Status.LoadBalancer.Ingress[0].Hostname
		case consts.ProviderTypeGKE:
			addr = service.Status.LoadBalancer.Ingress[0].IP
		case consts.ProviderTypeAKS:
			addr = service.Status.LoadBalancer.Ingress[0].IP
		default:
			t.Logger.Error(fmt.Errorf("unsupported provider type"), "unsupported provider type", "provider", t.APPBox.Spec.Provider)
		}
		t.APPBox.Status.DataflowAgentUrl = addr
	}
	return deploy.ErrorOrDone(controllers.EnsureChartDeploymentReady(ctx, t.Remote, types.NamespacedName{
		Namespace: t.config.GetNamespace(),
		Name:      t.config.GetName(),
	}))
}
