package tests

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

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/api"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/suite"
)

// func TestMain(m *testing.M) {
// 	testsetup.TestMain(m)
// }

type PackageTestSuite struct {
	tests.BillingTestSuite
	router *gin.Engine
}

func (suite *PackageTestSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	suite.router = gin.New()
	suite.router.Use(ginutils.ErrorMiddleWare)
	api.InitializePackageRouter(ginutils.MyRoute{Route: suite.router})
}

func TestPackageSuite(t *testing.T) {
	suite.Run(t, new(PackageTestSuite))
}

var packageModel = &billing.PackageModel{}
var bindedPackageModel = &billing.BindedPackageModel{}

func (suite *PackageTestSuite) TestListPackages() {
	assert := suite.Assert()
	packageModel.CreatePackage(&forms.PackageInfo{})
	w := tests.PerformGetRequest(suite.router, "/packages")
	assert.Equal(http.StatusOK, w.Code)
}

func (suite *PackageTestSuite) TestGetPackage() {
	assert := suite.Assert()
	info, _ := packageModel.CreatePackage(&forms.PackageInfo{})
	w := tests.PerformGetRequest(suite.router, fmt.Sprintf("/packages/%v", info.PackageID))
	assert.Equal(http.StatusOK, w.Code)
	ret := forms.PackageInfo{}
	err := json.NewDecoder(w.Body).Decode(&ret)
	assert.NoError(err)
	assert.Equal(info.PackageID, ret.PackageID)
}

func (suite *PackageTestSuite) TestBindPackage() {
	assert := suite.Assert()
	info, _ := packageModel.CreatePackage(&forms.PackageInfo{})
	w := tests.PerformRequest(suite.router, "POST", "/tenants/1/binded_packages", &forms.BindedPackageInfo{
		PackageID: info.PackageID,
	})
	assert.Equal(http.StatusOK, w.Code)
	res := forms.BindedPackageInfo{}
	json.Unmarshal(w.Body.Bytes(), &res)
	_, err := bindedPackageModel.GetBindedPackage(res.BindedPackageID)
	assert.NoError(err)
}

func (suite *PackageTestSuite) TestGetBindPackage() {
	assert := suite.Assert()
	info, _ := packageModel.CreatePackage(&forms.PackageInfo{})
	bindedPackage, _ := bindedPackageModel.BindPackage(uint(1), info.PackageID)
	w := tests.PerformGetRequest(suite.router, fmt.Sprintf("/tenants/1/binded_packages/%d", bindedPackage.PackageID))
	assert.Equal(http.StatusOK, w.Code)
	ret := forms.BindedPackageInfo{}
	err := json.NewDecoder(w.Body).Decode(&ret)
	assert.NoError(err)
}

type PartialPackageSchema struct {
	Discount decimal.Decimal `json:"discount"`
}

func (suite *PackageTestSuite) TestPartialUpdatePackage() {
	assert := suite.Assert()
	info, _ := packageModel.CreatePackage(&forms.PackageInfo{
		Description: "ABC",
	})
	w := tests.PerformRequest(suite.router, "PATCH", fmt.Sprintf("/packages/%d", info.PackageID), &PartialPackageSchema{Discount: decimal.NewFromInt(1)})
	assert.Equal(http.StatusOK, w.Code)
	res, err := packageModel.GetPackage(info.PackageID)
	assert.NoError(err)
	assert.Equal(res.Description, "ABC")
	assert.Equal(res.Discount, decimal.NewFromInt(1))
}

func (suite *PackageTestSuite) TestGetAllBindPackages() {
	assert := suite.Assert()
	info, _ := packageModel.CreatePackage(&forms.PackageInfo{})
	bindedPackageModel.BindPackage(uint(1), info.PackageID)
	w := tests.PerformGetRequest(suite.router, "/tenants/1/binded_packages")
	assert.Equal(http.StatusOK, w.Code)
	ret := []forms.BindedPackageInfo{}
	err := json.NewDecoder(w.Body).Decode(&ret)
	assert.NoError(err)
}

func (suite *PackageTestSuite) TestUnbindPackage() {
	assert := suite.Assert()
	info, _ := packageModel.CreatePackage(&forms.PackageInfo{})
	bindedPackage, _ := bindedPackageModel.BindPackage(uint(1), info.PackageID)
	w := tests.PerformRequest(suite.router, "DELETE", fmt.Sprintf("/tenants/1/binded_packages/%d", bindedPackage.BindedPackageID), nil)
	assert.Equal(http.StatusOK, w.Code)
	res, err := bindedPackageModel.GetAllUserBindedPackage(uint(1))
	assert.NoError(err)
	assert.Len(res, 0)
}

func (suite *PackageTestSuite) TestChargeCredits() {
	assert := suite.Assert()
	info, _ := packageModel.CreatePackage(&forms.PackageInfo{
		CreditsEnable: true,
	})
	bindedPackage, _ := bindedPackageModel.BindPackage(uint(1), info.PackageID)
	w := tests.PerformRequest(suite.router, "POST", fmt.Sprintf("/tenants/1/binded_packages/%d/charge_credits", bindedPackage.BindedPackageID), &forms.CreditsChargeInfo{
		ChargeCount: decimal.NewFromInt(100),
	})
	assert.Equal(http.StatusOK, w.Code)
	res, err := bindedPackageModel.GetBindedPackage(bindedPackage.BindedPackageID)
	assert.NoError(err)
	assert.Equal(res.RemainingCredits, decimal.NewFromInt(100))
}

func (suite *PackageTestSuite) TestChangeDiscount() {
	assert := suite.Assert()
	info, _ := packageModel.CreatePackage(&forms.PackageInfo{})
	bindedPackage, _ := bindedPackageModel.BindPackage(uint(1), info.PackageID)
	w := tests.PerformRequest(suite.router, "PUT", fmt.Sprintf("/tenants/1/binded_packages/%d/discount", bindedPackage.BindedPackageID), &forms.ChangeBindedDiscountInput{
		BindedDiscount: tests.AddrDecimal(decimal.NewFromInt(2)),
	})
	assert.Equal(http.StatusOK, w.Code)
	res, err := bindedPackageModel.GetBindedPackage(bindedPackage.BindedPackageID)
	assert.NoError(err)
	assert.Equal(*res.BindedDiscount, decimal.NewFromInt(2))
}
