package core

import (
	"fmt"
	"github.com/guo-shaoge/tiflash-autoscale/pkg/meta"
	"go.uber.org/zap"
	"sort"
	"time"
)

const sharePoolName = "SharePool"

type SharePool struct {
	basePool
}

// NewSharePool returns new share pool
func NewSharePool() *SharePool {
	res := SharePool{
		basePool: *newBasePool(sharePoolName),
	}
	return &res
}

// TryTake takes pods from SharePool.
// 1. shallow copy map to reduce lock contension.
// 2. iterate podsMap, return pods that not owned by this tenant and SharingRef succeed().
// called by PodMgr.SyncTakePods()
func (s *SharePool) TryTake(tenantName string, num uint,
	shareLowCPULimit float64, shareCoolDownSec uint,
	metricWindowSec uint, metricIntervalSec uint,
	coreNumPerPod uint, stillShareWhenMetricNotEnough bool,
	logger *zap.Logger) []*meta.PodDesc {
	res := make([]*meta.PodDesc, 0, num)
	var resMsg string
	defer func(resPtr *[]*meta.PodDesc) {
		tmpRes := *resPtr
		if len(tmpRes) == 0 {
			logger.Info("cannot take pod from SharePool", zap.Any("msg", resMsg))
		} else {
			logger.Info("take pod from SharePool", zap.Any("len", len(tmpRes)), zap.Any("msg", resMsg))
		}
	}(&res)

	copiedSlice := func() []*meta.PodDesc {
		s.basePool.mu.RLock()
		defer s.basePool.mu.RUnlock()

		if len(s.basePool.podsMap) == 0 {
			return nil
		}

		copiedSlice := make([]*meta.PodDesc, 0, len(s.basePool.podsMap))
		for _, v := range s.basePool.podsMap {
			copiedSlice = append(copiedSlice, v)
		}

		return copiedSlice
	}()

	if len(copiedSlice) == 0 {
		resMsg = "no pods in SharePool"
		return res
	}

	sort.Slice(copiedSlice, func(i, j int) bool {
		return copiedSlice[i].GetTenantCnt() < copiedSlice[j].GetTenantCnt()
	})

	now := time.Now()
	for _, podDesc := range copiedSlice {
		if uint(len(res)) >= num {
			break
		}
		podName := podDesc.GetName()
		// check if it's ok to share. and ref this pod.
		if !podDesc.HasTenant(tenantName) {
			msg, ok, err := podDesc.SharingRef(tenantName, now, shareLowCPULimit, shareCoolDownSec,
				metricWindowSec, metricIntervalSec, coreNumPerPod, stillShareWhenMetricNotEnough)
			if ok {
				res = append(res, podDesc)
				resMsg = fmt.Sprintf("sharing pod: %v;", podName)
			} else {
				resMsg += msg
				if err != nil {
					resMsg += fmt.Sprintf("; err: %v;", err.Error())
				}
			}
		} else {
			resMsg += fmt.Sprintf("skip share pod %s because this tenant(%s) already using this pod;", podName, tenantName)
		}
	}
	return res
}
