package clientfake

import (
	"context"
	"time"

	"github.com/tidbcloud/aws-shared-provider/pkg/tidbpool"
	"github.com/tidbcloud/aws-shared-provider/pkg/utils"
	ep "github.com/tidbcloud/aws-shared-provider/pkg/utils/endpoint"
	"github.com/tidbcloud/aws-shared-provider/pkg/utils/tidb"
	corev1 "k8s.io/api/core/v1"
)

type TokenPod struct {
	Token string
	Pod   string
}

type ActiveToken struct {
	Token     string
	Endpoints []string
	Replicas  int
}

type FakeTidbManager struct {
	GetTidbFromFreeCacheR           map[string][]string
	GetOldTidbFromFreeCacheR        []string
	GetNewTidbFromFreeCacheR        []string
	OldTidbFromAssignCache          []TokenPod
	FreeCacheR                      map[string]string
	FreeCacheSizeR                  int
	AssignCacheSizeTokenR           int
	AssignCacheSizeEndpointR        int
	WalkActiveTokenFromAssignCacheR []ActiveToken
	GetAssignedTidbByTokenR         map[string]*ep.Endpoints

	TidbPool tidbpool.Pool

	tidbScaler utils.Scaler

	ReplaceTidbFn func(ctx context.Context, podName string) error

	TryCreatePodFn func(ctx context.Context, pod *corev1.Pod) error
}

func (ft *FakeTidbManager) Start(ctx context.Context) error {
	return nil
}

func (ft *FakeTidbManager) AssignTidb(
	ctx context.Context,
	token string,
	param *tidb.ActivateParameter,
) ([]string, error) {
	return nil, nil
}

func (ft *FakeTidbManager) FreeTidb(ctx context.Context, podName, podIP, comment string) error {
	return nil
}

func (ft *FakeTidbManager) CheckTidb(ctx context.Context, token, podIP string) error {
	return nil
}

func (ft *FakeTidbManager) SyncCache(
	ctx context.Context, nodeName, podName, podIP string, podStatus utils.PodStatus,
) error {
	return nil
}

func (ft *FakeTidbManager) GetTidbPool() tidbpool.Pool { return ft.TidbPool }

func (ft *FakeTidbManager) GetTidbFromFreeCache(node string, includeActivating bool) []string {
	if node == "" {
		totalGetTidbFromFreeCacheR := make([]string, 0)
		for _, getTidbFromFreeCacheR := range ft.GetTidbFromFreeCacheR {
			totalGetTidbFromFreeCacheR = append(totalGetTidbFromFreeCacheR, getTidbFromFreeCacheR...)
		}
		return totalGetTidbFromFreeCacheR
	}
	return ft.GetTidbFromFreeCacheR[node]
}

func (ft *FakeTidbManager) GetOldTidbFromFreeCache(newRevision string, includeActivating bool) []string {
	return ft.GetOldTidbFromFreeCacheR
}

func (ft *FakeTidbManager) GetTidbFromFreeCacheByRevision(revision string, includeActivating bool) []string {
	return ft.GetNewTidbFromFreeCacheR
}

func (ft *FakeTidbManager) GetOldTidbFromAssignCache(newRevision string, limit int) (map[string][]string, int) {
	oldTidbFromAssignCache := ft.OldTidbFromAssignCache
	if limit > len(oldTidbFromAssignCache) {
		limit = len(oldTidbFromAssignCache)
	}
	if limit >= 0 {
		oldTidbFromAssignCache = oldTidbFromAssignCache[:limit]
	}

	rlt := map[string][]string{}
	for _, tokenPod := range oldTidbFromAssignCache {
		rlt[tokenPod.Token] = append(rlt[tokenPod.Token], tokenPod.Pod)
	}
	return rlt, len(ft.OldTidbFromAssignCache)
}

func (ft *FakeTidbManager) WalkActiveTokenFromAssignCache(f func(token string, endpoints []string, replicas int) bool) {
	for _, info := range ft.WalkActiveTokenFromAssignCacheR {
		if !f(info.Token, info.Endpoints, info.Replicas) {
			return
		}
	}
}

func (ft *FakeTidbManager) LockFreeCache() {}

func (ft *FakeTidbManager) UnlockFreeCache() {}

func (ft *FakeTidbManager) FreeCacheSize() int {
	return ft.FreeCacheSizeR
}

func (ft *FakeTidbManager) AssignCacheSize() (token, endpoint int) {
	return ft.AssignCacheSizeTokenR, ft.AssignCacheSizeEndpointR
}

func (ft *FakeTidbManager) FilterOrphanPods(inactivePods []string) []string {
	var res []string
	for _, podName := range inactivePods {
		if ft.FreeCacheR[podName] != "" {
			continue
		}
		res = append(res, podName)
	}
	return res
}

func (ft *FakeTidbManager) DumpStatus() map[string][]interface{} {
	return nil
}

func (ft *FakeTidbManager) SetTidbScaler(s utils.Scaler) { ft.tidbScaler = s }

func (ft *FakeTidbManager) GetTidbScaler() utils.Scaler { return ft.tidbScaler }

func (ft *FakeTidbManager) KillAll(ctx context.Context, token string, timeout time.Duration) error {
	return nil
}

func (ft *FakeTidbManager) GetAssignedTidbByToken(token string) *ep.Endpoints {
	return ft.GetAssignedTidbByTokenR[token]
}

func (ft *FakeTidbManager) GetAssignedToken(podName, podIP string) string {
	for token, tidb := range ft.GetAssignedTidbByTokenR {
		for _, endpoint := range tidb.Endpoints {
			if endpoint == ep.GenEndpointFromPod(podName, podIP) {
				return token
			}
		}
	}
	return ""
}

func (ft *FakeTidbManager) ReplaceTidb(ctx context.Context, podName string, deleteFn func(ctx context.Context, podName, namespace string) error) error {
	return ft.ReplaceTidbFn(ctx, podName)
}

func (ft *FakeTidbManager) TryCreatePod(ctx context.Context) error {
	pod := ft.TidbPool.GetPool(ctx).TidbPod.DeepCopy()
	return ft.TryCreatePodFn(ctx, pod)
}
