package tests

import (
	"fmt"
	"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/handler/paymenthandler/utils"
	"github.com/pingcap/dbaas/billing2/model/billing"
	"github.com/pingcap/dbaas/billing2/model/billingconfig"
	"github.com/pingcap/dbaas/billing2/model/costexplorer"
	"github.com/pingcap/dbaas/billing2/model/marketing"
	"github.com/pingcap/dbaas/billing2/model/marketing/discount"
	"github.com/pingcap/dbaas/billing2/model/marketing/plan"
	"github.com/pingcap/dbaas/billing2/model/marketing/unit_filter"
	"github.com/pingcap/dbaas/billing2/model/payment"
	"github.com/pingcap/dbaas/billing2/model/payment/paymentsub"
	"github.com/pingcap/dbaas/billing2/utils/panicIf"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/pingcap/dbaas/billing2/utils/tests/datainit"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/suite"
	"testing"
	"time"
)

var invoiceModel = &payment.InvoiceModel{}

type InvoiceSuite struct {
	tests.BillingTestSuite
}

func TestInvoiceSuite(t *testing.T) {
	suite.Run(t, new(InvoiceSuite))
}

func (suite *InvoiceSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	_, err := datainit.CreateBillingConfig(1, entity.BillingConfigType_DirectCustomer)
	panicIf.Error(err)
	config.Get().Set(config.EnableTaxCalculation, false)
}

func (suite *InvoiceSuite) TestSplitCPPOInvoice() {
	tests.MockCentralHandler()
	assert := suite.Assert()
	var err error
	tenantID := uint(1)
	billingConfigID, err := (&billingconfig.BillingConfigModel{}).GetActiveBillingConfigID(tenantID)
	assert.NoError(err)
	invoiceable := true
	subCode := "222"

	unitFilterModel := unit_filter.Model{}
	ukNodeGCPAsiaSmall := "node:gcp:asia:DEPLOY_TYPE_DEDICATED:small"
	ukStorageGCPAsiaStand := "storage:gcp:asia:DEPLOY_TYPE_DEDICATED:standard"
	_, err = (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   ukNodeGCPAsiaSmall,
		Price:     decimal.RequireFromString("1.00273"),
		Provider:  "gcp",
		PriceType: entity.PriceType_Normal,
	})
	assert.NoError(err)
	_, err = (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   ukStorageGCPAsiaStand,
		Provider:  "gcp",
		Price:     decimal.RequireFromString("1.00588"),
		PriceType: entity.PriceType_Normal,
	})
	assert.NoError(err)
	computeFilter, err := unitFilterModel.CreateUnitFilter(&forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{Name: "Compute", Description: "Compute"},
		UnitKeyPattern: "node:[\\w\\-]+:[\\w\\-]+:DEPLOY_TYPE_DEDICATED:[\\w\\-\\.]+$",
		General:        false,
	})

	paymentEnt := entity.PaymentMethod{
		TenantID:              tenantID,
		PaymentCode:           "122",
		Payable:               true,
		PaymentSubCode:        &subCode,
		PaymentType:           entity.PaymentType_AWSCPPOReseller,
		Invoiceable:           &invoiceable,
		BindedBillingConfigID: billingConfigID,
		Status:                entity.PaymentMethodStatus_Active,
	}
	err = paymentEnt.Create(db.GetDB())
	assert.NoError(err)
	err = (&plan.TenantPlanModel{}).EnableTenantOnDemandPlan(tenantID)
	assert.NoError(err)

	assert.NoError(err)
	config.Get().Set(config.AWSCPPODiscountedProductUnitFilterIDs, []string{fmt.Sprint(computeFilter.FilterID)})
	measureModel := &billing.MeasureModel{}
	cost0, err := measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: datainit.BillingDate.Unix(),
		Unit:          ukNodeGCPAsiaSmall,
		Cost:          110,
	})
	assert.NoError(err)
	assert.False(cost0.Internal)
	assert.Equal(entity.InvoiceUnbindID, cost0.InvoiceID)
	err = entity.NewUserUnitCostQuerySet(db.GetDB()).InvoiceIDEq(entity.InvoiceUnbindID).GetUpdater().SetInternal(true).Update() // manually delete cost
	assert.NoError(err)
	cost1, err := measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: datainit.BillingDate.Unix(),
		Unit:          ukNodeGCPAsiaSmall,
		Cost:          110,
	})
	assert.NoError(err)
	assert.Equal(entity.InvoiceUnbindID, cost1.InvoiceID)
	assert.False(cost1.Internal)
	cost2, err := measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: datainit.BillingDate.Unix(),
		Unit:          ukStorageGCPAsiaStand,
		Cost:          110,
	})
	assert.NoError(err)
	assert.Equal(entity.InvoiceUnbindID, cost2.InvoiceID)
	assert.False(cost2.Internal)
	credits, err := (&marketing.CreditsPackageModel{}).CreateCreditsPackage(tenantID, &forms.CreditsPackage{
		StartDate:             datainit.BillingDate,
		ExpiredDate:           time.Now().AddDate(0, 0, 1),
		TotalCredits:          decimal.NewFromInt(100),
		UnitFilters:           []*forms.UnitFilterSlim{{FilterID: computeFilter.FilterID}},
		BindedBillingConfigID: &billingConfigID,
	})
	assert.NoError(err)
	assert.Equal(credits.TotalCredits, decimal.NewFromInt(100))
	cost3, err := measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: datainit.BillingDate.Unix(),
		Unit:          ukNodeGCPAsiaSmall,
		Cost:          100,
	})
	assert.NoError(err)
	assert.Equal(entity.InvoiceNoUseID, cost3.InvoiceID)
	dis, err := (&discount.Model{}).CreateDiscount(tenantID, &forms.Discount{
		StartedAt:             datainit.BillingDate,
		ExpiredAt:             time.Now().AddDate(0, 0, 1),
		Value:                 decimal.RequireFromString("0.2"),
		UnitFilters:           []*forms.UnitFilterSlim{{FilterID: computeFilter.FilterID}},
		BindedBillingConfigID: &billingConfigID,
	})
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("0.2"), dis.Value)
	cost4, err := measureModel.CostUnits(tenantID, &forms.UnitCosts{
		CostTimeStamp: datainit.BillingDate.Unix(),
		Unit:          ukNodeGCPAsiaSmall,
		Cost:          100,
	})
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("20.0546"), cost4.CostPrice)
	inv, err := invoiceModel.CreateCostQuotaInvoice(tenantID, billingConfigID, datainit.BillingDate)
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("240.99"), inv.CostPrice)
	assert.Equal(decimal.RequireFromString("321.21"), inv.RawPrice)
	_, err = invoiceModel.ConfirmInvoice(inv.InvoiceID)
	assert.NoError(err)
	p, err := (&paymentsub.PaymentModel{}).GetActivePaymentMethod(tenantID, billingConfigID)
	assert.NoError(err)
	assert.Equal(paymentEnt.PaymentCode, p.PaymentCode)
	assert.Equal(subCode, *p.PaymentSubCode)
	queryResult, err := (&costexplorer.CostExplorerModel{}).CalculateCostV2(inv.TenantID, []uint{*inv.BindedBillingConfigID}, &forms.CostExplorerQueryV2{
		InvoiceID:  inv.InvoiceID,
		IgnoreBeta: true,
	})
	assert.NoError(err)
	assert.Equal(decimal.RequireFromString("110.3"), queryResult.InternalCosts.TotalCosts.RawPrice)
	invoices, err := utils.SplitCPPOInvoice(inv.InvoiceID, p)
	assert.NoError(err)
	assert.Len(invoices, 2)
	assert.Equal(p.PaymentCode, *invoices[0].PaymentCode)
	assert.Equal(decimal.RequireFromString("130.35"), invoices[0].CostPrice)
	assert.Equal(decimal.RequireFromString("210.57"), invoices[0].RawPrice)
	assert.Equal(*p.PaymentSubCode, *invoices[1].PaymentCode)
	assert.Equal(decimal.RequireFromString("110.64"), invoices[1].CostPrice)
	assert.Equal(inv.CostPrice, invoices[0].CostPrice.Add(invoices[1].CostPrice))
	assert.Equal(inv.RawPrice, invoices[0].RawPrice.Add(invoices[1].RawPrice))
}
