package gke

import (
	"context"

	"github.com/crossplane-contrib/provider-gcp/apis/v1beta1"
	v1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"github.com/tidbcloud/eks-provider/api/v1alpha1"
	"github.com/tidbcloud/eks-provider/controllers/network/task"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/namer"
)

type ProviderConfig struct {
	task.BaseTask
}

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

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

func (t *ProviderConfig) Create(ctx context.Context) deploy.Result {
	networkSpec, err := v1alpha1.GKENetworkSpecForNetwork(t.Network)
	if err != nil {
		return deploy.Panic(err)
	}

	// ProviderConfig is a cluster resource instead of namespaced, so we have to construct a unique
	// name for the object.
	name := namer.UniqueIdentifier(t.Network.ObjectMeta)
	config := &v1beta1.ProviderConfig{
		ObjectMeta: metav1.ObjectMeta{
			Name:   name,
			Labels: t.Namer.Labels(),
		},
	}
	_, err = controllerutil.CreateOrUpdate(ctx, t.Local, config, func() error {
		if config.Annotations == nil {
			config.Annotations = make(map[string]string)
		}
		config.Spec = v1beta1.ProviderConfigSpec{
			ProjectID: networkSpec.ProjectID,
			Credentials: v1beta1.ProviderCredentials{
				Source: v1.CredentialsSourceInjectedIdentity,
			},
		}
		return nil
	})
	return deploy.ErrorOrDone(err)
}

func (t *ProviderConfig) PostCreate(ctx context.Context) deploy.Result {
	var c v1beta1.ProviderConfig
	key := types.NamespacedName{
		Name: namer.UniqueIdentifier(t.Network.ObjectMeta),
	}
	err := t.Local.Get(ctx, key, &c)
	return deploy.ErrorOrDone(err)
}

func (t *ProviderConfig) Delete(ctx context.Context) deploy.Result {
	name := namer.UniqueIdentifier(t.Network.ObjectMeta)
	config := &v1beta1.ProviderConfig{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}
	return deploy.ErrorOrDone(k8s.DeleteObject(ctx, t.Local, config))
}

func (t *ProviderConfig) PostDelete(ctx context.Context) deploy.Result {
	var c v1beta1.ProviderConfig
	key := types.NamespacedName{
		Name: namer.UniqueIdentifier(t.Network.ObjectMeta),
	}
	return deploy.ErrorOrDone(k8s.GetDeletedObject(ctx, t.Local, key, &c))
}
