package task

import (
	"context"
	"fmt"

	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/crossplane/provider-aws/apis/eks/v1beta1"
	"github.com/tidbcloud/eks-provider/pkg/consts"
	"github.com/tidbcloud/eks-provider/pkg/deploy"
	"github.com/tidbcloud/eks-provider/pkg/util"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/pointer"
)

type EKS struct {
	*BaseTask
}

func NewEKS(ctx *Context) *EKS {
	return &EKS{BaseTask: &BaseTask{Context: ctx}}
}

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

func (t *EKS) Create(ctx context.Context) deploy.Result {
	name := util.UniqueName(t.Network.Namespace, t.Network.Name)

	cluster := &v1beta1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}
	_, err := controllerutil.CreateOrUpdate(ctx, t.Local, cluster, func() error {
		cluster.Spec = v1beta1.ClusterSpec{
			ForProvider: v1beta1.ClusterParameters{
				Region:  pointer.String(t.Network.Spec.Region),
				Version: pointer.String(t.ProviderSpec.EKS.Version),
				ResourcesVpcConfig: v1beta1.VpcConfigRequest{
					SecurityGroupIDs:      []string{t.NetworkSpec.SecurityGroupId},
					SubnetIDs:             t.NetworkSpec.SubnetIds,
					EndpointPrivateAccess: pointer.Bool(true),
					EndpointPublicAccess:  pointer.Bool(false),
				},
				RoleArn: t.NetworkSpec.ServiceRoleArn,
				Tags: map[string]string{
					consts.CrossplaneVendorKey: consts.CrossplaneVendorKey,
				},
			},
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{Name: name},
			},
		}
		return nil
	})
	return deploy.FromError(err)
}

func (t *EKS) PostCreate(ctx context.Context) (res deploy.Result) {
	key := types.NamespacedName{
		Name: util.UniqueName(t.Network.Namespace, t.Network.Name),
	}
	var c v1beta1.Cluster
	err := t.Local.Get(ctx, key, &c)
	if err != nil {
		return deploy.FromError(err)
	}
	status := c.Status.AtProvider.Status
	if status != v1beta1.ClusterStatusActive {
		return deploy.FromError(fmt.Errorf("cluster status not ready: %v", status))
	}
	t.cluster = &c
	return deploy.FromError(nil)
}

func (t *EKS) Delete(ctx context.Context) deploy.Result {
	name := util.UniqueName(t.Network.Namespace, t.Network.Name)
	cluster := &v1beta1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}
	err := t.Local.Delete(ctx, cluster)
	return deploy.FromError(err)
}

func (t *EKS) PostDelete(ctx context.Context) deploy.Result {
	key := types.NamespacedName{
		Name: util.UniqueName(t.Network.Namespace, t.Network.Namespace),
	}
	var c v1beta1.Cluster
	err := t.Local.Get(ctx, key, &c)
	if apierrors.IsNotFound(err) {
		return deploy.FromError(nil)
	}
	return deploy.FromError(err)
}
