package k8scliv1

import (
	"context"
	"encoding/base64"

	eksv1beta1 "github.com/crossplane-contrib/provider-aws/apis/eks/v1beta1"
	cpv1beta1 "github.com/crossplane-contrib/provider-aws/apis/v1beta1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/observability/agent-control-server/pkg/consts"
	"github.com/tidbcloud/observability/agent-control-server/pkg/util"
)

const (
	v1execConfigAPIVersionV1alpha1 = "client.authentication.k8s.io/v1alpha1" //[v1alpha1 removed]https://github.com/kubernetes/kubernetes/pull/108616
	v1execConfigAPIVersionV1beta1  = "client.authentication.k8s.io/v1beta1"
)

type eksTenantClientManager struct {
	cli    client.Client
	option client.Options
}

func NewEKSClientManager(cli client.Client, opt client.Options) ClientManager {
	return &eksTenantClientManager{
		cli:    cli,
		option: opt,
	}
}

func (t *eksTenantClientManager) GetClient(ctx context.Context, cluster client.ObjectKey) (client.Client, error) {
	restCfg, err := t.GetConfig(ctx, cluster)
	if err != nil {
		return nil, err
	}
	cli, err := client.New(restCfg, t.option)
	if err != nil {
		return nil, err
	}
	return cli, nil
}

func (t *eksTenantClientManager) GetConfig(ctx context.Context, eksKey client.ObjectKey) (*rest.Config, error) {
	eks := &eksv1beta1.Cluster{}
	err := t.cli.Get(ctx, eksKey, eks)
	if err != nil {
		return nil, err
	}

	args := []string{
		"token",
		"--region", *eks.Spec.ForProvider.Region,
		"--cluster-id", eks.Name,
	}

	if eks.Spec.ProviderConfigReference != nil && eks.Spec.ProviderConfigReference.Name != "" {
		providerCfg := &cpv1beta1.ProviderConfig{}
		err := t.cli.Get(ctx, client.ObjectKey{Name: eks.Spec.ProviderConfigReference.Name}, providerCfg)
		if err != nil {
			return nil, err
		}
		if providerCfg.Spec.AssumeRoleARN != nil {
			args = append(args, "--role", *providerCfg.Spec.AssumeRoleARN)
			if providerCfg.Spec.ExternalID != nil {
				args = append(args, "--external-id", *providerCfg.Spec.ExternalID)
			}
		}
	}

	cfg := clientcmdapi.NewConfig()

	cfgctx := clientcmdapi.NewContext()
	cfgctx.Cluster = eks.Name
	cfgctx.AuthInfo = eks.Name

	cluster := clientcmdapi.NewCluster()
	if eks.Status.AtProvider.CertificateAuthorityData != "" {
		ca, err := base64.StdEncoding.DecodeString(eks.Status.AtProvider.CertificateAuthorityData)
		if err != nil {
			return nil, err
		}
		cluster.CertificateAuthorityData = ca
	} else {
		cluster.InsecureSkipTLSVerify = true
	}

	endpointPublicAccess := eks.Spec.ForProvider.ResourcesVpcConfig.EndpointPublicAccess
	// If EKS enable public access, use the public kubeconfig.
	if util.GetAnnotation(eks, consts.AnnotationKeyEKSEndpoint) != "" && (endpointPublicAccess == nil || !*endpointPublicAccess) {
		cluster.Server = eks.Annotations[consts.AnnotationKeyEKSEndpoint]
	} else {
		cluster.Server = eks.Status.AtProvider.Endpoint
	}

	apiVersion := v1execConfigAPIVersionV1alpha1
	// With the release of Kubernetes v1.24.0
	// client.authentication.k8s.io/v1alpha1 has been [removed](https://github.com/kubernetes/kubernetes/pull/108616).
	if eks.Spec.ForProvider.Version != nil {
		k8sVersionGreaterThanEqual124, err := util.CompareVersions(*eks.Spec.ForProvider.Version, ">=", "1.24")
		if err != nil {
			return nil, err
		}
		if k8sVersionGreaterThanEqual124 {
			apiVersion = v1execConfigAPIVersionV1beta1
		}
	}

	auth := clientcmdapi.NewAuthInfo()
	auth.Exec = &clientcmdapi.ExecConfig{
		APIVersion:      apiVersion,
		InteractiveMode: clientcmdapi.NeverExecInteractiveMode,
		Command:         "aws-iam-authenticator",
		Args:            args,
	}

	cfg.Contexts[eks.Name] = cfgctx
	cfg.Clusters[eks.Name] = cluster
	cfg.AuthInfos[eks.Name] = auth
	cfg.CurrentContext = eks.Name

	return clientcmd.NewDefaultClientConfig(*cfg, nil).ClientConfig()
}
