package common

import (
	"context"
	"fmt"
	"time"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"

	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/helm"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/util"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task"
)

type ClusterGateway struct {
	*task.BaseTask
	config *ClusterGatewayConfig
}

type ClusterGatewayConfig struct {
	config.InfraComponentConfig

	lbServiceTags map[string]string
}

func (c *ClusterGatewayConfig) GetName() string {
	return "clustergateway"
}

func (c *ClusterGatewayConfig) ToHelmValues() map[string]interface{} {
	values := c.InfraComponentConfig.ToHelmValues()
	if proxy, ok := values["proxy"]; ok {
		if proxyMap, ok := proxy.(map[string]interface{}); ok {
			if annotations, ok := proxyMap["annotations"]; ok {
				if annotationsMap, ok := annotations.(map[string]interface{}); ok {
					annotationsMap["service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags"] = cloudv1.TagStringForAWS(c.lbServiceTags)
				}
			}
		}
	}
	return values
}

func NewClusterGateway(ctx *task.Context) *ClusterGateway {
	return &ClusterGateway{
		BaseTask: &task.BaseTask{Context: ctx},
		config:   &ClusterGatewayConfig{InfraComponentConfig: ctx.DedicatedTmplConfig.ClusterGateway},
	}
}

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

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

	// use the correct tags for loadbalancer
	tags := common.ConcatenateTags(
		cloudv1.TagsFromLabels(t.APPBox.Labels),
		map[string]string{
			common.Component: "cluster-gateway",
			common.UsedBy:    "dbaas-control-plane-shoot",
		})
	t.config.lbServiceTags = tags
	return deploy.ErrorOrDone(helm.Apply(ctx, t.Remote, t.config))
}

func (t *ClusterGateway) PostCreate(ctx context.Context) (res 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: fmt.Sprintf("%s-kong-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 cluster gateway load balancer 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 not supported", t.APPBox.Spec.Provider))
		}
		t.APPBox.Status.GatewayBaseUrl = addr
	}
	return deploy.ErrorOrDone(controllers.EnsureChartDeploymentReady(ctx, t.Remote, types.NamespacedName{
		Namespace: t.config.GetNamespace(),
		Name:      fmt.Sprintf("%s-kong", t.config.GetName()),
	}))
}

// Delete removes the proxy service, must use deploy.Panic() if error is not null.
// Because we want to remove the service before other tasks(eks,nodegroup,vpc).
func (t *ClusterGateway) Delete(ctx context.Context) deploy.Result {
	if _, ok := t.Remote.(*k8s.EmptyGetter); ok {
		t.Logger.Info("remote client is empty getter, skip deleting")
		return deploy.Done()
	}
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}

	return deploy.PanicOrDone(k8s.DeleteObjectCompleted(ctx, cli, &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: t.config.GetNamespace(),
			Name:      "clustergateway-kong-proxy",
		},
	}))
}
