package debugportal

import (
	"context"
	"fmt"
	"testing"

	topsqlmodel "github.com/pingcap/dbaas/central/model/debug/topsql"
	"github.com/stretchr/testify/assert"
	proto "github.com/tidbcloud/idl/pbgen/proto/central"
)

func TestCalcWindowSlots(t *testing.T) {
	rows := []topsqlmodel.TopCPUTimeDigestsWindowedRow{
		{TimeWindow: 1}, {TimeWindow: 1},
		{TimeWindow: 2},
		{TimeWindow: 3}, {TimeWindow: 3}, {TimeWindow: 3},
	}
	windowSlots := calcWindowSlots(rows)
	expected := []int{2, 1, 3}
	assert.ElementsMatch(t, windowSlots, expected)
}

func setupTopDigest(t *testing.T, req *proto.TopSQLGetTopDigestsReq, ret []topsqlmodel.TopCPUTimeDigestsWindowedRow) *proto.TopSQLGetTopDigestResp {
	topsqlModel := topsqlmodel.NewMockTopSQLModel()
	debugService := debugPortalService{
		topsqlModel: topsqlModel,
	}
	topsqlModel.SetReturnsGetTopCPUTimeDigestsWindowed(ret)
	resp, err := debugService.TopSQLGetTopDigests(context.Background(), req)
	assert.NoError(t, err, "TopSQLGetTopDigests() should not return error")
	return resp
}

func TestTopSQLGetTopDigests(t *testing.T) {
	t.Run("TopN=rows, 1 time window, no others in db", func(t *testing.T) {
		req := &proto.TopSQLGetTopDigestsReq{TopN: 4, WindowSizeSec: 2, By: "cpu"}
		ret := []topsqlmodel.TopCPUTimeDigestsWindowedRow{
			{SqlDigest: []byte("sqldigest1"), TimeWindow: 1, CpuTimeAgg: 1, CpuTimeSumInWindow: 10},
			{SqlDigest: []byte("sqldigest2"), TimeWindow: 1, CpuTimeAgg: 2, CpuTimeSumInWindow: 10},
			{SqlDigest: []byte("sqldigest3"), TimeWindow: 1, CpuTimeAgg: 3, CpuTimeSumInWindow: 10},
			{SqlDigest: []byte("sqldigest4"), TimeWindow: 1, CpuTimeAgg: 4, CpuTimeSumInWindow: 10},
		}
		resp := setupTopDigest(t, req, ret)

		assert.Equal(t, 4, len(resp.Records))
		for _, r := range resp.Records {
			assert.Equal(t, false, r.IsOthers)
			assert.NotEqual(t, "", string(r.SqlDigest))
			assert.Equal(t, 1, len(r.Data))
			assert.Equal(t, uint64(2), r.Data[0].Timestamp)
			for i := 1; i <= 4; i++ {
				if string(r.SqlDigest) == fmt.Sprintf("sqldigest%d", i) {
					assert.Equal(t, uint32(i), r.Data[0].CpuTimeMs)
					break
				}
			}
		}
	})

	t.Run("TopN<rows, 1 time window, no others in db", func(t *testing.T) {
		req := &proto.TopSQLGetTopDigestsReq{TopN: 3, WindowSizeSec: 2, By: "cpu"}
		ret := []topsqlmodel.TopCPUTimeDigestsWindowedRow{
			{SqlDigest: []byte("sqldigest2"), TimeWindow: 1, CpuTimeAgg: 2, CpuTimeSumInWindow: 10},
			{SqlDigest: []byte("sqldigest3"), TimeWindow: 1, CpuTimeAgg: 3, CpuTimeSumInWindow: 10},
			{SqlDigest: []byte("sqldigest4"), TimeWindow: 1, CpuTimeAgg: 4, CpuTimeSumInWindow: 10},
		}
		resp := setupTopDigest(t, req, ret)

		assert.Equal(t, 4, len(resp.Records))
		for _, r := range resp.Records {
			assert.Equal(t, 1, len(r.Data))
			assert.Equal(t, uint64(2), r.Data[0].Timestamp)
			for i := 1; i <= 3; i++ {
				if string(r.SqlDigest) == fmt.Sprintf("sqldigest%d", i) {
					assert.Equal(t, uint32(i), r.Data[0].CpuTimeMs)
					break
				}
			}
			if string(r.SqlDigest) == "" {
				assert.Equal(t, true, r.IsOthers)
				assert.Equal(t, uint32(1), r.Data[0].CpuTimeMs)
			}
		}
	})

	t.Run("TopN>rows, 1 time window, no others in db", func(t *testing.T) {
		req := &proto.TopSQLGetTopDigestsReq{TopN: 5, WindowSizeSec: 2, By: "cpu"}
		ret := []topsqlmodel.TopCPUTimeDigestsWindowedRow{
			{SqlDigest: []byte("sqldigest1"), TimeWindow: 1, CpuTimeAgg: 1, CpuTimeSumInWindow: 10},
			{SqlDigest: []byte("sqldigest2"), TimeWindow: 1, CpuTimeAgg: 2, CpuTimeSumInWindow: 10},
			{SqlDigest: []byte("sqldigest3"), TimeWindow: 1, CpuTimeAgg: 3, CpuTimeSumInWindow: 10},
			{SqlDigest: []byte("sqldigest4"), TimeWindow: 1, CpuTimeAgg: 4, CpuTimeSumInWindow: 10},
		}
		resp := setupTopDigest(t, req, ret)

		assert.Equal(t, 4, len(resp.Records))
		for _, r := range resp.Records {
			// no others in response
			assert.Equal(t, false, r.IsOthers)
			assert.NotEqual(t, "", string(r.SqlDigest))
			assert.Equal(t, 1, len(r.Data))
			assert.Equal(t, uint64(2), r.Data[0].Timestamp)
			for i := 1; i <= 4; i++ {
				if string(r.SqlDigest) == fmt.Sprintf("sqldigest%d", i) {
					assert.Equal(t, uint32(i), r.Data[0].CpuTimeMs)
					break
				}
			}
		}
	})

	// t.Run("TopN<rows, with others in db", func(t *testing.T) {
	// 	req := *_req
	// 	ret := make([]debugmodel.TopCPUTimeDigestsWindowedRow, len(_ret))
	// 	copy(ret, _ret)
	// 	// append an others into the returned rows
	// 	ret = append(ret, debugmodel.TopCPUTimeDigestsWindowedRow{
	// 		SqlDigest:          []byte(""),
	// 		TimeWindow:         1,
	// 		CpuTimeSum:         10,
	// 		WindowedCpuTimeSum: 20,
	// 	})
	// 	req.TopN = 3
	// 	resp := setupTopDigest(t, &req, ret)

	// })
}
