package tests

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

	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/handler/centralhandler"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/api"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/middleware"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/pingcap/dbaas/billing2/utils/tests/datainit"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/suite"
)

type MSPCostExplorerOpenAPITestSuite struct {
	tests.BillingTestSuite
	router *gin.Engine
}

func (suite *MSPCostExplorerOpenAPITestSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	suite.router = gin.New()

}

type MockCentralHandlerMspOpenAPIImpl struct {
	centralhandler.CentralHandler
	tenants []forms.Tenant
}

func (*MSPCostExplorerOpenAPITestSuite) GetTenant(tenantID uint) (*forms.Tenant, error) {
	return &forms.Tenant{
		TenantID: tenantID,
		Status:   centralhandler.TenantStatusActive,
	}, nil
}
func (*MockCentralHandlerMspOpenAPIImpl) GetTenantIdByOpenAPIAccess(apiKey string) (tenantID uint64, err error) {
	return 1, nil
}

func (*MockCentralHandlerMspOpenAPIImpl) GetProjectInfo(projectID uint) (*forms.Project, error) {
	return &forms.Project{
		ID:   projectID,
		Name: "project",
	}, nil
}

func mockCentralUseMspOpenAPI() {
	central := &MockCentralHandlerMspOpenAPIImpl{}
	centralhandler.SetCentralHandler(central)
}
func TestMspCostExplorerOpenAPISuite(t *testing.T) {
	suite.Run(t, new(MSPCostExplorerOpenAPITestSuite))
}

func (suite *MSPCostExplorerOpenAPITestSuite) TestMspRouter() {
	assert := suite.Assert()
	suite.router.Use(ginutils.ErrorMiddleWare)
	datainit.CreateBillingConfig(1, entity.BillingConfigType_MSP)

	billingGroup := suite.router.Group("/api/v1/billing")
	ws := ginutils.MyRoute{Route: billingGroup}
	api.InitializeCostExplorerRouter(ginutils.MyRoute{Route: ws})

	billingOpenAPIGroup := suite.router.Group("/openapi/v1")
	wsOpenAPI := ginutils.MyRoute{Route: billingOpenAPIGroup}
	api.InitializeOpenAPICostExplorerRouter(ginutils.MyRoute{Route: wsOpenAPI})

	mspBillingOpenAPIGroup := suite.router.Group("/openapi/v1/msp")
	wsMspOpenAPI := ginutils.MyRoute{Route: mspBillingOpenAPIGroup}
	api.InitializeMspOpenAPICostExplorerRouter(ginutils.MyRoute{Route: wsMspOpenAPI})

	_, err := datainit.CreateInvoice(uint(1))
	assert.NoError(err)

	// billing openapi
	w := tests.PerformRequest(suite.router, "GET", "/openapi/v1/tenants/1/bills/"+timeutils.GetNow().Format("2006-01"), nil)
	assert.Equal(http.StatusInternalServerError, w.Code)

	// billing openapi
	w = tests.PerformRequest(suite.router, "GET", "/openapi/v1/msp/tenants/1/bills/"+timeutils.GetNow().Format("2006-01"), nil)
	assert.Equal(http.StatusOK, w.Code)

	// billing web
	query := &forms.CostExplorerQuery{
		StartDate: datainit.BillingDate,
		EndDate:   datainit.BillingDate.Add(time.Hour),
	}
	w = tests.PerformRequest(suite.router, "POST", "/api/v1/billing/tenants/1/costexplorer", query)
	assert.Equal(http.StatusOK, w.Code)
	res := &forms.CostExplorerResult{}
	assert.NoError(json.Unmarshal(w.Body.Bytes(), res))
	assert.Equal(res.BindedInvoiceCosts.CostPrice, decimal.NewFromInt(1))
}

func (suite *MSPCostExplorerOpenAPITestSuite) TestMspOpenAPIPathNormalize() {
	assert := suite.Assert()
	suite.router.Use(ginutils.ErrorMiddleWare,
		middleware.UserMiddleware,
		middleware.OpenAPIPathNormalize(suite.router),
		middleware.HeaderMiddleware,
		//middleware.NewRBACMiddleWare(Path2ResourceMap),
	)
	datainit.CreateBillingConfig(1, entity.BillingConfigType_MSP)
	_, err := datainit.CreateInvoice(uint(1))
	assert.NoError(err)
	mockCentralUseMspOpenAPI()
	billingGroup := suite.router.Group("/api/v1/billing")
	ws := ginutils.MyRoute{Route: billingGroup}
	api.InitializeCostExplorerRouter(ginutils.MyRoute{Route: ws})

	billingOpenAPIGroup := suite.router.Group("/v1beta1/msp")
	wsOpenAPI := ginutils.MyRoute{Route: billingOpenAPIGroup}
	api.InitializeMspOpenAPICostExplorerRouter(ginutils.MyRoute{Route: wsOpenAPI})
	path := fmt.Sprintf("/v1beta1/msp/bills/%s", timeutils.GetNow().Format("2006-01"))
	w := tests.PerformRequestWithOpenAPIHeader(suite.router, "GET", path, nil)
	assert.Equal(http.StatusOK, w.Code)
	res := &forms.BillsExplorer{}
	assert.NoError(json.Unmarshal(w.Body.Bytes(), res), w.Body.String())

	// return err .
	path = fmt.Sprintf("/v1beta1/msp/bills/%s", timeutils.GetMonthDate(timeutils.GetNow()).AddDate(0, -7, 0).Format("2006-01"))
	w = tests.PerformRequestWithOpenAPIHeader(suite.router, "GET", path, nil)
	assert.Equal(http.StatusBadRequest, w.Code)
	ress := gin.H{}
	json.Unmarshal(w.Body.Bytes(), &ress)
	assert.NotNil(ress["error"])

	path = fmt.Sprintf("/v1beta1/msp/bills/%s", timeutils.GetNow().AddDate(0, 1, 0).Format("2006-01"))
	w = tests.PerformRequestWithOpenAPIHeader(suite.router, "GET", path, nil)
	assert.Equal(http.StatusBadRequest, w.Code)

	query := &forms.CostExplorerQuery{
		StartDate: datainit.BillingDate,
		EndDate:   datainit.BillingDate.Add(time.Hour),
	}
	w = tests.PerformRequest(suite.router, "POST", "/api/v1/billing/tenants/1/costexplorer", query)
	assert.Equal(http.StatusOK, w.Code)
	res1 := &forms.CostExplorerResult{}
	assert.NoError(json.Unmarshal(w.Body.Bytes(), res1), w.Body.String())
	assert.Equal(res1.BindedInvoiceCosts.CostPrice, decimal.NewFromInt(1))
}
