package task

import (
	"context"

	v1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/crossplane/provider-aws/apis/v1beta1"
	"github.com/tidbcloud/eks-provider/pkg/deploy"
	"github.com/tidbcloud/eks-provider/pkg/util"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/pointer"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

type ProviderConfig struct {
	BaseTask
}

func NewProviderConfig(ctx *Context) *ProviderConfig {
	return &ProviderConfig{BaseTask: BaseTask{Context: ctx}}
}

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

func (t *ProviderConfig) Create(ctx context.Context) deploy.Result {
	// ProviderConfig is a cluster resource instead of namespaced, so we have to construct a unique
	// name for the object.
	name := util.UniqueName(t.Network.Namespace, t.Network.Name)
	config := &v1beta1.ProviderConfig{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}
	_, err := controllerutil.CreateOrUpdate(ctx, t.Local, config, func() error {
		config.Annotations = map[string]string{
			"assume-role": t.NetworkSpec.AssumeRoleArn,
			"external-id": t.NetworkSpec.AssumeExternalId,
		}
		config.Spec = v1beta1.ProviderConfigSpec{
			AssumeRoleARN: pointer.String(t.NetworkSpec.AssumeRoleArn),
			ExternalID:    pointer.String(t.NetworkSpec.AssumeExternalId),
			Credentials: v1beta1.ProviderCredentials{
				Source: v1.CredentialsSourceInjectedIdentity,
			},
		}
		return nil
	})
	return deploy.FromError(err)
}

func (t *ProviderConfig) PostCreate(ctx context.Context) deploy.Result {
	var c v1beta1.ProviderConfig
	key := types.NamespacedName{
		Name: util.UniqueName(t.Network.Namespace, t.Network.Name),
	}
	err := t.Local.Get(ctx, key, &c)
	return deploy.FromError(err)
}

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

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