package engine

type initAggregatedDatumFn func() AggregatedPlan

var factory map[int]initAggregatedDatumFn

func init() {
	factory = make(map[int]initAggregatedDatumFn)
	factory[0x4001] = func() AggregatedPlan {
		return &SQLAggOnPTime{}
	}
}

func BuildAggregatedPlan(id int) AggregatedPlan {
	if fn, ok := factory[id]; ok {
		return fn()
	}
	return &SQLPlan{}
}


type AggregatedPlan interface {
	GetAbnormalPlan() (map[string][]string, error)
}

// stander bucket item
type BucketItem struct {
	Key      string `json:"key"`
	DocCount int64  `json:"doc_count"`
}

type AggregatedValue struct {
	Value float64  `json:"value"`
	Keys  []string `json:"keys"`
}

type SQLAggOnPTime struct {
	Buckets []struct {
		BucketItem
		MaxAvgPTime AggregatedValue `json:"max_avg_p_time"`
		MinAvgPTime AggregatedValue `json:"min_avg_p_time"`
		PlanDetail struct {
			Buckets []struct {
				BucketItem
				AvgPTime struct {
					Value float64 `json:"value"`
				} `json:"avg_p_time"`
				LastTime struct {
					Value         float64  `json:"value"`
					ValueAsString string `json:"value_as_string"`
				} `json:"last_time"`
			} `json:"buckets"`
		} `json:"plan_detail"`
	} `json:"buckets"`
}

func (sql *SQLAggOnPTime) GetAbnormalPlan() (map[string][]string, error) {
	abnormalPlan := make(map[string][]string)
	for _, bkt := range sql.Buckets {
		lastTimeFastPlan, lastTimeSlowPlan := 0.0, 0.0
		maxPlan, minPlan := bkt.MaxAvgPTime.Keys[0], bkt.MinAvgPTime.Keys[0]
		for _, plan := range bkt.PlanDetail.Buckets {
			if plan.Key == maxPlan {
				lastTimeSlowPlan = plan.LastTime.Value
			}
			if plan.Key == minPlan {
				lastTimeFastPlan = plan.LastTime.Value
			}
		}
		if lastTimeSlowPlan > lastTimeFastPlan {
			abnormalPlan[bkt.Key] = []string{maxPlan, minPlan}
		}
	}
	return abnormalPlan, nil
}

type SQLPlan struct {
	Buckets []struct {
		BucketItem
		PlanDetail struct {
			Buckets []BucketItem `json:"buckets"`
		} `json:"plan_detail"`
	} `json:"buckets"`
}

func (sql *SQLPlan) GetAbnormalPlan() (map[string][]string, error) {
	abnormalPlan := make(map[string][]string)
	for _, bkt := range sql.Buckets {
		planDigests := make([]string, len(bkt.PlanDetail.Buckets))
		for i, item := range bkt.PlanDetail.Buckets {
			planDigests[i] = item.Key
		}
		abnormalPlan[bkt.Key] = planDigests
	}
	return abnormalPlan, nil
}

type HitsInfo struct {
	Total struct {
		Value int64 `json:"value"`
	} `json:"total"`
}

type QueryResult struct {
	Error struct {
		Type   string `json:"type"`
		Reason string `json:"reason"`
	} `json:"error"`
	Took    int64 `json:"took"`
	TimeOut bool  `json:"time_out"`
	Hits    HitsInfo `json:"hits"`
	Aggregations struct {
		SQL AggregatedPlan `json:"sql"`
	} `json:"aggregations"`
}
