package services

import (
	"context"
	"database/sql"
	"testing"

	"quyun/v2/app/commands/testx"
	user_dto "quyun/v2/app/http/v1/dto"
	"quyun/v2/database"
	"quyun/v2/database/models"
	"quyun/v2/pkg/consts"

	. "github.com/smartystreets/goconvey/convey"
	"github.com/stretchr/testify/suite"
	"go.ipao.vip/atom/contracts"
	"go.uber.org/dig"
)

type WalletTestSuiteInjectParams struct {
	dig.In

	DB       *sql.DB
	Initials []contracts.Initial `group:"initials"`
}

type WalletTestSuite struct {
	suite.Suite
	WalletTestSuiteInjectParams
}

func Test_Wallet(t *testing.T) {
	providers := testx.Default().With(Provide)

	testx.Serve(providers, t, func(p WalletTestSuiteInjectParams) {
		suite.Run(t, &WalletTestSuite{WalletTestSuiteInjectParams: p})
	})
}

func (s *WalletTestSuite) Test_GetWallet() {
	Convey("GetWallet", s.T(), func() {
		ctx := s.T().Context()
		tenantID := int64(1)
		database.Truncate(ctx, s.DB, models.TableNameUser, models.TableNameOrder)

		u := &models.User{Username: "wallet_user", Balance: 5000} // 50.00
		models.UserQuery.WithContext(ctx).Create(u)
		ctx = context.WithValue(ctx, consts.CtxKeyUser, u.ID)

		// Create Orders
		o1 := &models.Order{
			TenantID: 0, UserID: u.ID, Type: consts.OrderTypeRecharge, Status: consts.OrderStatusPaid,
			AmountPaid: 5000,
		}
		o2 := &models.Order{
			TenantID: 1, UserID: u.ID, Type: consts.OrderTypeContentPurchase, Status: consts.OrderStatusPaid,
			AmountPaid: 1000,
		}
		models.OrderQuery.WithContext(ctx).Create(o1, o2)

		Convey("should return balance and transactions", func() {
			res, err := Wallet.GetWallet(ctx, tenantID, u.ID)
			So(err, ShouldBeNil)
			So(res.Balance, ShouldEqual, 50.0)
			So(len(res.Transactions), ShouldEqual, 2)

			// Order by CreatedAt Desc
			types := []string{res.Transactions[0].Type, res.Transactions[1].Type}
			So(types, ShouldContain, "income")
			So(types, ShouldContain, "expense")
		})
	})
}

func (s *WalletTestSuite) Test_Recharge() {
	Convey("Recharge", s.T(), func() {
		ctx := s.T().Context()
		tenantID := int64(1)
		database.Truncate(ctx, s.DB, models.TableNameUser, models.TableNameOrder)

		u := &models.User{Username: "recharge_user"}
		models.UserQuery.WithContext(ctx).Create(u)
		ctx = context.WithValue(ctx, consts.CtxKeyUser, u.ID)

		Convey("should create recharge order", func() {
			form := &user_dto.RechargeForm{Amount: 100.0}
			res, err := Wallet.Recharge(ctx, tenantID, u.ID, form)
			So(err, ShouldBeNil)
			So(res.OrderID, ShouldNotBeEmpty)

			// Verify order
			o, _ := models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.Type.Eq(consts.OrderTypeRecharge)).First()
			So(o, ShouldNotBeNil)
			So(o.AmountPaid, ShouldEqual, 10000)
			So(o.TenantID, ShouldEqual, 0)
		})
	})
}
