package sort

import (
	"fmt"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/context"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/recconf"
)

var sortMapping = make(map[string]ISort)
var sortService *SortService

// var sortMapping map[string]ISort

func init() {
	sortService = &SortService{}
	sortService.SortStrategies = make(map[string][]ISort, 0)
}

type SortData struct {
	Data    interface{}
	Context *context.RecommendContext
}
type ISort interface {
	Sort(sortData *SortData) error
}

type SortService struct {
	SortStrategies map[string][]ISort
}

func (ss *SortService) AddSort(scene string, s ISort) {
	sorts, ok := ss.SortStrategies[scene]
	if ok {
		for _, sort := range sorts {
			if sort == s {
				return
			}
		}

		sorts = append(sorts, s)
		ss.SortStrategies[scene] = sorts
	} else {
		sorts := []ISort{s}
		ss.SortStrategies[scene] = sorts
	}
}

func (ss *SortService) Sort(data *SortData, tag string) {
	var scene string
	ctx := data.Context
	s := ctx.GetParameter("scene")
	if _, ok := s.(string); ok {
		scene = s.(string)
	}
	var categoryName string
	c := ctx.GetParameter("category")
	if _, ok := c.(string); ok {
		categoryName = c.(string)
	} else {
		categoryName = "default"
	}

	sorts := make([]ISort, 0)
	if ctx.ExperimentResult != nil {
		names := ctx.ExperimentResult.GetLayerParams("sort").Get(categoryName+".SortNames", nil)
		if names != nil {
			if values, ok := names.([]interface{}); ok {
				for _, v := range values {
					if name, okay := v.(string); okay {
						if sort, found := sortMapping[name]; found {
							sorts = append(sorts, sort)
						}
					}
				}
			}
		}
	}

	if len(sorts) == 0 {
		scene = scene + tag
		var ok bool
		sorts, ok = ss.SortStrategies[scene]
		if !ok {
			sorts, ok = ss.SortStrategies[categoryName]
		}
		if !ok || sorts == nil || len(sorts) == 0 {
			sorts = make([]ISort, 1)
			sorts[0] = NewItemRankScoreSort()
			ctx.LogInfo(fmt.Sprintf("defaultSort=ItemRankScore\tscene=%s", scene))
		}
	}

	for _, sort := range sorts {
		sort.Sort(data)
	}
}

func Load(config *recconf.RecommendConfig) {
	for scene, names := range config.SortNames {
		for _, name := range names {
			if sort, ok := sortMapping[name]; ok {
				sortService.AddSort(scene, sort)
			}
		}
	}
}

func Sort(sortData *SortData, tag string) {
	sortService.Sort(sortData, tag)
}

func RegisterSort(name string, s ISort) {
	if s == nil {
		panic("Sort is nil, name:" + name)
	}
	sortMapping[name] = s
}
