package gke

import (
	"context"
	"os"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"github.com/tidbcloud/eks-provider/controllers/tiflow/task"
	mastermgr "github.com/tidbcloud/eks-provider/controllers/tiflow/task/master_mgr"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
)

// NOTE: For gke, since we need to access the rds by public ip, we will use this secret to
// pass the private key of the service account which has been attached to the rds before
// to enable secure access though cloud SQL proxy

type RDSSecret struct {
	*mastermgr.BaseTask

	name string
}

// NewRDSSecret create secret for rds in dataplane
func NewRDSSecret(ctx *mastermgr.Context) *RDSSecret {
	return &RDSSecret{
		BaseTask: &mastermgr.BaseTask{Context: ctx},
		name:     task.RDSSecretName,
	}
}

var _ deploy.Task = (*RDSSecret)(nil)

// Name implements deploy.Task.Name interface.
func (s *RDSSecret) Name() string {
	return "RDSSecret"
}

// Create implements deploy.Task interface.
func (s *RDSSecret) Create(ctx context.Context) deploy.Result {
	remote, err := s.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Error(err)
	}

	secret := s.getObject()
	_, err = controllerutil.CreateOrUpdate(ctx, remote, secret, func() error {
		secret.Type = corev1.SecretTypeOpaque
		secret.Data = map[string][]byte{
			task.RDSSecretKey: []byte(os.Getenv(task.RDSSecretEnvKey)),
		}
		return nil
	})
	return deploy.ErrorOrDone(err)
}

// PostCreate implements deploy.Task interface.
func (s *RDSSecret) PostCreate(ctx context.Context) deploy.Result {
	_, err := s.getSecret(ctx)
	return deploy.ErrorOrDone(err)
}

// Delete implements deploy.Task interface.
func (s *RDSSecret) Delete(ctx context.Context) deploy.Result {
	remote, err := s.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Error(err)
	}

	return deploy.ErrorOrDone(k8s.DeleteObject(ctx, remote, s.getObject()))
}

// PostDelete implements deploy.Task interface.
func (s *RDSSecret) PostDelete(ctx context.Context) deploy.Result {
	remote, err := s.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Error(err)
	}

	secret := s.getObject()
	return deploy.ErrorOrDone(k8s.GetDeletedObject(ctx, remote, client.ObjectKeyFromObject(secret), secret))
}

func (s *RDSSecret) getObject() *corev1.Secret {
	return &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: s.Namer.GetTiflowNamespace(),
			Name:      s.name,
		},
	}
}

func (s *RDSSecret) getSecret(ctx context.Context) (*corev1.Secret, error) {
	remote, err := s.Remote.GetClient(ctx)
	if err != nil {
		return nil, err
	}

	secret := s.getObject()
	err = remote.Get(ctx, client.ObjectKeyFromObject(secret), secret)
	if err != nil {
		return nil, err
	}
	return secret, nil
}
