package tests

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

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/api"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/model/creditriskcontrol"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/stretchr/testify/suite"
)

var creditRiskModel = &creditriskcontrol.CreditRiskLevelModel{}

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

func (suite *CreditRiskTestSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	suite.router = gin.New()
	suite.router.Use(ginutils.ErrorMiddleWare)
	api.InitializeCreditRiskLevelRouter(ginutils.MyRoute{Route: suite.router})
}

func TestCreditRiskSuite(t *testing.T) {
	suite.Run(t, new(CreditRiskTestSuite))
}

func (suite *CreditRiskTestSuite) TestGetAllCreditRiskLevels() {
	assert := suite.Assert()
	w := tests.PerformGetRequest(suite.router, "/credit_risk_levels")
	assert.Equal(http.StatusOK, w.Code)
	_, err := creditRiskModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})
	assert.NoError(err)
	w = tests.PerformGetRequest(suite.router, "/credit_risk_levels")
	assert.Equal(http.StatusOK, w.Code)
	response := []forms.CreditRiskLevel{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.Nil(err)
	assert.Equal(len(response), 1)
	assert.Equal(response[0].Name, "test")
}

func (suite *CreditRiskTestSuite) TestGetCreditRiskLevel() {
	assert := suite.Assert()
	info, err := creditRiskModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})
	assert.NoError(err)
	w := tests.PerformGetRequest(suite.router, fmt.Sprintf("/credit_risk_levels/%d", info.CreditRiskLevelID))
	assert.Equal(http.StatusOK, w.Code)
	response := forms.CreditRiskLevel{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.Nil(err)
	assert.Equal(response.Name, "test")
}

func (suite *CreditRiskTestSuite) TestUpdateCreditRiskLevel() {
	assert := suite.Assert()
	info, err := creditRiskModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})
	assert.NoError(err)
	w := tests.PerformRequest(suite.router, "PUT", fmt.Sprintf("/credit_risk_levels/%d", info.CreditRiskLevelID), &forms.CreditRiskLevelInput{
		Name: "test1",
	})
	assert.Equal(http.StatusOK, w.Code)

	info, err = creditRiskModel.GetCreditRiskLevel(info.CreditRiskLevelID)
	assert.NoError(err)
	assert.Equal(info.Name, "test1")
}

func (suite *CreditRiskTestSuite) TestDeleteCreditRiskLevel() {
	assert := suite.Assert()
	config.Get().Set(config.DefaultCreditRiskLevel, 0)

	info, err := creditRiskModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})
	assert.NoError(err)
	w := tests.PerformRequest(suite.router, "DELETE", fmt.Sprintf("/credit_risk_levels/%d", info.CreditRiskLevelID), nil)
	assert.Equal(http.StatusOK, w.Code)

	_, err = creditRiskModel.GetCreditRiskLevel(info.CreditRiskLevelID)
	assert.ErrorIs(err, billing_errors.ObjectNotExistsError)
}

func (suite *CreditRiskTestSuite) TestCreateCreditRiskLevel() {
	assert := suite.Assert()
	w := tests.PerformRequest(suite.router, "POST", "/credit_risk_levels", &forms.CreditRiskLevelInput{
		Name: "test1",
	})
	assert.Equal(http.StatusOK, w.Code)
	response := forms.CreditRiskLevel{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(err)
	assert.NotZero(response.CreditRiskLevelID)
	assert.Equal(response.Name, "test1")

	info, err := creditRiskModel.GetCreditRiskLevel(response.CreditRiskLevelID)
	assert.NoError(err)
	assert.Equal(info.Name, "test1")
}

func (suite *CreditRiskTestSuite) TestGetTenantCreditRiskLevel() {
	assert := suite.Assert()
	tenantID := uint(1)
	info, err := creditRiskModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})
	assert.NoError(err)
	err = creditRiskModel.SetTenantCreditRiskLevel(tenantID, info.CreditRiskLevelID)
	assert.NoError(err)
	response := forms.CreditRiskLevel{}
	w := tests.PerformGetRequest(suite.router, fmt.Sprintf("/tenants/%d/credit_risk_level", tenantID))
	assert.Equal(http.StatusOK, w.Code, w.Body.String())

	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(err)
	assert.NotZero(response.CreditRiskLevelID)
	assert.Equal(response.Name, "test")

}

func (suite *CreditRiskTestSuite) TestSetTenantCreditRiskLevel() {
	assert := suite.Assert()
	tenantID := uint(1)
	info, err := creditRiskModel.CreateCreditRiskLevel(&forms.CreditRiskLevel{
		Name: "test",
	})
	assert.NoError(err)
	w := tests.PerformRequest(suite.router, "PUT", fmt.Sprintf("/tenants/%d/credit_risk_level", tenantID), &forms.CreditRiskLevelIDInput{
		CreditRiskLevelID: info.CreditRiskLevelID,
	})
	assert.Equal(http.StatusOK, w.Code, w.Body.String())

	info, err = creditRiskModel.GetTenantCreditRiskLevel(tenantID)
	assert.NoError(err)
	assert.Equal(info.Name, "test")

}
