package services

import (
	"database/sql"
	"testing"

	"quyun/v2/app/commands/testx"
	tenantdto "quyun/v2/app/http/tenant/dto"
	"quyun/v2/database"
	"quyun/v2/database/models"
	"quyun/v2/pkg/consts"
	"quyun/v2/pkg/utils"

	. "github.com/smartystreets/goconvey/convey"
	"github.com/stretchr/testify/suite"

	_ "go.ipao.vip/atom"
	"go.ipao.vip/atom/contracts"
	"go.ipao.vip/gen/types"
	"go.uber.org/dig"
)

type TenantTestSuiteInjectParams struct {
	dig.In

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

type TenantTestSuite struct {
	suite.Suite

	TenantTestSuiteInjectParams
}

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

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

func (t *TenantTestSuite) Test_TenantUserCount() {
	Convey("test get tenants user count", t.T(), func() {
		database.Truncate(t.T().Context(), t.DB, models.TableNameTenant)

		result, err := Tenant.TenantUserCountMapping(t.T().Context(), []int64{1, 2})
		So(err, ShouldBeNil)
		So(result, ShouldHaveLength, 2)
		t.T().Logf("%s", utils.MustJsonString(result))
	})
}

func (t *TenantTestSuite) Test_AddUser() {
	Convey("Tenant.AddUser", t.T(), func() {
		ctx := t.T().Context()
		tenantID := int64(1)
		userID := int64(2)

		database.Truncate(ctx, t.DB, models.TableNameTenantUser)

		Convey("首次添加成员成功", func() {
			err := Tenant.AddUser(ctx, tenantID, userID)
			So(err, ShouldBeNil)

			m, err := Tenant.FindTenantUser(ctx, tenantID, userID)
			So(err, ShouldBeNil)
			So(m, ShouldNotBeNil)
			So(m.TenantID, ShouldEqual, tenantID)
			So(m.UserID, ShouldEqual, userID)
		})

		Convey("重复添加应幂等返回成功", func() {
			So(Tenant.AddUser(ctx, tenantID, userID), ShouldBeNil)
			So(Tenant.AddUser(ctx, tenantID, userID), ShouldBeNil)
		})
	})
}

func (t *TenantTestSuite) Test_SetUserRole() {
	Convey("Tenant.SetUserRole", t.T(), func() {
		ctx := t.T().Context()
		tenantID := int64(1)
		userID := int64(2)

		database.Truncate(ctx, t.DB, models.TableNameTenantUser)

		So(Tenant.AddUser(ctx, tenantID, userID), ShouldBeNil)

		Convey("设置为 tenant_admin 成功", func() {
			err := Tenant.SetUserRole(ctx, tenantID, userID, consts.TenantUserRoleTenantAdmin)
			So(err, ShouldBeNil)

			m, err := Tenant.FindTenantUser(ctx, tenantID, userID)
			So(err, ShouldBeNil)
			So(m, ShouldNotBeNil)
			So(len(m.Role), ShouldEqual, 1)
			So(m.Role[0], ShouldEqual, consts.TenantUserRoleTenantAdmin)
		})

		Convey("设置为 member 成功", func() {
			err := Tenant.SetUserRole(ctx, tenantID, userID, consts.TenantUserRoleMember)
			So(err, ShouldBeNil)

			m, err := Tenant.FindTenantUser(ctx, tenantID, userID)
			So(err, ShouldBeNil)
			So(m, ShouldNotBeNil)
			So(len(m.Role), ShouldEqual, 1)
			So(m.Role[0], ShouldEqual, consts.TenantUserRoleMember)
		})
	})
}

func (t *TenantTestSuite) Test_RemoveUser() {
	Convey("Tenant.RemoveUser", t.T(), func() {
		ctx := t.T().Context()
		tenantID := int64(1)
		userID := int64(2)

		database.Truncate(ctx, t.DB, models.TableNameTenantUser)

		Convey("移除不存在成员应幂等返回成功", func() {
			So(Tenant.RemoveUser(ctx, tenantID, userID), ShouldBeNil)
		})

		Convey("移除已存在成员成功", func() {
			So(Tenant.AddUser(ctx, tenantID, userID), ShouldBeNil)
			So(Tenant.RemoveUser(ctx, tenantID, userID), ShouldBeNil)

			_, err := Tenant.FindTenantUser(ctx, tenantID, userID)
			So(err, ShouldNotBeNil)
		})
	})
}

func (t *TenantTestSuite) Test_AdminTenantUsersPage() {
	Convey("Tenant.AdminTenantUsersPage", t.T(), func() {
		ctx := t.T().Context()
		tenantID := int64(1)

		database.Truncate(ctx, t.DB, models.TableNameTenantUser, models.TableNameUser)

		u1 := &models.User{
			Username: "u1",
			Password: "pw",
			Roles:    types.NewArray([]consts.Role{consts.RoleUser}),
			Status:   consts.UserStatusVerified,
		}
		So(u1.Create(ctx), ShouldBeNil)

		u2 := &models.User{
			Username: "u2",
			Password: "pw",
			Roles:    types.NewArray([]consts.Role{consts.RoleUser}),
			Status:   consts.UserStatusVerified,
		}
		So(u2.Create(ctx), ShouldBeNil)

		So(Tenant.AddUser(ctx, tenantID, u1.ID), ShouldBeNil)
		So(Tenant.AddUser(ctx, tenantID, u2.ID), ShouldBeNil)
		So(Tenant.SetUserRole(ctx, tenantID, u2.ID, consts.TenantUserRoleTenantAdmin), ShouldBeNil)

		Convey("不加过滤应返回用户信息", func() {
			pager, err := Tenant.AdminTenantUsersPage(ctx, tenantID, &tenantdto.AdminTenantUserListFilter{})
			So(err, ShouldBeNil)
			So(pager.Total, ShouldEqual, 2)

			items, ok := pager.Items.([]*tenantdto.AdminTenantUserItem)
			So(ok, ShouldBeTrue)
			So(len(items), ShouldEqual, 2)
			So(items[0].User, ShouldNotBeNil)
		})

		Convey("按 role=tenant_admin 过滤", func() {
			role := consts.TenantUserRoleTenantAdmin
			pager, err := Tenant.AdminTenantUsersPage(ctx, tenantID, &tenantdto.AdminTenantUserListFilter{Role: &role})
			So(err, ShouldBeNil)
			So(pager.Total, ShouldEqual, 1)
		})
	})
}
