package matcher

import (
	"fmt"
	"reflect"
	"time"

	"github.com/onsi/gomega"
)

var defaultConsistentlyWithMetricsDuration = time.Hour * 3
var defaultConsistentlyWithMetricsPollingInterval = time.Second * 30

func SetDefaultConsistentlyWithMetricsDuration(t time.Duration) {
	defaultConsistentlyWithMetricsDuration = t
}

func SetDefaultConsistentlyWithMetricsPollingInterval(t time.Duration) {
	defaultConsistentlyWithMetricsPollingInterval = t
}

// ConsistentlyWithMetrics is nothing but a gomega.Consistently with a longer
// default duration (3h) and polling interval (30s).
//
// These default values could be changed by SetDefaultConsistentlyWithMetricsDuration and
// SetDefaultConsistentlyWithMetricsPollingInterval, respectively.
func ConsistentlyWithMetrics(actual interface{}, intervals ...interface{}) gomega.AsyncAssertion {
	timeoutInterval := defaultConsistentlyWithMetricsDuration
	pollingInterval := defaultConsistentlyWithMetricsPollingInterval
	if len(intervals) > 0 {
		timeoutInterval = toDuration(intervals[0])
	}
	if len(intervals) > 1 {
		pollingInterval = toDuration(intervals[1])
	}

	return gomega.Consistently(actual, timeoutInterval, pollingInterval)
}

// toDuration is copied from gomega/gomega_dsl.go
func toDuration(input interface{}) time.Duration {
	duration, ok := input.(time.Duration)
	if ok {
		return duration
	}

	value := reflect.ValueOf(input)
	kind := reflect.TypeOf(input).Kind()

	if reflect.Int <= kind && kind <= reflect.Int64 {
		return time.Duration(value.Int()) * time.Second
	} else if reflect.Uint <= kind && kind <= reflect.Uint64 {
		return time.Duration(value.Uint()) * time.Second
	} else if reflect.Float32 <= kind && kind <= reflect.Float64 {
		return time.Duration(value.Float() * float64(time.Second))
	} else if reflect.String == kind {
		duration, err := time.ParseDuration(value.String())
		if err != nil {
			panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input))
		}
		return duration
	}

	panic(fmt.Sprintf("%v is not a valid interval.  Must be time.Duration, parsable duration string or a number.", input))
}
