package eks

import (
	"context"
	"fmt"

	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	eksv1alpha1 "github.com/tidbcloud/eks-provider/api/v1alpha1"
	"github.com/tidbcloud/eks-provider/controllers/tiflow/spec"
	"github.com/tidbcloud/eks-provider/controllers/tiflow/task/cluster"
	"github.com/tidbcloud/eks-provider/controllers/tiflow/task/cluster/master"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/controllers/common"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/util/collections"
)

type PrivateLinkEndpoint struct {
	*master.BaseTask
}

func NewPrivateLinkEndpoint(ctx *master.Context) *PrivateLinkEndpoint {
	return &PrivateLinkEndpoint{BaseTask: &master.BaseTask{Context: ctx}}
}

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

func (t *PrivateLinkEndpoint) Create(ctx context.Context) deploy.Result {
	privatelinkEndpoint := &eksv1alpha1.PrivateLinkEndpoint{}
	privatelinkEndpoint.Namespace = t.TiflowClusterMaster.Namespace
	privatelinkEndpoint.Name = t.GetPrivateLinkEndpointName()

	_, err := controllerutil.CreateOrUpdate(ctx, t.Local, privatelinkEndpoint, func() error {
		if privatelinkEndpoint.Labels == nil {
			privatelinkEndpoint.Labels = make(map[string]string)
		}
		labelFromConfig := t.Config.GenerateLabelFromConfig()
		privatelinkEndpoint.Labels = collections.MergeMaps(privatelinkEndpoint.Labels, labelFromConfig)
		privatelinkEndpoint.Labels = collections.MergeMaps(privatelinkEndpoint.Labels, map[string]string{
			spec.ClusterTagLabelKey:   spec.GetTiDBClusterName(&t.TiflowClusterMaster.ObjectMeta),
			spec.ComponentTagLabelKey: string(cloudv1.ComponentTiflow),
			spec.UseByTagLabelKey:     common.CustomerTiDB,
		})
		privatelinkEndpoint.Spec.NetworkName = t.Network.Name
		privatelinkEndpoint.Spec.Region = t.Network.Spec.Region
		privatelinkEndpoint.Spec.ServiceName = t.Config.TiflowCluster.Master.ShareDb.ServiceName
		return nil
	})
	if err != nil {
		return deploy.ErrorOrDone(err)
	}
	return deploy.Done()
}

func (t *PrivateLinkEndpoint) PostCreate(ctx context.Context) deploy.Result {
	isReady, err := t.checkEndpointIsReady(ctx)
	if err != nil {
		return deploy.Error(err)
	}

	cluster.UpdateCondition(&t.TiflowClusterMaster.Status.Conditions, eksv1alpha1.TiflowClusterPrivateLinkEndPointReady, isReady)

	if isReady {
		return deploy.Done()
	} else {
		return deploy.Error(fmt.Errorf("wait privatelink endpoint ready"))
	}
}

func (t *PrivateLinkEndpoint) checkEndpointIsReady(ctx context.Context) (bool, error) {
	privateLinkEndpoint := t.GetPrivateLinkEndpointMetaObject()
	err := t.Local.Get(ctx, client.ObjectKeyFromObject(privateLinkEndpoint), privateLinkEndpoint)
	if err != nil {
		return false, err
	}

	//privateLinkEndpoint.Status.Phase == eksv1alpha1.PrivateLinkEndpointPhaseRunning &&
	if privateLinkEndpoint.Status.AtProvider.State != nil &&
		*privateLinkEndpoint.Status.AtProvider.State == eksv1alpha1.EndpointResourceStateAvailable &&
		len(privateLinkEndpoint.Status.AtProvider.DNSEntries) > 0 {
		return true, nil
	}
	return false, nil
}

func (t *PrivateLinkEndpoint) Delete(ctx context.Context) deploy.Result {
	return deploy.ErrorOrDone(k8s.DeleteObject(ctx, t.Local, t.GetPrivateLinkEndpointMetaObject()))
}

func (t *PrivateLinkEndpoint) PostDelete(ctx context.Context) deploy.Result {
	privateLinkEndpoint := t.GetPrivateLinkEndpointMetaObject()
	return deploy.ErrorOrDone(k8s.GetDeletedObject(ctx, t.Local, client.ObjectKeyFromObject(privateLinkEndpoint), privateLinkEndpoint))
}
