package module

import (
	"math"

	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/recconf"
	"gitlab.alibaba-inc.com/pai_biz_arch/pairec/utils"
)

type FilterOp interface {
	Evaluate(map[string]interface{}) (bool, error)
}

type EqualFilterOp struct {
	Name  string
	Type  string
	Value interface{}
}

func NewEqualFilterOp(config recconf.FilterParamConfig) *EqualFilterOp {

	return &EqualFilterOp{
		Name:  config.Name,
		Type:  config.Type,
		Value: config.Value,
	}
}

func (p *EqualFilterOp) Evaluate(properties map[string]interface{}) (bool, error) {

	left, ok := properties[p.Name]
	if !ok {
		return false, nil
	}

	switch p.Type {
	case "string":
		v1 := utils.ToString(left, "v1")
		v2 := utils.ToString(p.Value, "v2")

		return v1 == v2, nil
	case "int":
		v1 := utils.ToInt(left, -1)
		v2 := utils.ToInt(p.Value, -2)
		return v1 == v2, nil
	case "int64":
		v1 := utils.ToInt64(left, -1)
		v2 := utils.ToInt64(p.Value, -2)
		return v1 == v2, nil
	default:
		return false, nil
	}
}

type NotEqualFilterOp struct {
	Name  string
	Type  string
	Value interface{}
}

func NewNotEqualFilterOp(config recconf.FilterParamConfig) *NotEqualFilterOp {

	return &NotEqualFilterOp{
		Name:  config.Name,
		Type:  config.Type,
		Value: config.Value,
	}
}

func (p *NotEqualFilterOp) Evaluate(properties map[string]interface{}) (bool, error) {

	left, ok := properties[p.Name]
	if !ok {
		return true, nil
	}

	switch p.Type {
	case "string":
		v1 := utils.ToString(left, "")
		v2 := utils.ToString(p.Value, "")

		return v1 != v2, nil
	case "int":
		v1 := utils.ToInt(left, 0)
		v2 := utils.ToInt(p.Value, 0)
		return v1 != v2, nil
	case "int64":
		v1 := utils.ToInt64(left, 0)
		v2 := utils.ToInt64(p.Value, 0)
		return v1 != v2, nil
	default:
		return false, nil
	}
}

type InFilterOp struct {
	Name          string
	Type          string
	int_values    []int
	string_values []string
}

func NewInFilterOp(config recconf.FilterParamConfig) *InFilterOp {

	op := &InFilterOp{
		Name: config.Name,
		Type: config.Type,
	}

	values, ok := config.Value.([]interface{})
	if !ok {
		panic("InFilterOp type error")

	}
	switch op.Type {
	case "int":
		for _, val := range values {
			switch value := val.(type) {
			case float64:
				op.int_values = append(op.int_values, int(value))
			case int:
				op.int_values = append(op.int_values, value)
			case int32:
				op.int_values = append(op.int_values, int(value))
			case int64:
				op.int_values = append(op.int_values, int(value))
			}
		}

	case "string":
		for _, val := range values {
			if value, ok := val.(string); ok {
				op.string_values = append(op.string_values, value)
			}
		}
	}

	return op
}

func (p *InFilterOp) Evaluate(properties map[string]interface{}) (bool, error) {

	left, ok := properties[p.Name]
	if !ok {
		return false, nil
	}

	switch p.Type {
	case "string":
		v1 := utils.ToString(left, "")
		for _, val := range p.string_values {
			if v1 == val {
				return true, nil
			}
		}
	case "int":
		v1 := utils.ToInt(left, math.MinInt32)
		for _, val := range p.int_values {
			if v1 == val {
				return true, nil
			}
		}
	}
	return false, nil
}

type FilterParam struct {
	operators []FilterOp
}

func NewFitlerParamWithConfig(configs []recconf.FilterParamConfig) *FilterParam {
	p := FilterParam{
		operators: make([]FilterOp, 0, 4),
	}

	for _, config := range configs {
		if config.Operator == "equal" {
			p.operators = append(p.operators, NewEqualFilterOp(config))
		} else if config.Operator == "not_equal" {
			p.operators = append(p.operators, NewNotEqualFilterOp(config))
		} else if config.Operator == "in" {
			p.operators = append(p.operators, NewInFilterOp(config))
		} else if config.Operator == "greater" {
			p.operators = append(p.operators, NewGreaterFilterOp(config))
		} else if config.Operator == "greaterThan" {
			p.operators = append(p.operators, NewGreaterThanFilterOp(config))
		} else if config.Operator == "less" {
			p.operators = append(p.operators, NewLessFilterOp(config))
		} else if config.Operator == "lessThan" {
			p.operators = append(p.operators, NewLessThanFilterOp(config))
		}
	}

	return &p
}

func (p *FilterParam) Evaluate(properties map[string]interface{}) (bool, error) {
	for _, op := range p.operators {
		ret, err := op.Evaluate(properties)
		if ret == false || err != nil {
			return false, err
		}
	}
	return true, nil
}

type GreaterFilterOp struct {
	Name  string
	Type  string
	Value interface{}
}

func NewGreaterFilterOp(config recconf.FilterParamConfig) *GreaterFilterOp {

	return &GreaterFilterOp{
		Name:  config.Name,
		Type:  config.Type,
		Value: config.Value,
	}
}

func (p *GreaterFilterOp) Evaluate(properties map[string]interface{}) (bool, error) {

	left, ok := properties[p.Name]
	if !ok {
		return false, nil
	}

	switch p.Type {
	case "float":
		v1 := utils.ToFloat(left, 0)
		v2 := utils.ToFloat(p.Value, 0)

		return v1 > v2, nil
	case "int":
		v1 := utils.ToInt(left, 0)
		v2 := utils.ToInt(p.Value, 0)
		return v1 > v2, nil
	case "int64":
		v1 := utils.ToInt64(left, 0)
		v2 := utils.ToInt64(p.Value, 0)
		return v1 > v2, nil
	default:
		return false, nil
	}
}

type GreaterThanFilterOp struct {
	Name  string
	Type  string
	Value interface{}
}

func NewGreaterThanFilterOp(config recconf.FilterParamConfig) *GreaterThanFilterOp {

	return &GreaterThanFilterOp{
		Name:  config.Name,
		Type:  config.Type,
		Value: config.Value,
	}
}

func (p *GreaterThanFilterOp) Evaluate(properties map[string]interface{}) (bool, error) {

	left, ok := properties[p.Name]
	if !ok {
		return false, nil
	}

	switch p.Type {
	case "float":
		v1 := utils.ToFloat(left, 0)
		v2 := utils.ToFloat(p.Value, 0)

		return v1 >= v2, nil
	case "int":
		v1 := utils.ToInt(left, 0)
		v2 := utils.ToInt(p.Value, 0)
		return v1 >= v2, nil
	case "int64":
		v1 := utils.ToInt64(left, 0)
		v2 := utils.ToInt64(p.Value, 0)
		return v1 >= v2, nil
	default:
		return false, nil
	}
}

type LessFilterOp struct {
	Name  string
	Type  string
	Value interface{}
}

func NewLessFilterOp(config recconf.FilterParamConfig) *LessFilterOp {

	return &LessFilterOp{
		Name:  config.Name,
		Type:  config.Type,
		Value: config.Value,
	}
}

func (p *LessFilterOp) Evaluate(properties map[string]interface{}) (bool, error) {

	left, ok := properties[p.Name]
	if !ok {
		return false, nil
	}

	switch p.Type {
	case "float":
		v1 := utils.ToFloat(left, 0)
		v2 := utils.ToFloat(p.Value, 0)

		return v1 < v2, nil
	case "int":
		v1 := utils.ToInt(left, 0)
		v2 := utils.ToInt(p.Value, 0)
		return v1 < v2, nil
	case "int64":
		v1 := utils.ToInt64(left, 0)
		v2 := utils.ToInt64(p.Value, 0)
		return v1 < v2, nil
	default:
		return false, nil
	}
}

type LessThanFilterOp struct {
	Name  string
	Type  string
	Value interface{}
}

func NewLessThanFilterOp(config recconf.FilterParamConfig) *LessThanFilterOp {

	return &LessThanFilterOp{
		Name:  config.Name,
		Type:  config.Type,
		Value: config.Value,
	}
}

func (p *LessThanFilterOp) Evaluate(properties map[string]interface{}) (bool, error) {

	left, ok := properties[p.Name]
	if !ok {
		return false, nil
	}

	switch p.Type {
	case "float":
		v1 := utils.ToFloat(left, 0)
		v2 := utils.ToFloat(p.Value, 0)

		return v1 <= v2, nil
	case "int":
		v1 := utils.ToInt(left, 0)
		v2 := utils.ToInt(p.Value, 0)
		return v1 <= v2, nil
	case "int64":
		v1 := utils.ToInt64(left, 0)
		v2 := utils.ToInt64(p.Value, 0)
		return v1 <= v2, nil
	default:
		return false, nil
	}
}
