package tests

import (
	"testing"

	"github.com/pingcap/dbaas/billing2/config"
	"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/costexplorer"
	"github.com/pingcap/dbaas/billing2/model/creditriskcontrol"
	"github.com/pingcap/dbaas/billing2/model/payment"
	"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 CostQuotaTestSuite struct {
	tests.BillingTestSuite
}

func TestCostQuotaSuite(t *testing.T) {
	suite.Run(t, new(CostQuotaTestSuite))
}

var costQuotaModel = &creditriskcontrol.CostQuotaModel{}

func (s *CostQuotaTestSuite) TestGetTenantDefaultCostQuota() {
	assert := s.Assert()
	tenantID := uint(1)
	info, err := costQuotaModel.GetTenantCostQuota(tenantID, 1)
	assert.NoError(err)
	assert.Equal(info.CostQuotaLimit, decimal.NewFromInt(int64(config.Get().GetInt(config.DefaultCostQuota))))

	creditRiskLevel, err := creditRiskLevelModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})

	assert.NoError(err)
	config.Get().SetDefault(config.DefaultCreditRiskLevel, creditRiskLevel.CreditRiskLevelID)

	quotaLimit := decimal.NewFromInt(123)

	costQuota, err := costQuotaModel.SetCreditRiskLevelQuota(creditRiskLevel.CreditRiskLevelID, &forms.CostQuota{
		CostQuotaLimit: quotaLimit,
	})

	assert.NoError(err)
	assert.Equal(costQuota.CostQuotaLimit, quotaLimit)

	info, err = costQuotaModel.GetTenantCostQuota(tenantID, 1)
	assert.NoError(err)
	assert.Equal(info.CostQuotaLimit, quotaLimit)

	info, err = costQuotaModel.GetCreditRiskLevelQuota(creditRiskLevel.CreditRiskLevelID)
	assert.NoError(err)
	assert.Equal(info.CostQuotaLimit, quotaLimit)
}

func (s *CostQuotaTestSuite) TestGetTenantCostQuota() {
	assert := s.Assert()
	tenantID := uint(1)
	creditRiskLevel, err := creditRiskLevelModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})
	assert.NoError(err)
	_, err = costQuotaModel.SetCreditRiskLevelQuota(creditRiskLevel.CreditRiskLevelID, &forms.CostQuota{
		CostQuotaLimit: decimal.NewFromInt(100),
	})
	assert.NoError(err)

	creditRiskLevel1, err := creditRiskLevelModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})
	assert.NoError(err)
	_, err = costQuotaModel.SetCreditRiskLevelQuota(creditRiskLevel1.CreditRiskLevelID, &forms.CostQuota{
		CostQuotaLimit: decimal.NewFromInt(200),
	})
	assert.NoError(err)

	err = creditRiskLevelModel.SetTenantCreditRiskLevel(tenantID, creditRiskLevel.CreditRiskLevelID)
	assert.NoError(err)

	info, err := costQuotaModel.GetTenantCostQuota(tenantID, 1)
	assert.NoError(err)
	assert.Equal(info.CostQuotaLimit, decimal.NewFromInt(100))

	err = creditRiskLevelModel.SetTenantCreditRiskLevel(tenantID, creditRiskLevel1.CreditRiskLevelID)
	assert.NoError(err)

	info, err = costQuotaModel.GetTenantCostQuota(tenantID, 1)
	assert.NoError(err)
	assert.Equal(info.CostQuotaLimit, decimal.NewFromInt(200))

	info, err = costQuotaModel.SetTenantCostQuota(tenantID, &forms.CostQuota{
		CostQuotaLimit: decimal.NewFromInt(321),
	})
	assert.NoError(err)
	assert.Equal(info.CostQuotaLimit, decimal.NewFromInt(321))

	info, err = costQuotaModel.GetTenantCostQuota(tenantID, 1)
	assert.NoError(err)
	assert.Equal(info.CostQuotaLimit, decimal.NewFromInt(321))
}

func (s *CostQuotaTestSuite) TestCreateCostQuotaInvoice() {
	assert := s.Assert()
	tenantID := uint(1)
	datainit.CreateBillingConfig(tenantID, entity.BillingConfigType_DirectCustomer)
	err := datainit.CreatePaymentMock(tenantID)
	assert.NoError(err)
	timeutils.SetMockNow(datainit.BillingDate)
	flag, err := costQuotaModel.CreateCostQuotaInvoice(tenantID, 1)
	assert.NoError(err)
	assert.False(flag)

	config.Get().Set(config.DefaultCostQuota, 500)
	err = datainit.CreatePricePackageAndBind(tenantID)
	assert.NoError(err)

	err = datainit.SendMeasureWithTime(tenantID, datainit.BillingDate, 500)
	assert.NoError(err)

	cost, err := (&costexplorer.CostExplorerModel{}).GetMonthlyCost(tenantID, 1, datainit.BillingDate.Year(), int(datainit.BillingDate.Month()))
	assert.NoError(err)
	assert.Equal(cost.UnbindedInvoiceCosts.CostPrice, decimal.NewFromInt(500))

	flag, err = costQuotaModel.CreateCostQuotaInvoice(tenantID, 1)
	assert.NoError(err)
	assert.True(flag)

	invoices, err := (&payment.InvoiceModel{}).ListAllInvoices(tenantID, 1, false)
	assert.NoError(err)
	assert.Len(invoices, 1)
	assert.Equal(invoices[0].Status, entity.InvoiceStatus_GENERATED)
	assert.Equal(invoices[0].CostPrice, decimal.NewFromInt(500))

	_, err = costQuotaModel.SetTenantCostQuota(tenantID, &forms.CostQuota{
		CostQuotaLimit: entity.NoLimitCostQuota,
	})
	assert.NoError(err)
	datainit.SendMeasureWithTime(tenantID, datainit.BillingDate, 50000)
	flag, err = costQuotaModel.CreateCostQuotaInvoice(tenantID, 1)
	assert.NoError(err)
	assert.False(flag)
}

func (s *CostQuotaTestSuite) TestGetTenantCostQuotaWithAlwaysNoLimit() {
	assert := s.Assert()
	tenantID := uint(1)
	datainit.CreateBillingConfig(tenantID, entity.BillingConfigType_DirectCustomer)
	creditRiskLevel, err := creditRiskLevelModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})
	assert.NoError(err)
	_, err = costQuotaModel.SetCreditRiskLevelQuota(creditRiskLevel.CreditRiskLevelID, &forms.CostQuota{
		CostQuotaLimit: decimal.NewFromInt(100),
	})
	assert.NoError(err)
	err = creditRiskLevelModel.SetTenantCreditRiskLevel(tenantID, creditRiskLevel.CreditRiskLevelID)
	assert.NoError(err)

	paymentMethod := entity.PaymentMethod{
		TenantID:              tenantID,
		PaymentType:           entity.PaymentType_STRIPE,
		ManagedBySales:        true,
		Payable:               true,
		BindedBillingConfigID: 1,
	}
	err = paymentMethod.Create(db.GetDB())
	assert.NoError(err)

	info, err := costQuotaModel.GetTenantCostQuota(tenantID, 1)
	assert.NoError(err)
	assert.Equal(info.CostQuotaLimit, entity.NoLimitCostQuota)

}
