package tests

import (
	"fmt"
	"testing"

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

var packageModel = &billing.PackageModel{}

type PackageTestSuite struct {
	tests.BillingTestSuite
}

func (suite *PackageTestSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	_, err := (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey: "debug-unit",
	})
	panicIf.Error(err)
}

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

func (suite *PackageTestSuite) TestCreatePackage() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{
		UnitPrices: forms.UnitPrices{
			"debug-unit": {Price: decimal.NewFromInt(1)},
		},
	}
	info, err := packageModel.CreatePackage(packageInfo)
	assert.Nil(err)
	assert.Equal(info.Version, uint(1))
	packageInfo, err = packageModel.GetPackage(info.PackageID)
	assert.Nil(err)
	assert.NotNil(packageInfo)
	assert.Equal(packageInfo.UnitPrices["debug-unit"].Price, decimal.NewFromInt(1))
	packageVersion, err := packageModel.GetPackageWithVersion(info.PackageID, 1)
	assert.Nil(err)
	assert.NotNil(packageVersion)
	assert.NotNil(packageVersion)
	assert.Equal(packageVersion.UnitPrices["debug-unit"].Price, decimal.NewFromInt(1))
}

func (suite *PackageTestSuite) TestCreatePackageWithDecimalDiscount() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{
		Discount: decimal.RequireFromString("0.123456789"),
	}
	info, err := packageModel.CreatePackage(packageInfo)
	assert.Nil(err)
	assert.Zero(info.Discount.Cmp(decimal.RequireFromString("0.123456789")))
}

func (suite *PackageTestSuite) TestGetAllPackages() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{}
	_, err := packageModel.CreatePackage(packageInfo)
	assert.Nil(err)
	allPackages, err := packageModel.GetAllPackages()
	assert.Nil(err)
	assert.Equal(len(allPackages), 1)
	_, err = packageModel.CreatePackage(packageInfo)
	assert.Nil(err)
	allPackages, err = packageModel.GetAllPackages()
	assert.Nil(err)
	assert.Equal(len(allPackages), 2)
	assert.NotEqual(allPackages[0].PackageID, allPackages[1].PackageID)
}

func (suite *PackageTestSuite) TestGetPackage() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{}
	info, _ := packageModel.CreatePackage(packageInfo)
	packageInfo, err := packageModel.GetPackage(info.PackageID)
	assert.Nil(err)
	assert.Equal(packageInfo.Version, uint(1))
}

func (suite *PackageTestSuite) TestGetPackageWithVersion() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{}
	info, err := packageModel.CreatePackage(packageInfo)
	assert.Nil(err)
	packageInfo, err = packageModel.GetPackageWithVersion(info.PackageID, info.Version)
	assert.Nil(err)
	assert.NotNil(packageInfo)
}

func (suite *PackageTestSuite) TestUpdatePackage() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{
		UnitPrices: forms.UnitPrices{
			"debug-unit": {Price: decimal.NewFromInt(1)},
		},
	}
	info, _ := packageModel.CreatePackage(packageInfo)
	oldVersion := info.Version
	packageInfo.UnitPrices["debug-unit"] = forms.UnitPrice{Price: decimal.NewFromInt(2)}
	info, err := packageModel.UpdatePackage(info.PackageID, info)
	assert.Nil(err)
	newVersion := info.Version
	assert.Equal(info.UnitPrices["debug-unit"].Price, decimal.NewFromInt(2))
	_ = oldVersion
	_ = newVersion
	info, err = packageModel.GetPackageWithVersion(info.PackageID, oldVersion)
	assert.Nil(err)
	assert.Equal(info.UnitPrices["debug-unit"].Price, decimal.NewFromInt(1))

	info, err = packageModel.GetPackageWithVersion(info.PackageID, newVersion)
	assert.Nil(err)
	assert.Equal(info.UnitPrices["debug-unit"].Price, decimal.NewFromInt(2))
	assert.NotEqual(oldVersion, newVersion)

	info, err = packageModel.GetPackage(info.PackageID)
	assert.Nil(err)
	assert.Equal(info.Version, newVersion)
	assert.Equal(info.UnitPrices["debug-unit"].Price, decimal.NewFromInt(2))

	assert.NotNil(packageInfo)
	// fmt.Println(packageInfo)
	pp.Print(packageInfo)
}

func (suite *PackageTestSuite) TestMyPackage() {
	packageInfo := &forms.PackageInfo{}
	fmt.Println(len(packageInfo.UnitPrices))
	fmt.Println(packageInfo.UnitPrices == nil)
	for i, a := range packageInfo.UnitPrices {
		fmt.Println(i, a)
	}
}

func (suite *PackageTestSuite) TestPackageKeyNoFound() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{
		UnitPrices: forms.UnitPrices{
			"not-exist-unit": {Price: decimal.NewFromInt(1)},
		},
	}
	_, err := packageModel.CreatePackage(packageInfo)
	assert.Error(err)
	assert.ErrorIs(err, billing_errors.InvalidInputError)

	packageInfo = &forms.PackageInfo{
		UnitPrices: map[string]forms.UnitPrice{},
	}
	info, err := packageModel.CreatePackage(packageInfo)
	assert.NoError(err)

	packageInfo = &forms.PackageInfo{
		UnitPrices: forms.UnitPrices{
			"not-exist-unit": {Price: decimal.NewFromInt(1)},
		},
	}

	_, err = packageModel.UpdatePackage(info.PackageID, packageInfo)
	assert.Error(err)
	assert.ErrorIs(err, billing_errors.InvalidInputError)
}

func (suite *PackageTestSuite) TestCreatePackageLabels() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{
		UnitPrices: forms.UnitPrices{
			"debug-unit": {Price: decimal.NewFromInt(1)},
		},
		Labels: map[string]string{"A": "B"},
		ExpressionLabels: []forms.ExpressionLabel{
			{Operator: "<=", Value: "1500", Label: "C"},
		},
	}
	info, err := packageModel.CreatePackage(packageInfo)
	assert.Nil(err)
	assert.Equal(info.Labels, map[string]string{"A": "B"})
	assert.Equal(info.ExpressionLabels, []forms.ExpressionLabel{
		{Operator: "<=", Value: "1500", Label: "C"},
	})
}

func (suite *PackageTestSuite) TestCreatePackageLabelsNeg() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{
		UnitPrices: forms.UnitPrices{
			"debug-unit": {Price: decimal.NewFromInt(1)},
		},
		Labels: map[string]string{"A": "B"},
		ExpressionLabels: []forms.ExpressionLabel{
			{Operator: "not_exits", Value: "1500", Label: "C"},
		},
	}
	_, err := packageModel.CreatePackage(packageInfo)
	assert.Error(err)
	assert.ErrorIs(err, billing_errors.InvalidInputError)
}

func (suite *PackageTestSuite) TestCreatePackageExtraPricing() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{
		UnitPrices: forms.UnitPrices{
			"debug-unit": {Price: decimal.NewFromInt(1)},
		},
		ExtraPricingInfos: []forms.PricingInfo{
			{
				UnitPrices: forms.UnitPrices{
					"debug-unit": {Price: decimal.NewFromInt(2)},
				},
			},
		},
	}
	res, err := packageModel.CreatePackage(packageInfo)
	assert.NoError(err)
	assert.Len(res.ExtraPricingInfos, 1)
	assert.Equal(res.ExtraPricingInfos[0].UnitPrices["debug-unit"].Price, decimal.NewFromInt(2))
	assert.Nil(res.ExtraPricingInfos[0].Discount)
}

func (suite *PackageTestSuite) TestCreatePackageMultipleFactor() {
	assert := suite.Assert()
	packageInfo := &forms.PackageInfo{
		UnitPrices: forms.UnitPrices{
			"debug-unit": {
				Price: decimal.NewFromInt(1),
				LabelMultipleFactorPrice: forms.LabelMultipleFactorPrice{
					MultipleFactorLabel:     "debug-label",
					MultipleFactorThreshold: 100,
				},
			},
		},
	}
	info, err := packageModel.CreatePackage(packageInfo)
	assert.Nil(err)
	assert.Equal(info.Version, uint(1))
	packageInfo, err = packageModel.GetPackage(info.PackageID)
	assert.Nil(err)
	assert.NotNil(packageInfo)
	assert.Equal(packageInfo.UnitPrices["debug-unit"].Price, decimal.NewFromInt(1))
	assert.Equal(packageInfo.UnitPrices["debug-unit"].MultipleFactorLabel, "debug-label")
	assert.Equal(packageInfo.UnitPrices["debug-unit"].MultipleFactorThreshold, 100)
	packageVersion, err := packageModel.GetPackageWithVersion(info.PackageID, 1)
	assert.Nil(err)
	assert.NotNil(packageVersion)
	assert.NotNil(packageVersion)
	assert.Equal(packageVersion.UnitPrices["debug-unit"].Price, decimal.NewFromInt(1))
	assert.Equal(packageInfo.UnitPrices["debug-unit"].MultipleFactorLabel, "debug-label")
	assert.Equal(packageInfo.UnitPrices["debug-unit"].MultipleFactorThreshold, 100)
}
