package jumper

import (
	"context"
	"fmt"

	jpent "github.com/tidbcloud/devops/entity/jump"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	"github.com/tidbcloud/devops/pkg/service/jump/jumppod/types"

	"k8s.io/client-go/tools/clientcmd/api"
)

type ServerlessJumper struct {
	registry         *DestinationRegistry
	k8sJumperFactory *K8sJumperFactory

	configs *container.Map[string, *api.Config] // TODO: clean cache
}

func NewServerlessJumper(dep JumperDependency) (Jumper, error) {
	j := &ServerlessJumper{
		registry:         dep.DestinationRegistry,
		k8sJumperFactory: dep.K8sJumperFactory,
		configs:          container.NewMap[string, *api.Config](),
	}
	return j, nil
}

func (j *ServerlessJumper) PrepareJump(ctx context.Context, jp JumpPodForJumper, idest jpent.IDestination, param *types.PrepareJumpParam) (*types.JumpParam, error) {
	dest, ok := jpent.AsDestinationServerlessCluster(idest)
	if !ok {
		return nil, fmt.Errorf("invalid destination type %s", idest.Type())
	}

	// load or create cluster config
	config, err := j.loadClusterConfig(dest)
	if err != nil {
		return nil, fmt.Errorf("load config: %w", err)
	}

	// proxy to k8s jumper
	k8sJumper, err := j.k8sJumperFactory.NewK8sTokenJumper(*config)
	if err != nil {
		return nil, fmt.Errorf("new k8s jumper: %w", err)
	}
	return k8sJumper.PrepareJump(ctx, jp, dest, param, K8sJumpOption{})
}

func (j *ServerlessJumper) FinalizeJump(ctx context.Context, jp JumpPodForJumper, idest jpent.IDestination, attr jpent.JumpAttributes) error {
	dest, ok := jpent.AsDestinationServerlessCluster(idest)
	if !ok {
		return fmt.Errorf("invalid destination type %s", idest.Type())
	}

	// load or create cluster config
	config, err := j.loadClusterConfig(dest)
	if err != nil {
		return fmt.Errorf("load config: %w", err)
	}

	// proxy to k8s jumper
	k8sJumper, err := j.k8sJumperFactory.NewK8sTokenJumper(*config)
	if err != nil {
		return fmt.Errorf("new k8s jumper: %w", err)
	}
	return k8sJumper.FinalizeJump(ctx, jp, dest)
}

func (j *ServerlessJumper) RefreshJump(ctx context.Context, jp JumpPodForJumper, idest jpent.IDestination, attr jpent.JumpAttributes, param *types.RefreshJumpParam) (*types.JumpParam, error) {
	dest, ok := jpent.AsDestinationServerlessCluster(idest)
	if !ok {
		return nil, fmt.Errorf("invalid destination type %s", idest.Type())
	}

	// load or create cluster config
	config, err := j.loadClusterConfig(dest)
	if err != nil {
		return nil, fmt.Errorf("load config: %w", err)
	}

	// proxy to k8s jumper
	k8sJumper, err := j.k8sJumperFactory.NewK8sTokenJumper(*config)
	if err != nil {
		return nil, fmt.Errorf("new k8s jumper: %w", err)
	}
	return k8sJumper.RefreshJump(ctx, jp, dest, attr, param, K8sJumpOption{})
}

func (j *ServerlessJumper) SupportedDestinations() (jpent.DestinationType, []string) {
	typ := jpent.DestinationTypeServerless
	names := j.registry.RegisteredDestinations(typ)
	return typ, names
}

func (j *ServerlessJumper) loadClusterConfig(dest *jpent.DestinationServerlessCluster) (*api.Config, error) {
	key := dest.Name

	config, exist := j.configs.Load(key)
	if exist {
		return config, nil
	}

	config, err := j.registry.LoadConfig(jpent.DestinationTypeServerless, string(dest.Name))
	if err != nil {
		return nil, fmt.Errorf("load config: %w", err)
	}

	j.configs.Store(key, config)
	return config, nil
}
