package services

import (
	"database/sql"
	"testing"

	"quyun/v2/app/commands/testx"
	super_dto "quyun/v2/app/http/super/v1/dto"
	"quyun/v2/app/requests"
	"quyun/v2/database"
	"quyun/v2/database/models"
	"quyun/v2/pkg/consts"

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

type SuperTestSuiteInjectParams struct {
	dig.In

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

type SuperTestSuite struct {
	suite.Suite
	SuperTestSuiteInjectParams
}

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

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

func (s *SuperTestSuite) Test_ListUsers() {
	Convey("ListUsers", s.T(), func() {
		ctx := s.T().Context()
		database.Truncate(ctx, s.DB, models.TableNameUser)

		u1 := &models.User{Username: "user1", Nickname: "Alice"}
		u2 := &models.User{Username: "user2", Nickname: "Bob"}
		models.UserQuery.WithContext(ctx).Create(u1, u2)

		Convey("should list users", func() {
			filter := &super_dto.UserListFilter{
				Pagination: requests.Pagination{Page: 1, Limit: 10},
			}
			res, err := Super.ListUsers(ctx, filter)
			So(err, ShouldBeNil)
			So(res.Total, ShouldEqual, 2)

			items := res.Items.([]super_dto.UserItem)
			So(items[0].Username, ShouldEqual, "user2") // Desc order
		})

		Convey("should filter users", func() {
			filter := &super_dto.UserListFilter{
				Pagination: requests.Pagination{Page: 1, Limit: 10},
				Username:   lo.ToPtr("Alice"),
			}
			res, err := Super.ListUsers(ctx, filter)
			So(err, ShouldBeNil)
			So(res.Total, ShouldEqual, 1)
			items := res.Items.([]super_dto.UserItem)
			So(items[0].Username, ShouldEqual, "user1")
		})
	})
}

func (s *SuperTestSuite) Test_CreateTenant() {
	Convey("CreateTenant", s.T(), func() {
		ctx := s.T().Context()
		database.Truncate(ctx, s.DB, models.TableNameUser, models.TableNameTenant)

		u := &models.User{Username: "admin1"}
		models.UserQuery.WithContext(ctx).Create(u)

		Convey("should create tenant", func() {
			form := &super_dto.TenantCreateForm{
				Name:        "Super Tenant",
				Code:        "st1",
				AdminUserID: u.ID,
			}
			err := Super.CreateTenant(ctx, form)
			So(err, ShouldBeNil)

			t, _ := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.Code.Eq("st1")).First()
			So(t, ShouldNotBeNil)
			So(t.Name, ShouldEqual, "Super Tenant")
			So(t.UserID, ShouldEqual, u.ID)
			So(t.Status, ShouldEqual, consts.TenantStatusVerified)
		})
	})
}

func (s *SuperTestSuite) Test_WithdrawalApproval() {
	Convey("Withdrawal Approval", s.T(), func() {
		ctx := s.T().Context()
		database.Truncate(ctx, s.DB, models.TableNameOrder, models.TableNameUser, models.TableNameTenantLedger)

		u := &models.User{Username: "user_w", Balance: 1000} // Initial 10.00
		models.UserQuery.WithContext(ctx).Create(u)

		// Create Withdrawal Order (Pending)
		o1 := &models.Order{
			UserID:     u.ID,
			Type:       consts.OrderTypeWithdrawal,
			Status:     consts.OrderStatusCreated,
			AmountPaid: 500,
		}
		models.OrderQuery.WithContext(ctx).Create(o1)

		Convey("should list withdrawals", func() {
			filter := &super_dto.SuperOrderListFilter{Pagination: requests.Pagination{Page: 1, Limit: 10}}
			res, err := Super.ListWithdrawals(ctx, filter)
			So(err, ShouldBeNil)
			So(res.Total, ShouldEqual, 1)
		})

		Convey("should approve withdrawal", func() {
			err := Super.ApproveWithdrawal(ctx, o1.ID)
			So(err, ShouldBeNil)

			oReload, _ := models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(o1.ID)).First()
			So(oReload.Status, ShouldEqual, consts.OrderStatusPaid)
		})

		Convey("should reject withdrawal and refund", func() {
			// Another order
			o2 := &models.Order{
				UserID:     u.ID,
				Type:       consts.OrderTypeWithdrawal,
				Status:     consts.OrderStatusCreated,
				AmountPaid: 200,
			}
			models.OrderQuery.WithContext(ctx).Create(o2)

			// Assuming user balance was deducted when o2 was created (logic in creator service)
			// But here we set balance manually to 1000. Let's assume it was 1200 before.
			// Current balance 1000. Refund 200 -> Expect 1200.

			err := Super.RejectWithdrawal(ctx, o2.ID, "Invalid account")
			So(err, ShouldBeNil)

			oReload, _ := models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(o2.ID)).First()
			So(oReload.Status, ShouldEqual, consts.OrderStatusFailed)

			uReload, _ := models.UserQuery.WithContext(ctx).Where(models.UserQuery.ID.Eq(u.ID)).First()
			So(uReload.Balance, ShouldEqual, 1200)

			// Check Ledger
			l, _ := models.TenantLedgerQuery.WithContext(ctx).Where(models.TenantLedgerQuery.OrderID.Eq(o2.ID)).First()
			So(l, ShouldNotBeNil)
			So(l.Type, ShouldEqual, consts.TenantLedgerTypeAdjustment)
		})
	})
}
