package tests

import (
	"strconv"
	"testing"

	"github.com/pingcap/dbaas/billing2/constant"
	"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/model/costexplorer"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/pingcap/dbaas/billing2/utils/tests/datainit"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/suite"
)

type MspCostExplorerSuite struct {
	tests.BillingTestSuite
}

func TestMspCostExplorerSuite(t *testing.T) {

	suite.Run(t, new(MspCostExplorerSuite))
}

func (suite *MspCostExplorerSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	datainit.CreateCategoryLabels()
}

func (suite *MspCostExplorerSuite) TestGet3LayerServiceOrder() {
	assert := suite.Assert()

	categoryLabels := costexplorer.GetAllCategoryItemInfoWithCache()
	layer1OrderMap, layer2OrderMap, layer3OrderMap := costexplorer.Get3LayerServiceOrder(categoryLabels)
	assert.Equal(layer1OrderMap["TiDB Serverless"], int32(1))
	assert.Equal(layer2OrderMap["TiDB Serverless"]["Row-based Storage"], int32(1))
	assert.Equal(layer3OrderMap["TiDB Dedicated"]["Data Transfer"]["Internet"], int32(1))

}

var testDedicated = "Dedicated"
var testNetWork = "Balancing"
var testServerLess = "Serverless"
var testSupport = "Support"

func mspAndCost(tenantId uint, isMsp bool, mspCustomerId uint) error {

	(&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   testDedicated,
		Price:     decimal.NewFromInt(1),
		PriceType: entity.PriceType_Normal,
		Category:  "Node Compute Cost",
	})
	(&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey: testNetWork,

		Price:     decimal.NewFromInt(1),
		PriceType: entity.PriceType_Normal,
		Category:  "Data Transfer Cost - Load Balancing",
	})
	(&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   testServerLess,
		Price:     decimal.NewFromInt(1),
		PriceType: entity.PriceType_Normal,
		Category:  "Serverless Row Storage Cost",
	})
	(&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   testSupport,
		Price:     decimal.NewFromInt(1),
		PriceType: entity.PriceType_Normal,
		Category:  "Support Plan",
	})

	customerIDStr := strconv.FormatUint(uint64(mspCustomerId), 10)
	costEntity := &entity.UserUnitCost{
		CostID:                "test-1-1" + customerIDStr,
		TenantID:              tenantId,
		CostTimeStamp:         timeutils.GetNow().Unix(),
		Unit:                  testDedicated,
		Cost:                  20,
		CostPrice:             decimal.NewFromInt(20),
		RawPrice:              decimal.NewFromInt(21),
		Status:                entity.UserUnitCostStatus_DONE,
		InvoiceID:             1,
		Labels:                map[string]string{"component": "COMPONENT_TYPE_TIKV", "project": testDedicated},
		BindedBillingConfigID: 1,
	}
	if isMsp {
		costEntity.Labels[constant.MSPCustomerIDLabel] = customerIDStr
	}
	err := costEntity.Create(db.GetDB())

	//Dedicated project Dedicated
	costEntity.CostID = "test-1-2" + customerIDStr
	costEntity.Labels = map[string]string{"component": "COMPONENT_TYPE_TIFLASH", "project": testDedicated}
	if isMsp {
		costEntity.Labels[constant.MSPCustomerIDLabel] = strconv.FormatUint(uint64(mspCustomerId), 10)
	}
	err = costEntity.Create(db.GetDB())
	costEntity.CostID = "test-1-3-3" + customerIDStr
	costEntity.Labels = map[string]string{"component": "COMPONENT_TYPE_TIDB", "project": testDedicated}
	if isMsp {
		costEntity.Labels[constant.MSPCustomerIDLabel] = strconv.FormatUint(uint64(mspCustomerId), 10)
	}
	err = costEntity.Create(db.GetDB())
	costEntity.CostID = "test-1-3-4" + customerIDStr
	costEntity.ConsumedSource = entity.ConsumedSource_Credits
	err = costEntity.Create(db.GetDB())
	costEntity.ConsumedSource = entity.ConsumedSource_Invoice

	//network project Dedicated
	costEntity.Unit = testNetWork
	costEntity.CostID = "test-2-1-5" + customerIDStr
	err = costEntity.Create(db.GetDB())

	//Serverless
	costEntity.Unit = testServerLess
	costEntity.CostID = "test-3-1-6" + customerIDStr
	costEntity.Labels = map[string]string{"project": testServerLess}
	if isMsp {
		costEntity.Labels[constant.MSPCustomerIDLabel] = strconv.FormatUint(uint64(mspCustomerId), 10)
	}
	err = costEntity.Create(db.GetDB())

	//Support
	costEntity.Unit = testSupport
	costEntity.CostID = "test-4-1-7" + customerIDStr
	costEntity.Labels = map[string]string{}
	if isMsp {
		costEntity.Labels[constant.MSPCustomerIDLabel] = strconv.FormatUint(uint64(mspCustomerId), 10)
	}
	err = costEntity.Create(db.GetDB())
	costEntity.CostID = "test-4-1-8" + customerIDStr
	costEntity.ConsumedSource = entity.ConsumedSource_Credits
	err = costEntity.Create(db.GetDB())

	return err
}

func (suite *MspCostExplorerSuite) TestCalculateMspCustomerOpenAPIMonthlyCost() {
	assert := suite.Assert()
	datainit.CreateBillingConfig(2, entity.BillingConfigType_MSP)

	err := mspAndCost(2, false, 0)
	assert.NoError(err)
	costExplorerModel := &costexplorer.CostExplorerModel{}
	res, err := costExplorerModel.CalculateMspCustomerOpenAPIMonthlyCost(2, 1, timeutils.GetNow())
	runningTotal, err := decimal.NewFromString("168")
	assert.Equal(runningTotal, res.BillsOverView.RunningTotal)
	Price1, err := decimal.NewFromString("20")
	assert.Equal(Price1, res.SummaryByProject.OtherCharges[0].TotalCost)
	Price2, err := decimal.NewFromString("42")
	assert.Equal(Price2, res.SummaryByProject.OtherCharges[0].RunningTotal)
	assert.Equal(res.BillsServiceCosts[0].ServiceName, "TiDB Serverless")

}

func (suite *MspCostExplorerSuite) TestCalculateMspOpenAPIMonthlyCost() {
	assert := suite.Assert()
	err := mspAndCost(2, true, 211)
	err = mspAndCost(2, true, 212)
	assert.NoError(err)
	costExplorerModel := &costexplorer.CostExplorerModel{}
	res, err := costExplorerModel.CalculateMspOpenAPIMonthlyCost(2, timeutils.GetNow())
	runningTotal, err := decimal.NewFromString("336")
	assert.Equal(runningTotal, res.BillsOverView.RunningTotal)
	Price1, err := decimal.NewFromString("20")
	assert.Equal(Price1, res.SummaryByCustomer[0].MspCustomerBills.BillsServiceCosts[0].TotalCost)
	Price2, err := decimal.NewFromString("42")
	assert.Equal(Price2, res.SummaryByCustomer[0].MspCustomerBills.SummaryByProject.OtherCharges[0].RunningTotal)
	assert.Equal(res.SummaryByCustomer[0].MspCustomerBills.BillsServiceCosts[0].ServiceName, "TiDB Serverless")

}

func (suite *MspCostExplorerSuite) TestCalculateMspCustomerOpenAPIMonthlyCost2() {
	assert := suite.Assert()
	datainit.CreateBillingConfig(2, entity.BillingConfigType_MSP)

	(&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   testDedicated,
		Price:     decimal.NewFromInt(1),
		PriceType: entity.PriceType_Normal,
		Category:  "Node Compute Cost",
	})
	costEntity := &entity.UserUnitCost{
		CostID:                "test-1-1",
		TenantID:              2,
		CostTimeStamp:         timeutils.GetNow().Unix(),
		Unit:                  testDedicated,
		Cost:                  20,
		CostPrice:             decimal.NewFromFloat(20.006),
		RawPrice:              decimal.NewFromFloat(21.006),
		Status:                entity.UserUnitCostStatus_DONE,
		InvoiceID:             1,
		Labels:                map[string]string{"component": "COMPONENT_TYPE_TIKV", "project": testDedicated},
		BindedBillingConfigID: 1,
	}
	err := costEntity.Create(db.GetDB())
	assert.NoError(err)
	costEntity.CostID = "test-1-2"
	costEntity.InvoiceID = 2
	err = costEntity.Create(db.GetDB())
	assert.NoError(err)
	costExplorerModel := &costexplorer.CostExplorerModel{}
	res, err := costExplorerModel.CalculateMspCustomerOpenAPIMonthlyCost(2, 1, timeutils.GetNow())

	runningTotal, err := decimal.NewFromString("40.01")
	assert.Equal(runningTotal, res.BillsOverView.TotalCost)

	res2, err := costExplorerModel.GetMonthlyCostWithCache(2, 1, timeutils.GetNow().Year(), int(timeutils.GetNow().Month()))
	runningTotal2, err := decimal.NewFromString("40")
	assert.Equal(runningTotal2, res2.TotalCosts.CostPrice)

	datainit.CreateBillingConfig(3, entity.BillingConfigType_MSPCustomer)
	costEntity.TenantID = 3
	costEntity.BindedBillingConfigID = 2

	costEntity.CostID = "test-1-3"
	costEntity.InvoiceID = 11
	err = costEntity.Create(db.GetDB())
	assert.NoError(err)

	costEntity.CostID = "test-1-4"
	costEntity.InvoiceID = 12
	err = costEntity.Create(db.GetDB())
	assert.NoError(err)

	res3, err := costExplorerModel.CalculateMspCustomerOpenAPIMonthlyCost(3, 2, timeutils.GetNow())
	runningTotal3, err := decimal.NewFromString("40.01")
	assert.Equal(runningTotal3, res3.BillsOverView.TotalCost)

	res4, err := costExplorerModel.GetMonthlyCostWithCache(3, 2, timeutils.GetNow().Year(), int(timeutils.GetNow().Month()))
	runningTotal4, err := decimal.NewFromString("40.01")
	assert.Equal(runningTotal4, res4.TotalCosts.CostPrice)

}
