package tests

import (
	"encoding/json"
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/api"
	"github.com/pingcap/dbaas/billing2/config"
	"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/marketing"
	"github.com/pingcap/dbaas/billing2/model/marketing/unit_filter"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"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"
)

var rechargeModel = &marketing.RechargeCreditsModel{}

type RechargeCreditsTestSuite struct {
	tests.BillingTestSuite
	router                 *gin.Engine
	generalType            *forms.UnitFilter
	GeneralUnitFilterID    uint
	DefaultRechargePlanReq *forms.RechargeCreditsPlan
}

func TestRechargeCreditsSuite(t *testing.T) {
	suite.Run(t, new(RechargeCreditsTestSuite))
}

func (s *RechargeCreditsTestSuite) SetupTest() {
	s.BillingTestSuite.SetupTest()
	s.router = gin.New()
	s.router.Use(ginutils.ErrorMiddleWare)
	api.InitializeRechargePlanRouter(ginutils.MyRoute{Route: s.router})
	assert := s.Assert()

	err := datainit.CreatePaymentMock(1)
	assert.NoError(err)

	_, err = (&billing.UnitModel{}).CreateUnit(&forms.UnitInfo{
		UnitKey:   datainit.TestUnit,
		Price:     decimal.NewFromInt(1),
		PriceType: entity.PriceType_Normal,
	})
	assert.NoError(err)
	generalUnitFilter, err := (&unit_filter.Model{}).CreateUnitFilter(&forms.UnitFilter{
		UnitFilterSlim: forms.UnitFilterSlim{Name: "General", Description: "General"},
		General:        true,
	})
	s.generalType = generalUnitFilter

	assert.NoError(err)
	config.Get().Set(config.CreditsPricePackage, datainit.NoUsePackageId)
	timeutils.GetNowFunc = func() time.Time {
		return datainit.BillingDate
	}
	config.Get().Set(config.RechargeCreditsBeforeSeconds, 10800)

	s.DefaultRechargePlanReq = &forms.RechargeCreditsPlan{
		Value: decimal.NewFromInt(1),
		Cycle: entity.RechargePlanCycle_Month,
		Rule: forms.RechargeCreditsPlanRule{
			FirstCycleStartedAt:   forms.FirstCycleStartedAt_RightNow,
			FirstCycleFinishedAt:  forms.FirstCycleFinishedAt_EndOfNaturalCycle,
			FirstCreditsConvertor: forms.FirstCreditsConvertor_Full,
		},
		StartDate:   timeutils.GetNow(),
		ExpiredDate: timeutils.GetNow().Add(time.Hour * 24),
	}
}

func (s *RechargeCreditsTestSuite) TestRechargeCreditsPlan() {
	var err error
	assert := s.Assert()
	q := *s.DefaultRechargePlanReq
	q.Value = decimal.Zero
	w := tests.PerformRequest(s.router, "POST", "/recharge_plans", &q)
	assert.Equal(http.StatusBadRequest, w.Code)
	w = tests.PerformRequest(s.router, "POST", "/recharge_plans", s.DefaultRechargePlanReq)
	assert.Equal(http.StatusOK, w.Code)
	mainPlan := &forms.RechargeCreditsPlan{}
	err = json.Unmarshal(w.Body.Bytes(), mainPlan)
	assert.Nil(err)
	assert.Equal(s.DefaultRechargePlanReq.Value, mainPlan.Value)
	assert.Equal(entity.RechargeCreditsPlanSource_Web, mainPlan.Source)
	w = tests.PerformGetRequest(s.router, fmt.Sprintf("/recharge_plans/%d", mainPlan.PlanID))
	assert.Equal(http.StatusOK, w.Code)
	err = json.Unmarshal(w.Body.Bytes(), mainPlan)
	assert.NoError(err)
	assert.Equal(s.DefaultRechargePlanReq.Value, mainPlan.Value)
	assert.Equal(entity.RechargeCreditsPlanSource_Web, mainPlan.Source)
	systemMainPlan, err := rechargeModel.CreateRechargePlan(s.DefaultRechargePlanReq)
	assert.NoError(err)
	assert.Equal(entity.RechargeCreditsPlanSource_System, systemMainPlan.Source)
	w = tests.PerformGetRequest(s.router, "/recharge_plans")
	assert.Equal(http.StatusOK, w.Code)
	var list []*forms.RechargeCreditsPlan
	err = json.Unmarshal(w.Body.Bytes(), &list)
	assert.NoError(err)
	assert.Len(list, 1)
	assert.Equal(s.DefaultRechargePlanReq.Value, list[0].Value)
	query := struct {
		Value decimal.Decimal `json:"value"`
	}{Value: decimal.NewFromInt(-1)}
	w = tests.PerformRequest(s.router, "PATCH", fmt.Sprintf("/recharge_plans/%d", mainPlan.PlanID), &query)
	assert.Equal(http.StatusBadRequest, w.Code)
	query.Value = decimal.Zero
	w = tests.PerformRequest(s.router, "PATCH", fmt.Sprintf("/recharge_plans/%d", mainPlan.PlanID), &query)
	assert.Equal(http.StatusBadRequest, w.Code)
	query.Value = decimal.NewFromInt(1111)
	w = tests.PerformRequest(s.router, "PATCH", fmt.Sprintf("/recharge_plans/%d", mainPlan.PlanID), &query)
	assert.Equal(http.StatusOK, w.Code)
	err = json.Unmarshal(w.Body.Bytes(), mainPlan)
	assert.Nil(err)
	assert.Equal(query.Value, mainPlan.Value)
	tenantID := uint(1)
	tenantPlan, err := rechargeModel.EnableTenantRechargePlan(tenantID, mainPlan.PlanID)
	assert.NoError(err)
	assert.Equal(mainPlan.PlanID, tenantPlan.MainPlanID)
	assert.Equal(tenantID, tenantPlan.TenantID)
	w = tests.PerformGetRequest(s.router, fmt.Sprintf("/tenants/%d/recharge_plans/%d", tenantID, tenantPlan.PlanID))
	assert.Equal(http.StatusOK, w.Code)
	err = json.Unmarshal(w.Body.Bytes(), tenantPlan)
	assert.NoError(err)
	assert.Equal(mainPlan.PlanID, tenantPlan.MainPlanID)
	assert.Equal(uint(0), tenantPlan.ClusterID)
	assert.Nil(tenantPlan.ClosedAt)
	w = tests.PerformRequest(s.router, "DELETE", fmt.Sprintf("/tenants/%d/recharge_plans/%d", tenantID, tenantPlan.PlanID), nil)
	assert.Equal(http.StatusOK, w.Code)
	w = tests.PerformRequest(s.router, "DELETE", fmt.Sprintf("/recharge_plans/%d", mainPlan.PlanID), nil)
	assert.Equal(http.StatusOK, w.Code)
	mainPlan, err = rechargeModel.GetRechargePlan(mainPlan.PlanID)
	assert.NoError(err)
	assert.NotNil(mainPlan.ClosedAt)
	tenantPlan, err = rechargeModel.GetTenantRechargePlan(tenantPlan.PlanID)
	assert.NoError(err)
	assert.NotNil(tenantPlan.ClosedAt)
}
