package metrics

import (
	"fmt"
	"testing"
	"time"

	"github.com/onsi/gomega"
	"github.com/pingcap/errors"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/prompb"
	"go.uber.org/atomic"
)

func Test_SinkMetric(t *testing.T) {
	g := gomega.NewWithT(t)
	orgID := uint(1)
	s := &MetricSink{
		Host:         "127.0.0.1",
		Port:         VmInsert,
		OrgID:        orgID,
		ClusterModel: true,
	}

	labels := []prompb.Label{
		{
			Name:  "Test_SinkMetric",
			Value: "Test_SinkMetric",
		},
	}

	now := time.Now().Unix() * 1000
	samples := []prompb.Sample{
		{
			Timestamp: now,
			Value:     1.1,
		},
	}

	ts := prompb.TimeSeries{
		Labels:  labels,
		Samples: samples,
	}

	err := s.Sink(ts)
	g.Expect(err).To(gomega.Succeed())

	result := QueryEventually("127.0.0.1", VmSelect, QueryOption{
		Query: `{Test_SinkMetric="Test_SinkMetric"}`,
	}, orgID, g)

	g.Expect(string(result[0].Metric[model.LabelName("Test_SinkMetric")])).To(gomega.Equal("Test_SinkMetric"))
	g.Expect(len(result[0].Values) > 0).To(gomega.BeTrue())
	g.Expect(float64(result[0].Values[0].Value)).To(gomega.Equal(1.1))
}

func Test_SplitInsertDataSingle1(t *testing.T) {
	ms := &MetricSink{
		Host:                  "127.0.0.1",
		Port:                  8480,
		MaxInsertRequestBytes: 1,
	}

	ts := prompb.TimeSeries{
		Labels: []prompb.Label{
			{
				Name:  "__name__",
				Value: "insertsingle1",
			},
			{
				Name:  ClusterLabel,
				Value: "single1",
			},
			{
				Name:  HostLabel,
				Value: "single1",
			},
		},
		Samples: []prompb.Sample{
			{
				Value:     1,
				Timestamp: time.Now().Unix(),
			},
		},
	}
	g := gomega.NewWithT(t)

	tss := prompb.WriteRequest{
		Timeseries: []prompb.TimeSeries{ts},
	}
	adata, err := tss.Marshal()
	g.Expect(err).To(gomega.Succeed())

	resp, err := ms.splitInsertData([]prompb.TimeSeries{ts})
	g.Expect(err).To(gomega.Succeed())
	g.Expect(len(resp)).To(gomega.Equal(1))
	g.Expect(adata).To(gomega.Equal(resp[0]))
}

func Test_SplitInsertDataSingle2(t *testing.T) {
	ms := &MetricSink{
		Host:                  "127.0.0.1",
		Port:                  8480,
		MaxInsertRequestBytes: 1,
	}
	tss := make([]prompb.TimeSeries, 0)

	g := gomega.NewWithT(t)

	expectResult := make([][]prompb.TimeSeries, 2)
	for i := 0; i < 2; i++ {
		ts := prompb.TimeSeries{
			Labels: []prompb.Label{
				{
					Name:  "__name__",
					Value: fmt.Sprintf("insertsingle%d", i),
				},
				{
					Name:  ClusterLabel,
					Value: fmt.Sprintf("insertsingle%d", i),
				},
				{
					Name:  HostLabel,
					Value: fmt.Sprintf("insertsingle%d", i),
				},
			},
			Samples: []prompb.Sample{
				{
					Value:     1,
					Timestamp: time.Now().Unix(),
				},
			},
		}

		mdata, err := ts.Marshal()
		g.Expect(err).To(gomega.Succeed())
		ms.MaxInsertRequestBytes = len(mdata) + 5

		tss = append(tss, ts)

		expectResult[i] = append(expectResult[i], ts)
	}

	resp, err := ms.splitInsertData(tss)
	g.Expect(err).To(gomega.Succeed())
	g.Expect(len(resp)).To(gomega.Equal(len(expectResult)))
	for i := 0; i < len(expectResult); i++ {
		wr := prompb.WriteRequest{
			Timeseries: expectResult[i],
		}
		expectData, err := wr.Marshal()
		g.Expect(err).To(gomega.Succeed())

		g.Expect(expectData).To(gomega.Equal(resp[i]))
	}
}

func Test_SplitInsertDataSingle3(t *testing.T) {
	ms := &MetricSink{
		Host:                  "127.0.0.1",
		Port:                  8480,
		MaxInsertRequestBytes: 1,
	}
	tss := make([]prompb.TimeSeries, 0)

	g := gomega.NewWithT(t)

	expectResult := make([][]prompb.TimeSeries, 6)
	for i := 0; i < 10; i++ {
		ts := prompb.TimeSeries{
			Labels: []prompb.Label{
				{
					Name:  "__name__",
					Value: fmt.Sprintf("insertsingle%d", i),
				},
				{
					Name:  ClusterLabel,
					Value: fmt.Sprintf("insertsingle%d", i),
				},
				{
					Name:  HostLabel,
					Value: fmt.Sprintf("insertsingle%d", i),
				},
			},
			Samples: []prompb.Sample{
				{
					Value:     1,
					Timestamp: time.Now().Unix(),
				},
			},
		}

		mdata, err := ts.Marshal()
		g.Expect(err).To(gomega.Succeed())
		ms.MaxInsertRequestBytes = len(mdata)*2 + 5

		tss = append(tss, ts)

		if i == 0 || i == 1 {
			expectResult[0] = append(expectResult[0], ts)
		}

		if i == 2 {
			expectResult[1] = append(expectResult[1], ts)
		}

		if i == 3 || i == 4 {
			expectResult[2] = append(expectResult[2], ts)
		}

		if i == 5 || i == 6 {
			expectResult[3] = append(expectResult[3], ts)
		}

		if i == 7 {
			expectResult[4] = append(expectResult[4], ts)
		}

		if i == 8 || i == 9 {
			expectResult[5] = append(expectResult[5], ts)
		}
	}

	resp, err := ms.splitInsertData(tss)
	g.Expect(err).To(gomega.Succeed())
	g.Expect(len(resp)).To(gomega.Equal(len(expectResult)))
	for i := 0; i < len(expectResult); i++ {
		wr := prompb.WriteRequest{
			Timeseries: expectResult[i],
		}
		expectData, err := wr.Marshal()
		g.Expect(err).To(gomega.Succeed())

		g.Expect(expectData).To(gomega.Equal(resp[i]))
	}
}

func Test_SplitInsertDataSingle4(t *testing.T) {
	ms := &MetricSink{
		Host:                  "127.0.0.1",
		Port:                  8480,
		MaxInsertRequestBytes: 1,
	}
	tss := make([]prompb.TimeSeries, 0)

	g := gomega.NewWithT(t)

	expectResult := make([][]prompb.TimeSeries, 5)
	for i := 0; i < 9; i++ {
		ts := prompb.TimeSeries{
			Labels: []prompb.Label{
				{
					Name:  "__name__",
					Value: fmt.Sprintf("insertsingle%d", i),
				},
				{
					Name:  ClusterLabel,
					Value: fmt.Sprintf("insertsingle%d", i),
				},
				{
					Name:  HostLabel,
					Value: fmt.Sprintf("insertsingle%d", i),
				},
			},
			Samples: []prompb.Sample{
				{
					Value:     1,
					Timestamp: time.Now().Unix(),
				},
			},
		}

		mdata, err := ts.Marshal()
		g.Expect(err).To(gomega.Succeed())
		ms.MaxInsertRequestBytes = len(mdata)*2 + 5

		tss = append(tss, ts)

		if i == 0 || i == 1 {
			expectResult[0] = append(expectResult[0], ts)
		}

		if i == 2 || i == 3 {
			expectResult[1] = append(expectResult[1], ts)
		}

		if i == 5 || i == 4 {
			expectResult[2] = append(expectResult[2], ts)
		}

		if i == 6 {
			expectResult[3] = append(expectResult[3], ts)
		}

		if i == 7 || i == 8 {
			expectResult[4] = append(expectResult[4], ts)
		}
	}

	resp, err := ms.splitInsertData(tss)
	g.Expect(err).To(gomega.Succeed())
	g.Expect(len(resp)).To(gomega.Equal(len(expectResult)))
	for i := 0; i < len(expectResult); i++ {
		wr := prompb.WriteRequest{
			Timeseries: expectResult[i],
		}
		expectData, err := wr.Marshal()
		g.Expect(err).To(gomega.Succeed())

		g.Expect(expectData).To(gomega.Equal(resp[i]))
	}
}

func Test_Retry(t *testing.T) {
	flag := atomic.Bool{}
	testF := func() error {
		if !flag.Load() {
			return errors.New("test")
		}

		return nil
	}

	err := retry(2, time.Second, func() error {
		return testF()
	})
	g := gomega.NewWithT(t)
	g.Expect(err).To(gomega.MatchError("test"))

	flag.CompareAndSwap(false, true)
	err = retry(10, time.Hour, func() error {
		return testF()
	})
	g.Expect(err).To(gomega.Succeed())
}
