package tests

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

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/utils/httpclient"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/stretchr/testify/suite"
)

type HTTPTestSuite struct {
	tests.BillingTestSuite
	httpServerAddress string
	listener          net.Listener
}

func (suite *HTTPTestSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	assert := suite.Assert()
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	assert.NoError(err)
	suite.httpServerAddress = "http://" + listener.Addr().String()
	suite.listener = listener
}

func TestHTTPSuite(t *testing.T) {
	suite.Run(t, new(HTTPTestSuite))
}

func (suite *HTTPTestSuite) TestHTTPCall() {
	assert := suite.Assert()
	testServer := gin.New()
	testServer.GET("/success", func(c *gin.Context) {
		c.JSON(http.StatusOK, "ok")
	})
	go http.Serve(suite.listener, testServer)
	resp, err := httpclient.HTTPCall("GET", suite.httpServerAddress+"/success", nil, nil, http.DefaultClient)
	assert.NoError(err)
	assert.Equal(string(resp), "\"ok\"")
}

func (suite *HTTPTestSuite) TestHTTPClientGet() {
	assert := suite.Assert()
	authFunc := func(req *http.Request) error {
		req.Header.Set("Authorization", "test")
		return nil
	}
	client := httpclient.NewClient(authFunc, suite.httpServerAddress, "/api/v1/")
	testServer := gin.New()
	testServer.GET("/api/v1/success", func(c *gin.Context) {
		if c.Request.Header.Get("Authorization") != "test" {
			c.AbortWithStatusJSON(http.StatusInternalServerError, "fail")
		}
		c.JSON(http.StatusOK, "ok")
	})
	go http.Serve(suite.listener, testServer)
	resp := ""
	err := client.Get("/success", nil, &resp)
	assert.NoError(err)
	assert.Equal(resp, "ok")

	err = client.Get("/error", nil, &resp)
	assert.Error(err)
	assert.ErrorIs(err, billing_errors.SystemError)
}

func (suite *HTTPTestSuite) TestHTTPClientRequest() {
	assert := suite.Assert()
	authFunc := func(req *http.Request) error {
		req.Header.Set("Authorization", "test")
		return nil
	}
	client := httpclient.NewClient(authFunc, suite.httpServerAddress, "/api/v1/")
	testServer := gin.New()
	testServer.PUT("/api/v1/success", func(c *gin.Context) {
		body := ""
		json.NewDecoder(c.Request.Body).Decode(&body)

		c.JSON(http.StatusOK, gin.H{
			"url":            c.Request.URL.String(),
			"auth":           c.Request.Header.Get("Authorization"),
			"input":          string(body),
			"test_bool_type": false,
			"test_int_type":  1,
		})
	})
	go http.Serve(suite.listener, testServer)
	output := gin.H{}

	err := client.Request("PUT", "/success", map[string]string{"test": "test_param"}, "input", &output)
	assert.NoError(err)
	assert.Equal(output["url"], "/api/v1/success?test=test_param")
	assert.Equal(output["auth"], "test")
	assert.Equal(output["input"], "input")
	assert.Equal(output["test_bool_type"], false)
	assert.EqualValues(output["test_int_type"], 1)
}
