package conprof

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"

	"github.com/pingcap-inc/clinic/pkg/ngm/utils"
	"github.com/pingcap-inc/clinic/pkg/o11y"
)

func Test_shouldProxyConprofToO11y(t *testing.T) {
	tests := []struct {
		name       string
		deployType string
		want       bool
	}{
		{name: "premium", deployType: o11y.PremiumType, want: true},
		{name: "nextgen", deployType: "nextgen", want: true},
		{name: "nextgen host", deployType: o11y.NgHostType, want: true},
		{name: "byoc host", deployType: o11y.ByocHostType, want: true},
		{name: "case insensitive byoc host", deployType: "BYOC-HOST", want: true},
		{name: "dedicated", deployType: o11y.DedicatedType, want: false},
		{name: "shared", deployType: o11y.SharedType, want: false},
		{name: "byoc logical cluster", deployType: o11y.ByocType, want: false},
		{name: "empty", deployType: "", want: false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			require.Equal(t, tt.want, shouldProxyConprofToO11y(tt.deployType))
		})
	}
}

func Test_shouldProxyConprofActionTokenToO11y(t *testing.T) {
	tests := []struct {
		name       string
		deployType string
		want       bool
	}{
		{name: "premium", deployType: o11y.PremiumType, want: true},
		{name: "nextgen", deployType: "nextgen", want: true},
		{name: "nextgen host", deployType: o11y.NgHostType, want: true},
		{name: "byoc host", deployType: o11y.ByocHostType, want: false},
		{name: "dedicated", deployType: o11y.DedicatedType, want: false},
		{name: "shared", deployType: o11y.SharedType, want: false},
		{name: "byoc logical cluster", deployType: o11y.ByocType, want: false},
		{name: "empty", deployType: "", want: false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			require.Equal(t, tt.want, shouldProxyConprofActionTokenToO11y(tt.deployType))
		})
	}
}

func Test_handleActionTokenByocHostKeepsDeployTypeInToken(t *testing.T) {
	gin.SetMode(gin.TestMode)
	svc := &Service{}
	q := url.Values{}
	q.Set("ts", "1783304760")
	q.Set("profile_type", "goroutine")
	q.Set("component", "tidb")
	q.Set("address", "db-2041564832649449472-tidb-dims7a")
	q.Set("data_format", "protobuf")

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	req, err := http.NewRequest(http.MethodGet, "/continuous_profiling/action_token?q="+url.QueryEscape(q.Encode()), nil)
	require.NoError(t, err)
	req.Header.Set("x-provider", "aws")
	req.Header.Set("x-region", "us-east-1")
	req.Header.Set("x-org-id", "1372813089209313726")
	req.Header.Set("x-project-id", "")
	req.Header.Set("x-cluster-id", "2034353583595458560")
	req.Header.Set("x-deploy-type", o11y.ByocHostType)
	c.Request = req

	svc.handleActionToken(c)

	require.Equal(t, http.StatusOK, w.Code)
	data, err := utils.ParseJWTString("conprof", w.Body.String())
	require.NoError(t, err)
	var params downloadTokenParams
	require.NoError(t, json.Unmarshal([]byte(data), &params))
	require.Equal(t, "aws", params.Provider)
	require.Equal(t, "us-east-1", params.Region)
	require.Equal(t, "1372813089209313726", params.TenantID)
	require.Empty(t, params.ProjectID)
	require.Equal(t, "2034353583595458560", params.ClusterID)
	require.Equal(t, o11y.ByocHostType, params.DeployType)
	require.Equal(t, q.Encode(), params.Q)
}

func Test_convertProfileMetasToListResponseItems(t *testing.T) {
	resp := convertProfileMetasToListResponseItems([]ProfileMeta{
		{Ts: 1, Component: "c-1", Address: "a-1", ProfileType: "p-1"},
		{Ts: 1, Component: "c-1", Address: "a-1", ProfileType: "p-2"},
		{Ts: 1, Component: "c-1", Address: "a-1", ProfileType: "p-3"},
		{Ts: 1, Component: "c-1", Address: "a-1", ProfileType: "p-4"},
		{Ts: 1, Component: "c-1", Address: "a-2", ProfileType: "p-1"},
		{Ts: 1, Component: "c-1", Address: "a-2", ProfileType: "p-2"},
		{Ts: 1, Component: "c-1", Address: "a-2", ProfileType: "p-3"},
		{Ts: 1, Component: "c-1", Address: "a-2", ProfileType: "p-4"},
		{Ts: 1, Component: "c-2", Address: "a-1", ProfileType: "p-1"},
		{Ts: 1, Component: "c-2", Address: "a-1", ProfileType: "p-2"},
		{Ts: 1, Component: "c-2", Address: "a-1", ProfileType: "p-3"},
		{Ts: 1, Component: "c-2", Address: "a-1", ProfileType: "p-4"},
		{Ts: 2, Component: "c-1", Address: "a-1", ProfileType: "p-1"},
		{Ts: 2, Component: "c-1", Address: "a-1", ProfileType: "p-2"},
		{Ts: 2, Component: "c-1", Address: "a-1", ProfileType: "p-3"},
		{Ts: 2, Component: "c-1", Address: "a-1", ProfileType: "p-4"},
		{Ts: 2, Component: "c-1", Address: "a-2", ProfileType: "p-1"},
		{Ts: 2, Component: "c-1", Address: "a-2", ProfileType: "p-2"},
		{Ts: 2, Component: "c-1", Address: "a-2", ProfileType: "p-3"},
		{Ts: 2, Component: "c-1", Address: "a-2", ProfileType: "p-4"},
	})
	require.Len(t, resp, 2)
	require.Equal(t, int64(1), resp[0].Ts)
	require.Len(t, resp[0].ComponentNum, 5+2)
	require.Equal(t, resp[0].ComponentNum["c-1"], 2)
	require.Equal(t, resp[0].ComponentNum["c-2"], 1)
	require.Equal(t, int64(2), resp[1].Ts)
	require.Len(t, resp[1].ComponentNum, 5+1)
	require.Equal(t, resp[1].ComponentNum["c-1"], 2)
}

func Test_convertProfileMetasToDetailResponse(t *testing.T) {
	resp := convertProfileMetasToDetailResponse([]ProfileMeta{
		{Ts: 1, Component: "c-1", Address: "a-1", ProfileType: "p-1"},
		{Ts: 1, Component: "c-1", Address: "a-1", ProfileType: "p-2"},
		{Ts: 1, Component: "c-1", Address: "a-1", ProfileType: "p-3"},
		{Ts: 1, Component: "c-1", Address: "a-1", ProfileType: "p-4"},
		{Ts: 1, Component: "c-1", Address: "a-2", ProfileType: "p-1"},
		{Ts: 1, Component: "c-1", Address: "a-2", ProfileType: "p-2"},
		{Ts: 1, Component: "c-1", Address: "a-2", ProfileType: "p-3"},
		{Ts: 1, Component: "c-1", Address: "a-2", ProfileType: "p-4"},
		{Ts: 1, Component: "c-2", Address: "a-1", ProfileType: "p-1"},
		{Ts: 1, Component: "c-2", Address: "a-1", ProfileType: "p-2"},
		{Ts: 1, Component: "c-2", Address: "a-1", ProfileType: "p-3"},
		{Ts: 1, Component: "c-2", Address: "a-1", ProfileType: "p-4"},
	})
	require.Equal(t, int64(1), resp.Ts)
	require.Len(t, resp.TargetProfiles, 12)
	require.Equal(t, "p-1", resp.TargetProfiles[0].ProfileType)
	require.Equal(t, "a-1", resp.TargetProfiles[0].Target.Address)
	require.Equal(t, "c-1", resp.TargetProfiles[0].Target.Component)
	require.Equal(t, "p-4", resp.TargetProfiles[11].ProfileType)
	require.Equal(t, "a-1", resp.TargetProfiles[11].Target.Address)
	require.Equal(t, "c-2", resp.TargetProfiles[11].Target.Component)
}
