package tests

import (
	"fmt"
	"net/http"
	"sort"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/api"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/constant"
	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/paymenthandler"
	"github.com/pingcap/dbaas/billing2/handler/paymenthandler/impl"
	"github.com/pingcap/dbaas/billing2/model/billingconfig"
	"github.com/pingcap/dbaas/billing2/model/marketing/unit_filter"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/panicIf"
	"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 BillingConfigTestSuite struct {
	tests.BillingTestSuite
	router *gin.Engine
}

func TestBillingConfigTestSuite(t *testing.T) {
	suite.Run(t, new(BillingConfigTestSuite))
}

func (suite *BillingConfigTestSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	suite.router = gin.New()
	suite.router.Use(ginutils.ErrorMiddleWare)
	api.InitializeBillingConfigRouter(ginutils.MyRoute{Route: suite.router})
	tenantID := uint(1)
	_, err := datainit.CreateBillingConfig(tenantID, entity.BillingConfigType_DirectCustomer)
	panicIf.Error(err)
}

func (suite *BillingConfigTestSuite) TestCreatCPPOBillingConfig() {
	var err error
	assert := suite.Assert()
	customerID := uint(1)
	resellerID := uint(2)
	timeutils.SetMockNow(datainit.BillingDate)
	mockCentral()
	paymenthandler.SetPaymentHandler(&impl.PaymentHandlerImpl{})
	generalUnitFilter, err := (&unit_filter.Model{}).CreateUnitFilter(&forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{Name: "General", Description: "General"},
		General:        true,
	})
	assert.NoError(err)
	networkUnitFilter, err := (&unit_filter.Model{}).CreateUnitFilter(&forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{Name: "Network", Description: "Network"},
		General:        false,
		UnitKeyPattern: "network",
	})
	assert.NoError(err)
	config.Get().Set(config.GeneralCreditTypeID, generalUnitFilter.FilterID)
	tests.MockCentralHandler()
	startedAt := datainit.BillingDate.AddDate(0, 0, 1)
	expiredAt := startedAt.AddDate(1, 0, 0)
	dis := forms.Discount{
		Value:       decimal.RequireFromString("0.72"),
		ExpiredAt:   expiredAt,
		Usage:       constant.DiscountUsageCPPO,
		UnitFilters: []*forms.UnitFilterSlim{{FilterID: generalUnitFilter.FilterID}},
	}
	networkDis := forms.Discount{
		Value:       decimal.RequireFromString("0.9"),
		ExpiredAt:   expiredAt,
		UnitFilters: []*forms.UnitFilterSlim{{FilterID: networkUnitFilter.FilterID}},
	}
	code := "!23"
	support := &forms.SupportPlanInput{
		SupportPlan: forms.SupportPlan{
			Public:                 false,
			Level:                  entity.SupportPlanLevel_Standard,
			Type:                   entity.SupportPlanType_PercentagePrice,
			FixedChargePrice:       decimal.NewFromInt(10),
			SupportPricePercentage: decimal.RequireFromString("0.21"),
		},
		ExpiredDate: &expiredAt,
	}
	w := tests.PerformRequest(suite.router, "POST", fmt.Sprintf("/tenants/%d/cppo_billing_config", customerID), &forms.CPPOBillingConfig{
		MarketplaceResellerBillingConfig: forms.MarketplaceResellerBillingConfig{
			TenantID:        customerID,
			ResellerId:      resellerID,
			EffectiveDate:   &startedAt,
			PaymentCode:     code,
			Discounts:       []forms.Discount{dis, networkDis},
			SupportPlanInfo: support,
		},
	})
	assert.Equal(http.StatusOK, w.Code)
	model := &billingconfig.BillingConfigModel{}
	billingConfigList, err := model.GetTenantIDScheduledBillingConfig(customerID)
	assert.NoError(err)
	assert.Len(billingConfigList, 1)
	assert.Equal(entity.BillingConfigType_MarketplaceResellerManagedCustomer, billingConfigList[0].Type)
	assert.True(billingConfigList[0].BindSupportPlanOnEnabled)
	assert.Equal(expiredAt, *billingConfigList[0].SupportPlanExpiredDate)

	supportPlan, err := supportPlanModel.GetSupportPlan(*billingConfigList[0].SupportPlanID)
	assert.NoError(err)
	assert.False(supportPlan.Public)
	assert.Equal(support.Type, supportPlan.Type)
	assert.Equal(support.FixedChargePrice, support.FixedChargePrice)
	assert.Equal(support.SupportPricePercentage, supportPlan.SupportPricePercentage)
	assert.False(supportPlan.IsSupportFreeTrial)
	resellerBillingConfig, err := model.GetBillingConfigByManagedTenantID(customerID)
	assert.NoError(err)
	assert.Equal(entity.BillingConfigType_MarketplaceReseller, resellerBillingConfig.Type)
	resellerPayment, err := paymentModel.GetActivePaymentMethod(resellerID, resellerBillingConfig.BillingConfigID)
	assert.NoError(err)
	assert.True(resellerPayment.PaymentMeta.EnlargeMarketplaceMetering)
	assert.Equal(entity.PaymentType_AWSCPPOReseller, resellerPayment.PaymentType)
	assert.Equal(code, resellerPayment.PaymentCode)
	assert.Nil(resellerPayment.PaymentSubCode)
	assert.Equal(dis.Value, resellerPayment.PaymentMeta.EnlargerDiscount)
	assert.False(resellerPayment.Invoiceable)

	discounts, err := discountModel.ListTenantDiscounts(resellerID, resellerBillingConfig.BillingConfigID)
	assert.NoError(err)
	assert.Len(discounts, 2)
	sort.Slice(discounts, func(i, j int) bool {
		return discounts[i].Value.LessThan(discounts[j].Value)
	})
	for i, d := range []forms.Discount{dis, networkDis} {
		assert.Equal(d.Value, discounts[i].Value)
		assert.Equal(expiredAt, discounts[i].ExpiredAt)
		assert.Equal(startedAt, discounts[i].StartedAt)
		assert.Len(discounts[i].UnitFilters, len(d.UnitFilters))
		assert.Equal(d.UnitFilters[0].FilterID, discounts[i].UnitFilters[0].FilterID)
	}
}
