/*

 Copyright (c) [2023] [pcm]
 [pcm-coordinator] is licensed under Mulan PSL v2.
 You can use this software according to the terms and conditions of the Mulan PSL v2.
 You may obtain a copy of Mulan PSL v2 at:
          http://license.coscl.org.cn/MulanPSL2
 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 EITHER EXPaRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 See the Mulan PSL v2 for more details.

*/

package strategy

import (
	"errors"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler/algorithm/providerPricing"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler/strategy/param"
	"strconv"
)

type PricingStrategy struct {
	ProviderList []*providerPricing.Provider
	Task         *providerPricing.Task
	StrategyList []*providerPricing.Strategy
}

func NewPricingStrategy(params *param.ResourcePricingParams) *PricingStrategy {
	providers := params.GetProviders()
	task := params.GetTask()
	var providerList []*providerPricing.Provider
	var res [][]int

	for _, p := range providers {
		p.GenMaxResourceNum(task)
		providerList = append(providerList, p)
	}

	providerPricing.Back_trace_task(task.Replicas, 0, providerList, 0, &res, 0)

	var strategyList []*providerPricing.Strategy
	for _, r := range res {
		var path []int
		var pathlist [][]int

		var resourcePerProvider []int
		for j, p := range providerList {
			if r[j] > p.MaxReplicas {
				resourcePerProvider = append(resourcePerProvider, p.MaxReplicas)
			} else {
				resourcePerProvider = append(resourcePerProvider, r[j])
			}
		}
		providerPricing.Back_trace_resource(resourcePerProvider, 0, path, &pathlist)
		strategy := providerPricing.NewStrategy()
		strategy.Tasksolution = r
		strategy.Resourcesolution = pathlist
		strategyList = append(strategyList, strategy)
	}

	return &PricingStrategy{ProviderList: providerList, Task: task, StrategyList: strategyList}
}

func (ps *PricingStrategy) computeMaxScore() (*providerPricing.Task, error) {
	maxStrategy := providerPricing.NewStrategy()
	var maxprofit float64

	//先计算出最大的利润值
	for _, strategy := range ps.StrategyList {
		for _, resourceSolu := range strategy.Resourcesolution {
			profit := providerPricing.ComputeProfit(ps.Task, strategy.Tasksolution, resourceSolu, ps.ProviderList)
			if profit > maxprofit {
				maxprofit = profit
			}
		}
	}

	for _, strategy := range ps.StrategyList {
		for _, resourceSolu := range strategy.Resourcesolution {
			profit := providerPricing.ComputeProfit(ps.Task, strategy.Tasksolution, resourceSolu, ps.ProviderList)
			highDegree := providerPricing.ComputeHighDegree(ps.Task, resourceSolu, ps.ProviderList)

			valueSum := profit/maxprofit + highDegree

			//将每个确定任务分配策略的最高的策略得分存储到里面
			if valueSum > maxStrategy.ValueSum {
				strategy.Profit = profit
				strategy.HighDegree = highDegree
			}

			if valueSum > maxStrategy.ValueSum {
				maxStrategy.ValueSum = valueSum
				maxStrategy.Tasksolution = strategy.Tasksolution
				newResourceSolu := [][]int{}
				newResourceSolu = append(newResourceSolu, resourceSolu)
				maxStrategy.Resourcesolution = newResourceSolu
				maxStrategy.Profit = profit
				maxStrategy.HighDegree = highDegree
			}
		}
	}

	if len(ps.ProviderList) == 0 {
		return nil, errors.New("empty providers")
	}

	ps.Task.MaxscoreStrategy = maxStrategy // 记录该任务的最终分配策略
	return ps.Task, nil
}

func (ps *PricingStrategy) produceMaxScoreStrategy() (*providerPricing.Strategy, error) {
	task, err := ps.computeMaxScore()
	if err != nil {
		return nil, err
	}

	//计算任务i的resourcePerTask属性
	for i, _ := range ps.ProviderList {
		tasksolu := task.MaxscoreStrategy.Tasksolution[i]            // 第j个提供商分到的任务数
		resourcesolu := task.MaxscoreStrategy.Resourcesolution[0][i] // 第j个提供商分到的资源数

		// 在第j个云提供商处声明一个长度为资源数的链表
		resourcePerTaskPerProviders := make([]int, resourcesolu)
		if tasksolu > 0 {
			for tasksolu > 0 {
				for j := 0; j < resourcesolu; j++ {
					resourcePerTaskPerProviders[j] += 1
					tasksolu -= 1
				}
			}
		} else if tasksolu == 0 {
			resourcePerTaskPerProviders = []int{0}
		}

		task.ResourcePerTask = append(task.ResourcePerTask, resourcePerTaskPerProviders)
	}

	return task.MaxscoreStrategy, nil
}

func (ps *PricingStrategy) Schedule() ([]*AssignedCluster, error) {
	strategy, err := ps.produceMaxScoreStrategy()
	if err != nil {
		return nil, err
	}

	if len(strategy.Tasksolution) == 0 {
		return nil, errors.New("调度失败， 未能获取调度结果")
	}

	var results []*AssignedCluster
	for i, e := range strategy.Tasksolution {
		if e == 0 {
			continue
		}
		cluster := &AssignedCluster{ClusterId: strconv.FormatInt(ps.ProviderList[i].Pid, 10), Replicas: int32(e)}
		results = append(results, cluster)
	}

	if len(results) == 0 {
		return nil, errors.New("可用集群为空")
	}

	return results, nil
}
