package metrics

import (
	"bytes"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/generic"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestScalableHistogram(t *testing.T) {
	h := generic.NewHistogram("test", 1)
	sh, err := NewHistogramWithScale(h, time.Millisecond)
	require.NoError(t, err)

	ticker := time.NewTicker(500 * time.Millisecond)
	<-ticker.C
	start := time.Now()
	<-ticker.C
	sh.ObserveFromStart(start)

	var b bytes.Buffer
	h.Print(&b)

	extractedDurationString := strings.Split(strings.Split(b.String(), "\n")[1], " ")
	measuredDuration, err := time.ParseDuration(extractedDurationString[0] + "ms")
	assert.NoError(t, err)

	assert.InDelta(t, 500*time.Millisecond, measuredDuration, float64(15*time.Millisecond))
}

func TestNewMultiRegistry(t *testing.T) {
	registries := []Registry{newCollectingRetryMetrics(), newCollectingRetryMetrics()}
	registry := NewMultiRegistry(registries)

	registry.ServiceReqsCounter().With(nil, "key", "requests").Add(1)
	registry.ServiceReqDurationHistogram().With("key", "durations").Observe(float64(2))
	registry.ServiceRetriesCounter().With("key", "retries").Add(3)

	for _, collectingRegistry := range registries {
		cReqsCounter := collectingRegistry.ServiceReqsCounter().(*counterWithHeadersMock)
		cReqDurationHistogram := collectingRegistry.ServiceReqDurationHistogram().(*histogramMock)
		cRetriesCounter := collectingRegistry.ServiceRetriesCounter().(*counterMock)

		wantCounterValue := float64(1)
		if cReqsCounter.counterValue != wantCounterValue {
			t.Errorf("Got value %f for ReqsCounter, want %f", cReqsCounter.counterValue, wantCounterValue)
		}
		wantHistogramValue := float64(2)
		if cReqDurationHistogram.lastHistogramValue != wantHistogramValue {
			t.Errorf("Got last observation %f for ReqDurationHistogram, want %f", cReqDurationHistogram.lastHistogramValue, wantHistogramValue)
		}
		wantCounterValue = float64(3)
		if cRetriesCounter.counterValue != wantCounterValue {
			t.Errorf("Got value %f for RetriesCounter, want %f", cRetriesCounter.counterValue, wantCounterValue)
		}

		assert.Equal(t, []string{"key", "requests"}, cReqsCounter.lastLabelValues)
		assert.Equal(t, []string{"key", "durations"}, cReqDurationHistogram.lastLabelValues)
		assert.Equal(t, []string{"key", "retries"}, cRetriesCounter.lastLabelValues)
	}
}

func newCollectingRetryMetrics() Registry {
	return &standardRegistry{
		serviceReqsCounter:          &counterWithHeadersMock{},
		serviceReqDurationHistogram: &histogramMock{},
		serviceRetriesCounter:       &counterMock{},
	}
}

type counterMock struct {
	counterValue    float64
	lastLabelValues []string
}

func (c *counterMock) With(labelValues ...string) metrics.Counter {
	c.lastLabelValues = labelValues
	return c
}

func (c *counterMock) Add(delta float64) {
	c.counterValue += delta
}

type counterWithHeadersMock struct {
	counterValue    float64
	lastLabelValues []string
}

func (c *counterWithHeadersMock) With(_ http.Header, labelValues ...string) CounterWithHeaders {
	c.lastLabelValues = labelValues
	return c
}

func (c *counterWithHeadersMock) Add(delta float64) {
	c.counterValue += delta
}

type histogramMock struct {
	lastHistogramValue float64
	lastLabelValues    []string
}

func (c *histogramMock) With(labelValues ...string) ScalableHistogram {
	c.lastLabelValues = labelValues
	return c
}

func (c *histogramMock) Start() {}

func (c *histogramMock) ObserveFromStart(t time.Time) {}

func (c *histogramMock) Observe(v float64) {
	c.lastHistogramValue = v
}
