package handler

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"
	"time"

	"cloud.google.com/go/pubsub"
	"github.com/form3tech-oss/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/suite"
	"github.com/tidbcloud/metrics-server/central/config"
	"github.com/tidbcloud/metrics-server/central/test"
)

type GCPTestSuite struct {
	test.CentralTestSuite
}

func TestGCPSuite(t *testing.T) {
	suite.Run(t, new(GCPTestSuite))
}

const (
	GCPProject        = ""
	GCPPubSubTopic    = ""
	GCPProductCode    = ""
	GCPAccountID      = ""
	GCPEntitlementID  = ""
	GCPReportID       = ""
	GCPProductUnit    = ""
	GCPCredentialFile = ""
	GCPMarketPlaceSA  = ""
)

func (s *GCPTestSuite) SetupTest() {
	s.CentralTestSuite.SetupTest()

	// open it for function test
	// config.Global().MarketPlaceConfig.GCPMarketPlaceSA = GCPMarketPlaceSA
	// s.NoError(auth.InitWithLocalFile(GCPCredentialFile))
}

func (s *GCPTestSuite) TestGCPExtractToken() {
	assert := s.Assert()

	config.Global().MarketPlaceConfig.GCPISSURL = "mock_iss_url"

	_ = assert
	key, err := rsa.GenerateKey(rand.Reader, 2048)
	assert.NoError(err)

	signKeyFunc = func(token *jwt.Token) (interface{}, error) {
		return key.Public(), nil
	}
	jwtToken := jwt.NewWithClaims(
		jwt.SigningMethodRS256,
		jwt.MapClaims{
			"aud": "mock_aud",
			"iss": config.Global().MarketPlaceConfig.GCPISSURL,
			"sub": "mock_user_id",
		},
	)

	assert.NoError(err)
	res, err := jwtToken.SignedString(key)
	assert.NoError(err)
	c, _ := gin.CreateTestContext(httptest.NewRecorder())
	c.Request, _ = http.NewRequest("POST", "", nil)
	input := url.Values{}
	input.Add(GCPTokenKey, res)
	c.Request.Body = io.NopCloser(strings.NewReader(input.Encode()))
	c.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	token, err := (&gcpAccountProviderImpl{}).GetTokenFromRedirect(c)
	assert.NoError(err)
	assert.Equal(*token, "mock_user_id")
}

func (s *GCPTestSuite) TestGCPResolveToken() {
	assert := s.Assert()

	config.Global().MarketPlaceConfig.GCPISSURL = "mock_iss_url"
	config.Global().MarketPlaceConfig.GCPProductCode = "mock_product_code"

	postAccountApprovalFunc = func(accountName string) error {
		return nil
	}

	res := "mock_user_id"

	user, err := (&gcpAccountProviderImpl{}).ResolveToken(&res)
	assert.NoError(err)
	assert.Equal(*user, "mock_user_id")
}

// should use for manual create subscription
func createSubscription(gcpproject, marketplaceproject string, subsname string) error {
	client, err := pubsub.NewClient(context.Background(), gcpproject)
	if err != nil {
		return err
	}
	topic := client.TopicInProject(gcpproject, marketplaceproject)
	_, err = client.CreateSubscription(context.Background(), subsname, pubsub.SubscriptionConfig{
		Topic: topic,
	})
	if err != nil {
		return err
	}
	return nil
}

var _ = createSubscription

func (s *GCPTestSuite) TestGCPSubsribeChange() {
	s.T().Skip()
	config.Global().MarketPlaceConfig.EnableGCPBackground = true
	config.Global().MarketPlaceConfig.GCPProject = GCPProject
	config.Global().MarketPlaceConfig.GCPPubSubTopic = GCPPubSubTopic
	config.Global().MarketPlaceConfig.GCPProductCode = GCPProductCode

	// For test env, has no config file and aws session. so skip this test.
	handler, err := GetAccountHandler().GetAccountProvider(GCPAccountProvider)
	s.NoError(err)
	processor := func(ctx context.Context, input SubscribeChange) (bool, error) {
		return true, nil
	}
	ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*10)

	defer cancelFunc()

	err = handler.ProcessTenantSubscribeChange(ctx, processor)
	s.NoError(err)
}

func (s *GCPTestSuite) TestGCPGetEntitlementInfo() {
	s.T().Skip()
	config.Global().MarketPlaceConfig.GCPProject = GCPProject
	config.Global().MarketPlaceConfig.GCPProductCode = GCPProductCode
	accountID := GCPAccountID
	entitlmentID := GCPEntitlementID
	info, err := getEntitlementInfo(entitlmentID)
	s.NoError(err)
	s.Equal(info.Product, config.Global().MarketPlaceConfig.GCPProductCode)
	s.Equal(info.Account, accountID)
}
func (s *GCPTestSuite) TestGCPApproveAccount() {
	s.T().Skip()
	config.Global().MarketPlaceConfig.GCPProject = GCPProject
	s.NoError(postAccountApproval(GCPAccountID))
}

func (s *GCPTestSuite) TestGCPEntitlmentApprove() {
	s.T().Skip()
	config.Global().MarketPlaceConfig.GCPProject = GCPProject
	config.Global().MarketPlaceConfig.GCPProductCode = GCPProductCode
	entitlmentID := GCPEntitlementID
	err := approveEntitlement(entitlmentID)
	s.NoError(err)
}
