package advance

import (
	"fmt"
	"math"
	"sort"
	"strconv"
	"time"
)

const (
	ValueNumber ValueType = "number"
	ValueDate   ValueType = "date"
	ValueChars  ValueType = "chars"
)

const (
	daySec   = 60 * 60 * 24 //一天的秒数
	Infinity = "$"          // 表示无穷

	// 分隔符
	sepAnd = " and "
	sepOr  = " or "
)

const (
	Eq               OpType = "="    //eq =>>in
	LessThanEqual    OpType = "<="   //le
	GreaterThanEqual OpType = ">="   //ge
	Like             OpType = "like" //like
	LessThan         OpType = "<"    //lt
	GreaterThan      OpType = ">"    //gt
	NotEqual         OpType = "<>"   //ne =>> not in

	NotIn  OpType = "not in"
	In     OpType = "in"     //in
	Range  OpType = "range"  //range
	Recent OpType = "recent" //recent 近几天
)

type (
	ValueType string
	OpType    string
	Column    struct {
		// 字段 userName
		Column string `json:"column"`
		// 名称
		Name string `json:"name"`
		// 列别名 对应数据库字段 ext->>'userName'
		DbAlias string `json:"-"`
		// 值类型
		ValueType ValueType `json:"valueType"`
	}
	Expr struct {
		// 操作符
		OpChar OpType `json:"oc"`
		// 如果 OpChar=range ,LeftOp、RightOp需要有值
		LeftOp  OpType `json:"loc"` // "<= <"
		RightOp OpType `json:"roc"` // ">= >"
		// 值
		Value []interface{} `json:"values"`
	}
	ExprResult struct {
		// 操作符
		OpChar OpType
		// 值类型
		ValueType ValueType
		// 值
		Value []interface{}
		// 如果 OpType=range ,LeftOp、RightOp需要有值
		LeftOp  OpType
		RightOp OpType
	}
)

type (
	exprCompute interface {
		Append(result []ExprResult) []ExprResult
	}
	sqlGenerator interface {
		SqlGen(q ColumnExprResult) string
	}
	InExprCompute struct {
		expr      []Expr
		valueType ValueType
		OpType    OpType
	}
	RangeNumberExprCompute struct {
		expr      []Expr
		valueType ValueType
	}
	RecentDateExprCompute struct {
		expr      []Expr
		valueType ValueType
	}
	NotEqualExprCompute struct {
		expr      []Expr
		valueType ValueType
		OpType    OpType
	}
)

// in合并
func NewInExprCompute(expr []Expr, valueType ValueType) exprCompute {
	inExpr := InExprCompute{valueType: valueType}
	for i := 0; i < len(expr); i++ {
		if expr[i].OpChar == Eq || expr[i].OpChar == In {
			inExpr.expr = append(inExpr.expr, expr[i])
		}
	}
	if len(inExpr.expr) > 0 {
		return inExpr
	}
	return nil
}
func (ex InExprCompute) Append(result []ExprResult) []ExprResult {
	var res = ExprResult{
		OpChar:    In,
		ValueType: ex.valueType,
	}
	if ex.OpType != "" {
		res.OpChar = ex.OpType
	}
	for i := range ex.expr {
		res.Value = combine(append(res.Value, ex.expr[i].Value...))
	}
	result = append(result, res)
	return result
}
func combine(arr []interface{}) []interface{} {
	var mapArr = make(map[string]interface{})
	for i := range arr {
		key := fmt.Sprintf("%v", arr[i])
		if _, ok := mapArr[key]; !ok {
			mapArr[key] = arr[i]
		}
	}
	var keys []string
	for k, _ := range mapArr {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	var res []interface{}
	for i := range keys {
		res = append(res, mapArr[keys[i]])
	}
	return res
}

//范围合并
func NewRangeExprCompute(expr []Expr, valueType ValueType) exprCompute {
	rec := RangeNumberExprCompute{valueType: valueType}
	for i := range expr {
		if expr[i].OpChar == Range || expr[i].OpChar == LessThanEqual || expr[i].OpChar == GreaterThanEqual || expr[i].OpChar == LessThan || expr[i].OpChar == GreaterThan {
			rec.expr = append(rec.expr, expr[i])
		}
	}
	if len(rec.expr) == 0 {
		return nil
	}
	var exprSort = exprSortable(rec.expr)
	sort.Sort(exprSort)
	rec.expr = exprSort
	return rec
}
func (ex RangeNumberExprCompute) Append(result []ExprResult) []ExprResult {
	arr := &ExprResult{
		OpChar:    Range,
		ValueType: ex.valueType,
		Value:     ex.expr[0].Value,
		LeftOp:    ex.expr[0].OpChar,
		RightOp:   ex.expr[0].OpChar,
	}
	if len(ex.expr[0].LeftOp) != 0 {
		arr.LeftOp = ex.expr[0].LeftOp
	}
	if len(ex.expr[0].RightOp) != 0 {
		arr.RightOp = ex.expr[0].RightOp
	}
	for i := 1; i < len(ex.expr); i++ {
		if !arr.NumberCompare(ex.expr[i]) {
			result = append(result, *arr)
			arr.Value = ex.expr[i].Value
			arr.LeftOp = ex.expr[i].OpChar
			arr.RightOp = ex.expr[i].OpChar
			//if len(ex.expr[0].LeftOp) != 0 {
			//	arr.LeftOp = ex.expr[0].LeftOp
			//}
			if len(ex.expr[0].RightOp) != 0 {
				arr.RightOp = ex.expr[0].RightOp
			}
			continue
		}
	}
	result = append(result, *arr)
	return result
}

// recent范围
func NewRecentDateExprCompute(expr []Expr, valueType ValueType) exprCompute {
	inExpr := RecentDateExprCompute{valueType: valueType}
	for i := 0; i < len(expr); i++ {
		if expr[i].OpChar == Recent {
			inExpr.expr = append(inExpr.expr, expr[i])
		}
	}
	if len(inExpr.expr) > 0 {
		return inExpr
	}
	return nil
}
func (ex RecentDateExprCompute) Append(result []ExprResult) []ExprResult {
	var res = ExprResult{
		OpChar:    Recent,
		ValueType: ex.valueType,
	}
	var recent int64 = 0
	for i := range ex.expr {
		v, _ := strconv.ParseInt(fmt.Sprintf("%v", ex.expr[i].Value[0]), 10, 64)
		if v > recent {
			recent = v
		}
	}
	res.Value = append(res.Value, []interface{}{time.Now().Unix() - daySec*recent, Infinity})
	result = append(result, res)
	return result
}

// like 合并(跟in操作一致)
func NewLikeExprCompute(expr []Expr, valueType ValueType) exprCompute {
	inExpr := InExprCompute{valueType: valueType, OpType: Like}
	for i := 0; i < len(expr); i++ {
		if expr[i].OpChar == Like {
			inExpr.expr = append(inExpr.expr, expr[i])
		}
	}
	if len(inExpr.expr) > 0 {
		return inExpr
	}
	return nil
}

// not equal合并
func NewNotEqualExprCompute(expr []Expr, valueType ValueType) exprCompute {
	notEqualExpr := NotEqualExprCompute{valueType: valueType, OpType: NotIn}
	for i := 0; i < len(expr); i++ {
		if expr[i].OpChar == NotEqual {
			notEqualExpr.expr = append(notEqualExpr.expr, expr[i])
		}
	}
	if len(notEqualExpr.expr) > 0 {
		return notEqualExpr
	}
	return nil
}
func (ex NotEqualExprCompute) Append(result []ExprResult) []ExprResult {
	var res = ExprResult{
		OpChar:    NotIn,
		ValueType: ex.valueType,
	}
	if ex.OpType != "" {
		res.OpChar = ex.OpType
	}
	for i := range ex.expr {
		res.Value = append(res.Value, ex.expr[i].Value...)
	}
	result = append(result, res)
	return result
}

func (er *ExprResult) NumberCompare(expr Expr) bool {
	if len(expr.Value) != 2 {
		return false
	}
	_, x2 := toFloat64(er.Value[0], er.Value[1])
	y1, _ := toFloat64(expr.Value[0], expr.Value[1])
	if y1 <= x2 {
		er.Value[1] = max(er.Value[1], expr.Value[1])
		if isEqual(er.Value[1], expr.Value[1]) {
			er.RightOp = expr.OpChar
			if len(expr.RightOp) != 0 {
				er.RightOp = expr.RightOp
			}
		}
		return true
	}
	if isInfinity(er.Value[1]) {
		return true
	}
	if isInfinity(er.Value[0]) && isInfinity(er.Value[1]) {
		return true
	}
	return false
}

type exprSortable []Expr

func (e exprSortable) Len() int {
	return len(e)
}
func (e exprSortable) Less(i, j int) bool {
	var a, b float64
	initValue := func(vi, vj interface{}) {
		a, _ = strconv.ParseFloat(fmt.Sprintf("%v", vi), 64)
		b, _ = strconv.ParseFloat(fmt.Sprintf("%v", vj), 64)
	}
	if isInfinity(e[i].Value[0]) && !isInfinity(e[j].Value[0]) {
		return true
	}
	if isInfinity(e[i].Value[0]) && isInfinity(e[j].Value[0]) {
		initValue(e[i].Value[1], e[j].Value[1])
		return a < b
	}
	if e[i].Value[0] == e[j].Value[0] {
		initValue(e[i].Value[1], e[j].Value[1])
		return a < b
	}
	initValue(e[i].Value[0], e[j].Value[0])
	return a < b
}
func (e exprSortable) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}

func joinExprResult(ec []exprCompute) []ExprResult {
	var result []ExprResult
	for _, ecItem := range ec {
		if ecItem != nil {
			result = ecItem.Append(result)
		}
	}
	return result
}

func toFloat64(vi, vj interface{}) (float64, float64) {
	a, _ := strconv.ParseFloat(fmt.Sprintf("%v", vi), 64)
	b, _ := strconv.ParseFloat(fmt.Sprintf("%v", vj), 64)
	return a, b
}

func max(x, y interface{}) interface{} {
	if isInfinity(x) {
		return x
	}
	if isInfinity(y) {
		return y
	}
	fx, fy := toFloat64(x, y)
	return math.Max(fx, fy)
}

func isInfinity(val interface{}) bool {
	v := fmt.Sprintf("%v", val)
	inf := fmt.Sprintf("%v", Infinity)
	return v == inf
}

func isEqual(v1, v2 interface{}) bool {
	sv1 := fmt.Sprintf("%v", v1)
	sv2 := fmt.Sprintf("%v", v2)
	return sv1 == sv2
}