package schedulers

import (
	"context"
	"github.com/pkg/errors"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler/algorithm/providerPricing"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler/database"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler/schedulers/option"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/scheduler/strategy"
	"gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc"
	"gitlink.org.cn/JointCloud/pcm-coordinator/pkg/constants"
	"gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models"
	"gitlink.org.cn/JointCloud/pcm-coordinator/pkg/response"
	"gitlink.org.cn/JointCloud/pcm-coordinator/pkg/tracker"
	"gitlink.org.cn/JointCloud/pcm-coordinator/pkg/utils"
	"gorm.io/gorm"
)

type VmScheduler struct {
	yamlString string
	storage    database.Storage
	task       *response.TaskInfo
	*scheduler.Scheduler
	option     *option.VmOption
	ctx        context.Context
	promClient tracker.Prometheus
	dbEngin    *gorm.DB
	svcCtx     *svc.ServiceContext
}

type VmResult struct {
	TaskId      string
	ClusterId   string
	ClusterName string
	Strategy    string
	Replica     int32
	Msg         string
}

func NewVmScheduler(ctx context.Context, val string, scheduler *scheduler.Scheduler, option *option.VmOption, dbEngin *gorm.DB, promClient tracker.Prometheus) (*VmScheduler, error) {
	return &VmScheduler{ctx: ctx, yamlString: val, Scheduler: scheduler, option: option, dbEngin: dbEngin, promClient: promClient}, nil
}

func (vm *VmScheduler) PickOptimalStrategy() (strategy.Strategy, error) {
	if len(vm.option.ClusterIds) == 1 {
		// TODO database operation Find
		return &strategy.SingleAssignment{Cluster: &strategy.AssignedCluster{ClusterId: vm.option.ClusterIds[0], Replicas: 1}}, nil
	}
	//resources, err := vm.findClustersWithResources()

	/*	if err != nil {
		return nil, err
	}*/

	//if len(resources) == 1 {
	//	var cluster strategy.AssignedCluster
	//	cluster.ClusterId = resources[0].ClusterId
	//	cluster.Replicas = 1
	//	return &strategy.SingleAssignment{Cluster: &cluster}, nil
	//}
	//params := &param.Params{Resources: resources}

	switch vm.option.Strategy {
	/*	case strategy.REPLICATION:
			var clusterIds []string
			for _, resource := range resources {
				clusterIds = append(clusterIds, resource.ClusterId)
			}
			strategy := strategy.NewReplicationStrategy(clusterIds, 1)
			return strategy, nil
		case strategy.RESOURCES_PRICING:
			strategy := strategy.NewPricingStrategy(&param.ResourcePricingParams{Params: params, Replicas: 1})
			return strategy, nil*/
	/*	case strategy.DYNAMIC_RESOURCES:
		strategy := strategy.NewDynamicResourcesStrategy(params.Resources, vm.option, 1)
		return strategy, nil*/
	case strategy.STATIC_WEIGHT:
		//todo resources should match cluster StaticWeightMap
		strategy := strategy.NewStaticWeightStrategy(vm.option.StaticWeightMap, 1)
		return strategy, nil
	case strategy.RANDOM:
		strategy := strategy.NewRandomStrategy(vm.option.ClusterIds, vm.option.Replicas)
		return strategy, nil
	}
	return nil, errors.New("no strategy has been chosen")
}

func (v *VmScheduler) GetNewStructForDb(task *response.TaskInfo, resource string, participantId int64) (interface{}, error) {
	//TODO implement me
	vm := models.Vm{}
	utils.Convert(task.Metadata, &vm)
	vm.Id = utils.GenSnowflakeID()
	vm.TaskId = vm.TaskId
	vm.Status = constants.Saved
	vm.ParticipantId = participantId
	return vm, nil
}

func (vm *VmScheduler) genTaskAndProviders() (*providerPricing.Task, []*providerPricing.Provider, error) {
	proParams, err := vm.storage.GetProviderParams()
	if err != nil {
		return nil, nil, nil
	}
	var providerList []*providerPricing.Provider
	for _, p := range proParams {
		provider := providerPricing.NewProvider(p.Participant_id, p.Cpu_avail, p.Mem_avail, p.Disk_avail, 0.0, 0.0, 0.0)
		providerList = append(providerList, provider)
	}

	//replicas := task.Metadata.(map[string]interface{})["spec"].(map[string]interface{})["replicas"].(float64)
	//t := algorithm.NewTask(0, int(replicas), 2, 75120000, 301214500, 1200, 2, 6, 2000)

	return nil, providerList, nil
}

func (as *VmScheduler) AssignTask(clusters []*strategy.AssignedCluster) (interface{}, error) {
	//TODO implement me
	if clusters == nil {
		return nil, errors.New("clusters is nil")
	}

	for i := len(clusters) - 1; i >= 0; i-- {
		if clusters[i].Replicas == 0 {
			clusters = append(clusters[:i], clusters[i+1:]...)
		}
	}

	if len(clusters) == 0 {
		return nil, errors.New("clusters is nil")
	}

	var results []*VmResult

	for _, cluster := range clusters {
		cName := ""
		as.dbEngin.Table("t_cluster").Select("name").Where("id=?", cluster.ClusterId).Find(&cName)
		cr := VmResult{
			ClusterId:   cluster.ClusterId,
			ClusterName: cName,
			Replica:     cluster.Replicas,
		}
		cr.ClusterId = cluster.ClusterId
		cr.Replica = cluster.Replicas

		cr.ClusterName = cName
		results = append(results, &cr)
	}

	return results, nil
}
