package tests

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

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/pingcap/dbaas/billing2/api"
	"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"
	"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"
	"github.com/stripe/stripe-go/v76"
)

type MockTaxStripeHandler struct {
	paymenthandler.StripePaymentHandler
	paymenthandler.PaymentProvider
}

func (*MockTaxStripeHandler) CalculateTax(billingInfo *forms.BillingInformation, subCode string, price decimal.Decimal) (*forms.StripeCalculateTaxResult, error) {
	rate := decimal.RequireFromString("0.07")
	return &forms.StripeCalculateTaxResult{
		Amount:            price.Mul(rate),
		Inclusive:         false,
		TaxAbilityReason:  "standard",
		Country:           billingInfo.CountryCode,
		PercentageDecimal: rate.Mul(decimal.NewFromInt(100)).String(),
		State:             billingInfo.State,
		TaxType:           string(stripe.TaxRateTaxTypeSalesTax),
	}, nil
}

func (*MockTaxStripeHandler) CreateTaxRate(rate *forms.TaxRate) (*forms.TaxRate, error) {
	rate.StripeTaxRateID = uuid.NewString()
	return rate, nil
}

func (*MockTaxStripeHandler) GetTaxRate(rateID string) (*forms.TaxRate, error) {
	return &forms.TaxRate{
		StripeTaxRateID:  rateID,
		Value:            decimal.NewFromInt(7),
		DisplayName:      "Sales Tax",
		TaxAbilityReason: "standard",
		Inclusive:        false,
		Country:          "US",
		State:            "CA",
		TaxType:          string(stripe.TaxRateTaxTypeSalesTax),
		TenantID:         1,
		TaxCode:          config.Get().GetString(config.TiDBCloudStripeProductTaxCode),
	}, nil
}

func (*MockTaxStripeHandler) InactiveTaxRate(id string) error {
	return nil
}

type MockTaxPaymentHandlerImpl struct {
	paymenthandler.PaymentHandler
}

func (*MockTaxPaymentHandlerImpl) GetProvider(string) (paymenthandler.PaymentProvider, error) {
	return &MockTaxStripeHandler{}, nil
}

func MockTaxPaymentHandler() {
	paymenthandler.SetPaymentHandler(&MockTaxPaymentHandlerImpl{})
}

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

func TestTaxPaymentTestSuite(t *testing.T) {
	suite.Run(t, new(TaxPaymentTestSuite))
}

func (suite *TaxPaymentTestSuite) SetupTest() {
	suite.BillingTestSuite.SetupTest()
	MockTaxPaymentHandler()
	suite.router = gin.New()
	suite.router.Use(ginutils.ErrorMiddleWare)
	config.Get().Set(config.EnableTaxCalculation, true)
	api.InitializeTaxRateRouter(ginutils.MyRoute{Route: suite.router})
	api.InitializeBillingInfoRouter(ginutils.MyRoute{Route: suite.router})
}

func (suite *TaxPaymentTestSuite) TearDownTest() {
	suite.BillingTestSuite.TearDownTest()
	config.Get().Set(config.EnableTaxCalculation, false)
}

func (suite *TaxPaymentTestSuite) TestFlushTaxRate() {
	assert := suite.Assert()
	tenantID := uint(1)
	ent := entity.BillingInformation{
		TenantID:    tenantID,
		CountryCode: "US",
		State:       "CA",
	}
	assert.NoError(datainit.CreateCompany())
	assert.NoError(ent.Create(db.GetDB()))
	w := tests.PerformRequest(suite.router, "POST", "/tenants/1/tax_rates", nil)
	assert.Equal(http.StatusOK, w.Code)
	flushedTaxRate := forms.TaxRate{}
	err := json.Unmarshal(w.Body.Bytes(), &flushedTaxRate)
	assert.NoError(err)
	w = tests.PerformGetRequest(suite.router, "/tenants/1/billing_info")
	assert.Equal(http.StatusOK, w.Code)
	info := forms.BillingInformation{}
	err = json.Unmarshal(w.Body.Bytes(), &info)
	assert.NoError(err)
	assert.Equal(&flushedTaxRate, info.TaxRate)
}
