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 ClusterProxy struct {
	*task.BaseTask
	config *clusterProxyConfig
}

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

func (c *clusterProxyConfig) 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 NewClusterProxy(ctx *task.Context) *ClusterProxy {
	t := &task.BaseTask{Context: ctx}

	return &ClusterProxy{
		BaseTask: t,
		config: &clusterProxyConfig{
			InfraComponentConfig: ctx.DedicatedTmplConfig.ClusterProxy,
		},
	}
}

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

func (t *ClusterProxy) Create(ctx context.Context) deploy.Result {
	t.BeginAt = util.Pointer(time.Now())

	if t.Config.APPBoxConfig.DisableClusterProxy {
		if t.config.GetName() == "" {
			t.Logger.Info("ClusterProxy is disabled and has no template, skipping uninstall")
			t.APPBox.Status.ProxyBaseUrl = ""
			return deploy.Done()
		}
		t.Logger.Info("ClusterProxy is disabled, uninstalling if exists")
		if err := helm.Uninstall(ctx, t.Remote, t.config, t.config.GetName()); err != nil {
			return deploy.Error(err)
		}
		t.APPBox.Status.ProxyBaseUrl = ""
		return deploy.Done()
	}

	tags := util.MergeMaps(controllers.ConvertToCloudTags(t.APPBox.Labels, cloudv1.ProviderTypeEKS), map[string]string{
		consts.TagKeyComponent: "dbaas-proxy",
		consts.TagKeyUsedBy:    "dbaas-control-plane-shoot",
	})
	t.config.lbServiceTags = tags
	if tag := t.APPBox.Annotations[v1alpha1.AnnotationKeyOwnImageTagClusterProxy]; tag != "" {
		t.config.overwriteImageTag = tag
	}
	return deploy.ErrorOrDone(helm.Apply(ctx, t.Remote, t.config))
}

func (t *ClusterProxy) PostCreate(ctx context.Context) deploy.Result {
	if t.Config.APPBoxConfig.DisableClusterProxy {
		t.APPBox.Status.ProxyBaseUrl = ""
		return deploy.Done()
	}

	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: fmt.Sprintf("%s-cluster-proxy", t.config.GetName())}, &service); err != nil {
		return deploy.Error(err)
	}
	if service.Spec.Type == corev1.ServiceTypeLoadBalancer {
		if len(service.Status.LoadBalancer.Ingress) < 1 {
			return deploy.Error(fmt.Errorf("waiting for proxy 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:
			return deploy.Error(fmt.Errorf("provider %s is not supported", t.APPBox.Spec.Provider))
		}
		t.APPBox.Status.ProxyBaseUrl = addr
	}

	return deploy.ErrorOrDone(controllers.EnsureChartDeploymentReady(ctx, t.Remote, types.NamespacedName{
		Namespace: t.config.GetNamespace(),
		Name:      fmt.Sprintf("%s-cluster-proxy", t.config.GetName()),
	}))
}
