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/forms"
	"github.com/pingcap/dbaas/billing2/handler/paymenthandler"
	"github.com/pingcap/dbaas/billing2/utils/ginutils"
	"github.com/pingcap/dbaas/billing2/utils/tests"
	"github.com/stretchr/testify/suite"
)

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

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

func TestStripePaymentSuite(t *testing.T) {
	suite.Run(t, new(StripePaymentTestSuite))
}

type MockStripeHandler struct {
	paymenthandler.StripePaymentHandler
}

func (*MockStripeHandler) SetupIntent(userCode string) (*forms.SetupIntent, error) {
	return &forms.SetupIntent{
		PublicKey:    "mock_key",
		ClientSecret: "mock_secret",
	}, nil
}

func (*MockStripeHandler) GetUserCode(tenant forms.Tenant) (string, error) {
	return fmt.Sprintf("mock_%d", tenant.TenantID), nil
}

func (*MockStripeHandler) Pay(userCode string, invoice *forms.Invoice) error {
	return nil
}

func (*MockStripeHandler) Payable(userCode string) (bool, error) {
	return false, nil
}

func (*MockStripeHandler) ListPaymentMethods(userCode string) ([]forms.StripePaymentMethod, error) {
	return []forms.StripePaymentMethod{
		{ID: "mock_id"},
	}, nil
}

func (*MockStripeHandler) ListStripeInvoices(string) ([]forms.StripeInvoice, error) {
	return []forms.StripeInvoice{
		{
			Status: "Paid",
		},
	}, nil
}

func (*MockStripeHandler) SetPaymentMethodDefault(userCode, methodId string) error {
	return nil
}

type MockPaymentHandlerImpl struct {
}

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

func MockPaymentHandler() {
	paymenthandler.SetPaymentHandler(&MockPaymentHandlerImpl{})
}

func (suite *StripePaymentTestSuite) TestSetupIntent() {
	assert := suite.Assert()

	tests.MockCentralHandler()
	MockPaymentHandler()
	w := tests.PerformRequest(suite.router, "POST", "/tenants/1/payments/stripe/setup_intent", nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
	ret := forms.SetupIntent{}
	err := json.NewDecoder(w.Body).Decode(&ret)
	assert.NoError(err)
	assert.Equal(ret.ClientSecret, "mock_secret")
	assert.Equal(ret.PublicKey, "mock_key")
}

func (suite *StripePaymentTestSuite) TestListPayments() {
	assert := suite.Assert()

	tests.MockCentralHandler()
	MockPaymentHandler()
	w := tests.PerformRequest(suite.router, "GET", "/tenants/1/payments/stripe/payment_methods", nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
	ret := []forms.StripePaymentMethod{}
	err := json.NewDecoder(w.Body).Decode(&ret)
	assert.NoError(err)
	assert.Equal(len(ret), 0)
	assert.NoError(api.CreateStripeIfNotExists(1))
	w = tests.PerformRequest(suite.router, "GET", "/tenants/1/payments/stripe/payment_methods", nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
	ret = []forms.StripePaymentMethod{}
	err = json.NewDecoder(w.Body).Decode(&ret)
	assert.NoError(err)
	assert.Equal(len(ret), 1)
	assert.Equal(ret[0].ID, "mock_id")
}

func (suite *StripePaymentTestSuite) TestSetupIntentConfirm() {
	assert := suite.Assert()

	tests.MockCentralHandler()
	MockPaymentHandler()
	assert.NoError(api.CreateStripeIfNotExists(1))
	w := tests.PerformRequest(suite.router, "PUT", "/tenants/1/payments/stripe/setup_intent/confirm", nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
}

func (suite *StripePaymentTestSuite) TestSetDefaultPayment() {
	assert := suite.Assert()

	tests.MockCentralHandler()
	MockPaymentHandler()
	assert.NoError(api.CreateStripeIfNotExists(1))
	w := tests.PerformRequest(suite.router, "PUT", "/tenants/1/payments/stripe/payment_methods/mock_id/default", nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
}

func (suite *StripePaymentTestSuite) TestListInvoices() {
	assert := suite.Assert()

	tests.MockCentralHandler()
	MockPaymentHandler()
	assert.NoError(api.CreateStripeIfNotExists(1))
	w := tests.PerformRequest(suite.router, "GET", "/tenants/1/payments/stripe/invoices", nil)
	assert.Equal(http.StatusOK, w.Code, w.Body.String())
	resp := []forms.StripeInvoice{}
	assert.NoError(json.Unmarshal(w.Body.Bytes(), &resp))
	assert.Len(resp, 1)
	assert.Equal(resp[0].Status, "Paid")
}
