package eks

import (
	"context"
	"fmt"
	"time"

	eksv1beta1 "github.com/crossplane-contrib/provider-aws/apis/eks/v1beta1"
	iamv1beta1 "github.com/crossplane-contrib/provider-aws/apis/iam/v1beta1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	"github.com/tidbcloud/infra-provider/pkg/util"
	crossplaneutil "github.com/tidbcloud/infra-provider/pkg/util/crossplane"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task"
)

// OIDC task create an oidc provider in aws to auth
type OIDC struct {
	*task.BaseTask
}

func NewOIDC(ctx *task.Context) *OIDC {
	return &OIDC{
		BaseTask: &task.BaseTask{Context: ctx},
	}
}

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

// this oidcprovider resource cannot be migrated from infra-apiserver,
// so let it run for migrated eks.
func (t *OIDC) Create(ctx context.Context) deploy.Result {
	t.BeginAt = util.Pointer(time.Now())
	eks := eksv1beta1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name: t.APPBox.Status.KubernetesClusterName,
		},
	}
	if err := t.Local.Get(ctx, client.ObjectKeyFromObject(&eks), &eks); err != nil {
		return deploy.Panic(err)
	}
	if eks.Status.AtProvider.Identity.OIDC.Issuer == "" {
		return deploy.Panic(fmt.Errorf("wait for eks ready to get oidc issuer"))
	}
	oidc := &iamv1beta1.OpenIDConnectProvider{
		ObjectMeta: metav1.ObjectMeta{
			Name: namer.UniqueIdentifier(t.APPBox.ObjectMeta),
		},
		Spec: iamv1beta1.OpenIDConnectProviderSpec{
			ForProvider: iamv1beta1.OpenIDConnectProviderParameters{
				ClientIDList: []string{
					"sts.amazonaws.com",
				},
				ThumbprintList: []string{
					"9e99a48a9960b14926bb7f3b02e22da2b0ab7280",
				},
				URL: eks.Status.AtProvider.Identity.OIDC.Issuer,
			},
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{
					Name: t.ProviderConfigName(),
				},
			},
		},
	}

	if err := k8s.Apply(ctx, t.Local, oidc); err != nil {
		return deploy.Panic(err)
	}

	return deploy.Done()
}

func (t *OIDC) PostCreate(ctx context.Context) deploy.Result {
	oidc := t.newOIDCProviderMeta()
	if err := t.Local.Get(ctx, client.ObjectKeyFromObject(oidc), oidc); err != nil {
		return deploy.Error(err)
	}
	if k8s.IsCrossplaneCRPaused(oidc) {
		return deploy.Done()
	}
	if !crossplaneutil.IsReadyAndSynced(oidc.Status.Conditions) {
		return deploy.Error(fmt.Errorf("oidc provider %s is not ready and synced yet", oidc.Name))
	}
	if err := k8s.PauseCrossplaneCR(ctx, t.Local, oidc); err != nil {
		return deploy.Error(err)
	}

	return deploy.Done()
}

func (t *OIDC) Delete(ctx context.Context) deploy.Result {
	if err := k8s.DeleteObjectCompletedWithHooks(ctx, t.Local, t.newOIDCProviderMeta(), k8s.HookAutoUnpauseCrossplaneCR); err != nil {
		return deploy.Error(err)
	}
	return deploy.Done()
}

func (t *OIDC) newOIDCProviderMeta() *iamv1beta1.OpenIDConnectProvider {
	return &iamv1beta1.OpenIDConnectProvider{
		ObjectMeta: metav1.ObjectMeta{
			Name: namer.UniqueIdentifier(t.APPBox.ObjectMeta),
		},
	}
}
