package tests

import (
	"testing"

	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/db"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/suite"
)

var bindedPackageModel = &billing.BindedPackageModel{}

type BindedPackageTestSuite struct {
	tests.BillingTestSuite
}

func TestBindedPackageSuite(t *testing.T) {
	suite.Run(t, new(BindedPackageTestSuite))
}

func (suite *BindedPackageTestSuite) TestCreateBindPackage() {
	assert := suite.Assert()
	tenantID := uint(0)
	packageInfo, _ := (&billing.PackageModel{}).CreatePackage(&forms.PackageInfo{})
	info, err := bindedPackageModel.BindPackage(tenantID, packageInfo.PackageID)
	assert.Nil(err)
	assert.NotNil(info)
	assert.Equal(info.PackageID, packageInfo.PackageID)
	assert.Equal(info.RemainingCredits.Cmp(packageInfo.Credits), 0)
	assert.Equal(info.PackageInfo.Version, packageInfo.Version)
	assert.Equal(info.TenantID, tenantID)
}

func (suite *BindedPackageTestSuite) TestUnbindPackage() {
	assert := suite.Assert()
	tenantID := uint(0)
	packageInfo, _ := (&billing.PackageModel{}).CreatePackage(&forms.PackageInfo{})
	info, _ := bindedPackageModel.BindPackage(tenantID, packageInfo.PackageID)
	err := bindedPackageModel.UnbindPackage(info.BindedPackageID)
	assert.Nil(err)
	info, err = bindedPackageModel.GetBindedPackage(info.BindedPackageID)
	assert.Nil(err)
	assert.Equal(info.Status, entity.BindedPackageStatus_InActive)
	infos, _ := bindedPackageModel.GetAllUserBindedPackage(tenantID)
	assert.Equal(len(infos), 0)
}

func (suite *BindedPackageTestSuite) TestGetAllPackage() {
	assert := suite.Assert()
	tenantID := uint(0)
	packageInfo, _ := (&billing.PackageModel{}).CreatePackage(&forms.PackageInfo{})
	bindedPackageModel.BindPackage(tenantID, packageInfo.PackageID)
	infos, err := bindedPackageModel.GetAllUserBindedPackage(tenantID)
	assert.Nil(err)
	assert.Equal(len(infos), 1)
	bindedPackageModel.BindPackage(tenantID, packageInfo.PackageID)
	infos, err = bindedPackageModel.GetAllUserBindedPackage(tenantID)
	assert.Nil(err)
	assert.Equal(len(infos), 2)
}

func (suite *BindedPackageTestSuite) TestBindedPackageCharge() {
	assert := suite.Assert()
	tenantID := uint(0)
	packageInfo, _ := (&billing.PackageModel{}).CreatePackage(&forms.PackageInfo{
		CreditsEnable: true,
		CreditsOnly:   false,
	})
	info, err := bindedPackageModel.BindPackage(tenantID, packageInfo.PackageID)
	assert.NoError(err)
	res, err := bindedPackageModel.ChargeCredits(info.BindedPackageID, &forms.CreditsChargeInfo{
		ChargeCount:     decimal.NewFromInt(100),
		ChargerTenantID: 0,
		ChargerUserID:   0,
		ChargeReason:    "manual",
	})
	assert.NoError(err)
	assert.Equal(res.RemainingCredits, decimal.NewFromInt(100))
	record := entity.CreditsChargeRecord{}
	err = entity.NewCreditsChargeRecordQuerySet(db.GetDB()).One(&record)
	assert.NoError(err)
	assert.Equal(record.ChargeCount, decimal.NewFromInt(100))
	assert.Equal(record.ChargerTenantID, uint(0))
	assert.Equal(record.ChargerTenantID, uint(0))
	assert.Equal(record.ChargeReason, "manual")
	assert.Equal(record.ChargeType, entity.CreditsChargeType_MANUAL)
}

func (suite *BindedPackageTestSuite) TestGetChargeHistory() {
	assert := suite.Assert()
	tenantID := uint(0)
	packageInfo, _ := (&billing.PackageModel{}).CreatePackage(&forms.PackageInfo{
		CreditsEnable: true,
		CreditsOnly:   false,
	})
	info, err := bindedPackageModel.BindPackage(tenantID, packageInfo.PackageID)
	assert.NoError(err)
	_, err = bindedPackageModel.ChargeCredits(info.BindedPackageID, &forms.CreditsChargeInfo{
		ChargeCount:     decimal.NewFromInt(100),
		ChargerTenantID: 0,
		ChargerUserID:   0,
		ChargeReason:    "manual",
	})

	assert.NoError(err)

	res, err := bindedPackageModel.GetChargeCreditsHistory(info.BindedPackageID)
	assert.NoError(err)

	assert.Equal(res.TotalAmounts, decimal.NewFromInt(100))
	assert.Len(res.History, 1)
	assert.Equal(res.History[0].ChargeCount, decimal.NewFromInt(100))
}

func (suite *BindedPackageTestSuite) TestBindedPackageMaxTimes() {
	assert := suite.Assert()
	tenantID := uint(0)
	packageInfo, _ := (&billing.PackageModel{}).CreatePackage(&forms.PackageInfo{
		TenantMaxBindedTimes: 1,
	})
	_, err := bindedPackageModel.BindPackage(tenantID, packageInfo.PackageID)
	assert.NoError(err)
	_, err = bindedPackageModel.BindPackage(tenantID, packageInfo.PackageID)
	assert.Error(err)
	assert.ErrorIs(err, billing_errors.InvalidInputError)
	res, err := bindedPackageModel.GetAllUserBindedPackage(tenantID)
	assert.NoError(err)
	assert.Len(res, 1)
}

func (suite *BindedPackageTestSuite) TestBindedPackageBindedDiscount() {
	assert := suite.Assert()
	tenantID := uint(0)
	packageInfo, _ := (&billing.PackageModel{}).CreatePackage(&forms.PackageInfo{})
	info, err := bindedPackageModel.BindPackage(tenantID, packageInfo.PackageID)
	assert.NoError(err)
	assert.Nil(info.BindedDiscount)
	info, err = bindedPackageModel.ChangeBindedDiscount(info.BindedPackageID, &forms.ChangeBindedDiscountInput{
		BindedDiscount: tests.AddrDecimal(decimal.RequireFromString("0.5")),
	})
	assert.NoError(err)
	assert.Zero(info.BindedDiscount.Cmp(decimal.RequireFromString("0.5")))
	info, err = bindedPackageModel.ChangeBindedDiscount(info.BindedPackageID, &forms.ChangeBindedDiscountInput{
		BindedDiscount: nil,
	})
	assert.NoError(err)
	assert.Nil(info.BindedDiscount)
}
