package filter

import (
	"fmt"

	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/context"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/log"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/module"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/recconf"
)

var filterMapping = make(map[string]IFilter)

var filterService *FilterService

func init() {
	filterService = &FilterService{}
	filterService.Filters = make(map[string][]IFilter)
}

type FilterData struct {
	Uid     module.UID
	Data    interface{}
	Context *context.RecommendContext
}
type IFilter interface {
	Filter(filterData *FilterData) error

	// if match the tag ,can do Filter
	//MatchTag(tag string) bool
}

type FilterService struct {
	Filters map[string][]IFilter
}

func (fs *FilterService) AddFilter(scene string, filter IFilter) {
	// if exist in Filters, direct return
	filters, ok := fs.Filters[scene]
	if ok {
		for _, f := range filters {
			if f == filter {
				return
			}
		}

		filters = append(filters, filter)
		fs.Filters[scene] = filters
	} else {
		filters := []IFilter{filter}
		fs.Filters[scene] = filters
	}
}

func (fs *FilterService) Filter(filterData *FilterData, tag string) {
	context := filterData.Context
	scene := context.GetParameter("scene").(string)

	var filters []IFilter
	if context.ExperimentResult != nil {
		names := context.ExperimentResult.GetExperimentParams().Get("filterNames", nil)
		if names != nil {
			if values, ok := names.([]interface{}); ok {
				for _, v := range values {
					if filterName, ok := v.(string); ok {
						if filter, exist := filterMapping[filterName]; exist {
							filters = append(filters, filter)
						}
					}
				}
			}
		}
	}

	if len(filters) == 0 {
		if filterList, ok := fs.Filters[scene]; ok {
			filters = filterList
		} else {
			filters, _ = fs.Filters["default"]

		}

		if len(filters) == 0 {
			log.Error(fmt.Sprintf("Filters:not find, scene:%s", scene))
			return
		}

	}

	// var err error
	for _, f := range filters {
		f.Filter(filterData)
	}
}

func RegisterFilterWithConfig(config *recconf.RecommendConfig) {
	for _, conf := range config.FilterConfs {
		var f IFilter
		if conf.FilterType == "User2ItemExposureFilter" {
			f = NewUser2ItemExposureFilter(conf)
		} else if conf.FilterType == "User2ItemCustomFilter" {
			f = NewUser2ItemCustomFilter(conf)
		} else if conf.FilterType == "AdjustCountFilter" {
			f = NewAdjustCountFilter(conf)
		} else if conf.FilterType == "PriorityAdjustCountFilter" {
			f = NewPriorityAdjustCountFilter(conf)
		} else if conf.FilterType == "ItemStateFilter" {
			f = NewItemStateFilter(conf)
		}

		RegisterFilter(conf.Name, f)

	}
}

func Load(config *recconf.RecommendConfig) {
	for scene, filterList := range config.FilterNames {
		for _, name := range filterList {
			if filter, ok := filterMapping[name]; ok {
				filterService.AddFilter(scene, filter)
			} else {
				log.Error(fmt.Sprintf("Filter:not find, name:%s", name))
			}
		}
	}
}

func Filter(filterData *FilterData, tag string) {
	filterService.Filter(filterData, tag)
}
func RegisterFilter(name string, filter IFilter) {
	if filter == nil {
		panic("Filter is nil, name:" + name)
	}
	filterMapping[name] = filter
}
