package api

import (
	"context"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"path"
	"testing"

	"github.com/tidbcloud/account/internel/svc/user"

	"github.com/tidbcloud/pkgv2/auth/auth0"
	"github.com/tj/assert"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/account/commons/config"
	"github.com/tidbcloud/account/internel/dao/entity"
)

// handleRouteStripGin handles the route and strips it as a prefix. This is useful for proxying.
func handleRouteStripGin(eng *gin.Engine, route string, handler http.Handler) {
	eng.Any(path.Join(route, "*any"), gin.WrapH(http.StripPrefix(route, handler)))
}

func GetBillingProxyFunc(isAdmin bool) http.HandlerFunc {
	user := &user.User{
		TenantID: 10,
		User:     entity.User{},
	}
	if isAdmin {
		user.TenantID = entity.PingCAPTenantID
	}
	return func(w http.ResponseWriter, req *http.Request) {
		fmt.Println(req.URL)
		newCtx := context.WithValue(req.Context(), auth0.AuthTokenClaimKey, user)
		req = req.WithContext(newCtx)
		BillingProxy(w, req)
	}
}

func TestBillingProxyRedirectNotExists(t *testing.T) {
	assert := assert.New(t)
	router := gin.New()
	handleRouteStripGin(router, "/api_proxy/billing", GetBillingProxyFunc(false))
	testServer := gin.New()
	// use random port
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		panic(err)
	}
	config.Global().BillingConfig.Billing2BaseURL = "http://" + listener.Addr().String()
	go http.Serve(listener, testServer) //nolint
	frontend := httptest.NewServer(router)
	defer frontend.Close()
	req, err := http.NewRequest("GET", frontend.URL+"/api_proxy/billing/api/v1/billing/tenants/10/invoices", nil)
	assert.NoError(err)
	frontendClient := frontend.Client()
	result, err := frontendClient.Do(req)
	assert.NoError(err)
	_ = assert
	assert.Equal(result.StatusCode, 404)
}

func TestBillingProxyNormalUser(t *testing.T) {
	assert := assert.New(t)
	router := gin.New()
	handleRouteStripGin(router, "/api_proxy/billing", GetBillingProxyFunc(false))
	testServer := gin.New()
	testServer.GET("/api/v1/billing/tenants/10/invoices", func(c *gin.Context) {
		c.String(http.StatusOK, "test_res")
	})
	// use random port
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		panic(err)
	}
	config.Global().BillingConfig.Billing2BaseURL = "http://" + listener.Addr().String()
	go http.Serve(listener, testServer) //nolint
	frontend := httptest.NewServer(router)
	defer frontend.Close()
	req, err := http.NewRequest("GET", frontend.URL+"/api_proxy/billing/api/v1/billing/tenants/10/invoices", nil)
	assert.NoError(err)
	frontendClient := frontend.Client()
	result, err := frontendClient.Do(req)
	assert.NoError(err)
	_ = assert
	body, err := ioutil.ReadAll(result.Body)
	assert.NoError(err)
	assert.Equal(string(body), "test_res")
	assert.Equal(result.StatusCode, 200)
}

func TestBillingProxyNormalUserBanned(t *testing.T) {
	assert := assert.New(t)
	router := gin.New()
	handleRouteStripGin(router, "/api_proxy/billing", GetBillingProxyFunc(false))
	testServer := gin.New()
	testServer.GET("/api/v1/billing/tenants/5/invoices", func(c *gin.Context) {
		c.String(http.StatusOK, "test_res")
	})
	// use random port
	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		panic(err)
	}
	config.Global().BillingConfig.Billing2BaseURL = "http://" + listener.Addr().String()
	go http.Serve(listener, testServer) //nolint
	frontend := httptest.NewServer(router)
	defer frontend.Close()
	req, err := http.NewRequest("GET", frontend.URL+"/api_proxy/billing/api/v1/billing/tenants/5/invoices", nil)
	assert.NoError(err)
	frontendClient := frontend.Client()
	result, err := frontendClient.Do(req)
	assert.NoError(err)
	_ = assert
	body, err := ioutil.ReadAll(result.Body)
	assert.NoError(err)
	assert.Equal(result.StatusCode, 400)
	fmt.Println(string(body))
}
