package task

import (
	"context"

	"github.com/crossplane/provider-aws/apis/eks/v1beta1"
	"github.com/tidbcloud/eks-provider/api/v1alpha1"
	"github.com/tidbcloud/eks-provider/pkg/deploy"
	"github.com/tidbcloud/eks-provider/pkg/k8s"
	"github.com/tidbcloud/eks-provider/pkg/util"
	"sigs.k8s.io/aws-iam-authenticator/pkg/token"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

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

type Connection struct {
	*BaseTask
	tg token.Generator
}

func NewConnection(ctx *Context, tg token.Generator) *Connection {
	return &Connection{BaseTask: &BaseTask{Context: ctx}, tg: tg}
}

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

func (t *Connection) Create(ctx context.Context) deploy.Result {
	name := util.UniqueName(t.Network.Namespace, t.Network.Name)
	if t.cluster == nil {
		return deploy.Skip("empty cluster")
	}
	cr := &corev1.ObjectReference{
		APIVersion: v1beta1.ClusterKindAPIVersion,
		Kind:       v1beta1.ClusterKind,
		Namespace:  t.cluster.GetNamespace(),
		Name:       util.UniqueName(t.Network.Namespace, t.Network.Name),
		UID:        t.cluster.GetUID(),
	}

	conn := &v1alpha1.EKSConnection{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}
	_, err := controllerutil.CreateOrUpdate(ctx, t.Local, conn, func() error {
		conn.Spec = v1alpha1.EKSConnectionSpec{
			Type:              v1alpha1.FRPType,
			Target:            cr,
			AssumeRole:        t.NetworkSpec.AssumeRoleArn,
			ExternalID:        t.NetworkSpec.AssumeExternalId,
			ProviderConfigRef: name,
		}
		return nil
	})
	return deploy.FromError(err)
}

func (t *Connection) PostCreate(ctx context.Context) deploy.Result {
	name := util.UniqueName(t.Network.Namespace, t.Network.Name)
	req := k8s.EKSRequest{
		ConnectionName:   name,
		ClusterName:      name,
		ClusterRegion:    t.Network.Spec.Region,
		AssumeRoleArn:    t.NetworkSpec.AssumeRoleArn,
		AssumeExternalID: t.NetworkSpec.AssumeExternalId,
	}
	g, err := k8s.NewEKSGetter(ctx, t.Local, req)
	if err != nil {
		return deploy.FromError(err)
	}
	t.remote = g
	return deploy.FromError(nil)
}

func (t *Connection) Delete(ctx context.Context) deploy.Result {
	conn := v1alpha1.EKSConnection{
		ObjectMeta: metav1.ObjectMeta{
			Name: util.UniqueName(t.Network.Namespace, t.Network.Name),
		},
	}
	err := t.Local.Delete(ctx, &conn)
	return deploy.FromError(err)
}

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