package k8s

import (
	"context"
	"encoding/base64"
	"fmt"
	stdlog "log"

	"helm.sh/helm/v3/pkg/action"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/utils/pointer"
	"sigs.k8s.io/controller-runtime/pkg/client"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
)

const (
	// Namespace for Helm to store data.
	HelmNamespace = "helm"
)

type ClientGetter interface {
	GetClient(context.Context) (client.Client, error)
	GetHelmConfig(context.Context) (*action.Configuration, error)
}

type ClusterMetaGetter interface {
	client.Object
	GetClusterName() string
	GetRegion() string
	GetProvider() string
	GetNetwork() *cloudv1.Network
}

type EmptyGetter struct{}

var _ ClientGetter = &EmptyGetter{}

func (g *EmptyGetter) GetClient(context.Context) (client.Client, error) {
	return nil, fmt.Errorf("empty getter")
}
func (g *EmptyGetter) GetHelmConfig(context.Context) (*action.Configuration, error) {
	return nil, fmt.Errorf("empty getter")
}

// KubeconfigClientGetter implements ClientGetter from base64-encoded kubeconfig
type KubeconfigClientGetter struct {
	cli       client.Client
	actionCfg *action.Configuration
}

var _ ClientGetter = &KubeconfigClientGetter{}

// NewKubeconfigClientGetter creates a ClientGetter from base64-encoded kubeconfig string
func NewKubeconfigClientGetter(kubeconfigBase64 string) (*KubeconfigClientGetter, error) {
	// Decode base64 kubeconfig
	decoded, err := base64.StdEncoding.DecodeString(kubeconfigBase64)
	if err != nil {
		return nil, fmt.Errorf("failed to decode kubeconfig: %w", err)
	}

	// Create rest.Config from kubeconfig
	restCfg, err := clientcmd.RESTConfigFromKubeConfig(decoded)
	if err != nil {
		return nil, fmt.Errorf("failed to create rest config from kubeconfig: %w", err)
	}

	// Create controller-runtime client
	cli, err := client.New(restCfg, client.Options{})
	if err != nil {
		return nil, fmt.Errorf("failed to create k8s client: %w", err)
	}

	// Initialize helm action.Configuration
	configFlags := genericclioptions.NewConfigFlags(false)
	configFlags.APIServer = &restCfg.Host
	configFlags.Insecure = pointer.Bool(restCfg.TLSClientConfig.Insecure)
	configFlags.WrapConfigFn = func(_ *rest.Config) *rest.Config {
		return restCfg
	}

	actionCfg := &action.Configuration{}
	if err := actionCfg.Init(configFlags, HelmNamespace, "configmap", stdlog.Printf); err != nil {
		return nil, fmt.Errorf("failed to init helm config: %w", err)
	}

	return &KubeconfigClientGetter{
		cli:       cli,
		actionCfg: actionCfg,
	}, nil
}

func (g *KubeconfigClientGetter) GetClient(context.Context) (client.Client, error) {
	return g.cli, nil
}

func (g *KubeconfigClientGetter) GetHelmConfig(context.Context) (*action.Configuration, error) {
	// Reset capabilities to nil to avoid the api version cache
	g.actionCfg.Capabilities = nil
	return g.actionCfg, nil
}
