/******************************************************************************
 * Copyright (c)  2022 PingCAP                                                *
 * Licensed under the Apache License, Version 2.0 (the "License");            *
 * you may not use this file except in compliance with the License.           *
 * You may obtain a copy of the License at                                    *
 *                                                                            *
 * http://www.apache.org/licenses/LICENSE-2.0                                 *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 ******************************************************************************/

/*******************************************************************************
 * @File: metrics_test.go
 * @Description:
 * @Author: jiangxunyu@pingcap.com
 * @Version: 1.0.0
 * @Date: 2022/7/12 17:16
*******************************************************************************/

package metrics

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"sync"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/tidbcloud/observability/pkg/common/structs"

	"github.com/tidbcloud/observability/pkg/common/constants"

	"github.com/stretchr/testify/assert"
)

func Test_handleHTTPMetrics(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		w := httptest.NewRecorder()
		c, _ := gin.CreateTestContext(w)
		SetupMetrics()
		// set context
		c.Set(structs.GlobalServerRequestParametersKey, structs.RequestParameters{
			CommonReqInfo: structs.CommonReqInfo{
				Vendor: constants.AWS,
				Region: "us-west-2",
			},
		})
		c.Request = &http.Request{
			Method: http.MethodPost,
		}

		handleHTTPMetrics(c, constants.ObservabilityGlobalServer, constants.EndpointClusterCreate)
		assert.Equal(t, http.StatusOK, w.Code)
	})
}

func resetMetricsSetupWithRegisterFlag() {
	once = sync.Once{}
}

func TestMetrics(t *testing.T) {
	SetupMetrics()

	t.Run("normal", func(t *testing.T) {
		m := GetMetrics()
		assert.NotNil(t, m.BootTimeGaugeMetric)

		assert.NotNil(t, m.APIRequestsCounterMetric)
		assert.NotNil(t, m.RequestDurationHistogramMetric)
		assert.NotNil(t, m.RequestSizeHistogramMetric)
		assert.NotNil(t, m.ResponseSizeHistogramMetric)

		assert.NotNil(t, m.ErrorHandlerTotalCounterMetric)

		assert.NotNil(t, m.RegionalControllerRunsTotalCounterMetric)

		assert.NotNil(t, m.ErrorDialTestMetricsInfoCounterMetric)
		assert.NotNil(t, m.ErrorDialTestRegionalServerCounterMetric)
		assert.NotNil(t, m.DialTestLogsInfoCounterMetric)

		assert.NotNil(t, m.AWSAPIRequestCounterMetric)
		assert.NotNil(t, m.GCPAPIRequestCounterMetric)
	})

	t.Run("normalWithRegister", func(t *testing.T) {
		reg := prometheus.NewRegistry()
		SetupMetrics(reg)
		defer resetMetricsSetupWithRegisterFlag()

		m := GetMetrics()
		assert.NotNil(t, m.BootTimeGaugeMetric)

		assert.NotNil(t, m.APIRequestsCounterMetric)
		assert.NotNil(t, m.RequestDurationHistogramMetric)
		assert.NotNil(t, m.RequestSizeHistogramMetric)
		assert.NotNil(t, m.ResponseSizeHistogramMetric)

		assert.NotNil(t, m.ErrorHandlerTotalCounterMetric)

		assert.NotNil(t, m.RegionalControllerRunsTotalCounterMetric)

		assert.NotNil(t, m.ErrorDialTestMetricsInfoCounterMetric)
		assert.NotNil(t, m.ErrorDialTestRegionalServerCounterMetric)
		assert.NotNil(t, m.DialTestLogsInfoCounterMetric)

		assert.NotNil(t, m.AWSAPIRequestCounterMetric)
		assert.NotNil(t, m.GCPAPIRequestCounterMetric)
	})

	t.Run("handle global api metrics", func(t *testing.T) {
		apiMetrics := HandleGlobalAPIMetrics(constants.EndpointClusterDetail)
		assert.NotNil(t, apiMetrics)
	})

	t.Run("handle regional api metrics", func(t *testing.T) {
		apiMetrics := HandleRegionalAPIMetrics(constants.EndpointClusterQuery)
		assert.NotNil(t, apiMetrics)
	})

	t.Run("handle global routing regional total metrics", func(t *testing.T) {
		HandleGlobalRoutingRegionalTotalMetrics("aws", 2)
		assert.NotNil(t, GetMetrics().GlobalRoutingRegionalTotalGaugeMetric)
	})
	t.Run("handle global routing regional info metrics", func(t *testing.T) {
		HandleGlobalRoutingRegionalInfoMetrics("aws", "us-west-2")
		assert.NotNil(t, GetMetrics().GlobalRoutingRegionalInfoGaugeMetric)
	})

	t.Run("handle regional controller runs total metrics", func(t *testing.T) {
		HandleRegionalControllerRunsMetrics("aws", "us-west-2", "contoller", "resource", "15", "error msg")
		assert.NotNil(t, GetMetrics().RegionalControllerRunsTotalCounterMetric)
	})
	t.Run("handle biz error metrics", func(t *testing.T) {
		HandleGlobalBizErrorMetrics("error msg")
		HandleRegionalBizErrorMetrics("error msg")
		handleBizErrorMetrics(constants.ObservabilityGlobalServer, "error msg")
		assert.NotNil(t, GetMetrics().ErrorHandlerTotalCounterMetric)
	})
	t.Run("normalWithRegister", func(t *testing.T) {
		reg := prometheus.NewRegistry()
		SetupMetrics(reg)
		defer resetMetricsSetupWithRegisterFlag()

		m := GetMetrics()
		assert.NotNil(t, m.BootTimeGaugeMetric)

		assert.NotNil(t, m.APIRequestsCounterMetric)
		assert.NotNil(t, m.RequestDurationHistogramMetric)
		assert.NotNil(t, m.RequestSizeHistogramMetric)
		assert.NotNil(t, m.ResponseSizeHistogramMetric)

		assert.NotNil(t, m.ErrorHandlerTotalCounterMetric)

		assert.NotNil(t, m.RegionalControllerRunsTotalCounterMetric)
	})

	t.Run("handle global api metrics", func(t *testing.T) {
		apiMetrics := HandleGlobalAPIMetrics(constants.EndpointClusterDetail)
		assert.NotNil(t, apiMetrics)
	})

	t.Run("handle regional api metrics", func(t *testing.T) {
		apiMetrics := HandleRegionalAPIMetrics(constants.EndpointClusterQuery)
		assert.NotNil(t, apiMetrics)
	})

	t.Run("handle global routing regional total metrics", func(t *testing.T) {
		HandleGlobalRoutingRegionalTotalMetrics("aws", 2)
		assert.NotNil(t, GetMetrics().GlobalRoutingRegionalTotalGaugeMetric)
	})
	t.Run("handle global routing regional info metrics", func(t *testing.T) {
		HandleGlobalRoutingRegionalInfoMetrics("aws", "us-west-2")
		assert.NotNil(t, GetMetrics().GlobalRoutingRegionalInfoGaugeMetric)
	})

	t.Run("handle regional controller runs total metrics", func(t *testing.T) {
		HandleRegionalControllerRunsMetrics("aws", "us-west-2", "contoller", "resource", "15", "error msg")
		assert.NotNil(t, GetMetrics().RegionalControllerRunsTotalCounterMetric)
	})
	t.Run("handle biz error metrics", func(t *testing.T) {
		HandleGlobalBizErrorMetrics("error msg")
		HandleRegionalBizErrorMetrics("error msg")
		handleBizErrorMetrics(constants.ObservabilityGlobalServer, "error msg")
		assert.NotNil(t, GetMetrics().ErrorHandlerTotalCounterMetric)
	})

	t.Run("handle dial test metrics error info metrics", func(t *testing.T) {
		HandleDialTestMetricsErrorInfoMetrics("aws", "us-west-2", "id", "tenantID", "projectID", "applicationID")
		assert.NotNil(t, GetMetrics().ErrorDialTestMetricsInfoCounterMetric)
	})

	t.Run("handle dial test regional server error info metrics", func(t *testing.T) {
		HandleDialTestRegionalServerErrorMetrics("aws", "us-west-2")
		assert.NotNil(t, GetMetrics().ErrorDialTestRegionalServerCounterMetric)
	})

	t.Run("handle dial test logs info metrics", func(t *testing.T) {
		HandleDialTestLogsInfoMetrics("aws", "us-west-2", "id", "tenantID", "projectID", "applicationID", "123")
		assert.NotNil(t, GetMetrics().DialTestLogsInfoCounterMetric)
	})

	t.Run("handle aws api request metrics", func(t *testing.T) {
		HandleAWSAPIRequestMetrics("us-west-2", "iam-client", "UpdateAssumeRolePolicy", "testname", "update assume role policy failed")
		assert.NotNil(t, GetMetrics().AWSAPIRequestCounterMetric)
	})

	t.Run("handle gcp api request metrics", func(t *testing.T) {
		HandleGCPAPIRequestMetrics("us-west1", "iam-client", "Create", "testname", "create service account failed")
		assert.NotNil(t, GetMetrics().GCPAPIRequestCounterMetric)
	})
}

func TestRegisterNewGaugeVec(t *testing.T) {
	md := MetricDef{
		Name:       "test_gauge",
		Help:       "A gauge test metrics",
		LabelNames: []string{ServiceLabel},
	}
	t.Run("normal", func(t *testing.T) {
		m := RegisterNewGaugeVec(md)
		assert.NotNil(t, m)
	})
}

func TestRegisterNewCounterVec(t *testing.T) {
	md := MetricDef{
		Name:       "test_counter",
		Help:       "A counter test metrics",
		LabelNames: []string{ServiceLabel},
	}
	t.Run("normal", func(t *testing.T) {
		m := RegisterNewCounterVec(md)
		assert.NotNil(t, m)
	})
}

func TestRegisterNewHistogramVec(t *testing.T) {
	md := MetricDef{
		Name:       "test_histogram",
		Help:       "A histogram test metrics",
		LabelNames: []string{ServiceLabel},
	}
	t.Run("normal", func(t *testing.T) {
		m := RegisterNewHistogramVec(md)
		assert.NotNil(t, m)
	})
}

func TestRegisterNewSummaryVec(t *testing.T) {
	md := MetricDef{
		Name:       "test_summary",
		Help:       "A summary test metrics",
		LabelNames: []string{ServiceLabel},
	}
	t.Run("normal", func(t *testing.T) {
		m := RegisterNewSummaryVec(md)
		assert.NotNil(t, m)
	})
}
func TestRegisterNewSummaryVecWithRegister(t *testing.T) {
	md := MetricDef{
		Name:       "test_summary",
		Help:       "A summary test metrics",
		LabelNames: []string{ServiceLabel},
	}
	t.Run("normal", func(t *testing.T) {

		m := RegisterNewSummaryVecWithRegister(prometheus.NewRegistry(), md)
		assert.NotNil(t, m)
	})
}

func Test_computeApproximateRequestSize(t *testing.T) {
	req := &http.Request{
		Method: http.MethodPost,
		URL: &url.URL{
			Path:     "/api/v1",
			RawQuery: "vendor=aws&region=us-west-2",
		},
		Header: map[string][]string{
			"Auth": {"Bearer test_token"},
		},
	}
	size := computeApproximateRequestSize(req)
	assert.Equal(t, 32, size)
}
