package group_r

import (
	"context"
	"encoding/json"
	"fmt"
	"git.hilo.cn/hilo-common/domain"
	"git.hilo.cn/hilo-common/mycontext"
	"git.hilo.cn/hilo-common/resource/mysql"
	"git.hilo.cn/hilo-common/resource/redisCli"
	"git.hilo.cn/hilo-common/rpc"
	"git.hilo.cn/hilo-common/utils"
	"github.com/gin-gonic/gin"
	"github.com/spf13/cast"
	"gorm.io/gorm"
	"hilo-group/_const/enum/game_e"
	"hilo-group/_const/enum/group_e"
	"hilo-group/_const/enum/mgr_e"
	"hilo-group/_const/enum/msg_e"
	"hilo-group/_const/redis_key"
	"hilo-group/cv/gift_cv"
	"hilo-group/cv/group_cv"
	"hilo-group/cv/group_power_cv"
	"hilo-group/cv/medal_cv"
	"hilo-group/cv/user_cv"
	"hilo-group/domain/cache/res_c"
	"hilo-group/domain/cache/room_c"
	"hilo-group/domain/model/game_m"
	"hilo-group/domain/model/group_m"
	"hilo-group/domain/model/mgr_m"
	"hilo-group/domain/model/noble_m"
	"hilo-group/domain/model/res_m"
	"hilo-group/domain/model/rocket_m"
	"hilo-group/domain/model/user_m"
	"hilo-group/domain/model/version_m"
	"hilo-group/domain/service/group_s"
	"hilo-group/myerr"
	"hilo-group/myerr/bizerr"
	"hilo-group/req"
	"hilo-group/resp"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

// @Tags 群组
// @Summary 查询我创建的群组
// @Accept application/x-www-form-urlencoded
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Success 200 {object} group_cv.JoinedGroupInfo
// @Router /v1/imGroup/ownGroup [get]
func GetOwnGroup(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)

	userId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}

	model := domain.CreateModelContext(myContext)

	groupIds, err := group_m.GetGroups(model.Db, userId, group_e.GROUP_OWNER)
	if err != nil {
		return myContext, err
	}
	if len(groupIds) <= 0 {
		resp.ResponseOk(c, []group_cv.JoinedGroupInfo{})
		return myContext, nil
	}

	myService := domain.CreateService(myContext)
	result, _, err := group_cv.BuildJoinedGroupInfo(myService, userId, groupIds, 1, 1)
	if err != nil {
		return myContext, err
	}

	resp.ResponseOk(c, result)
	return myContext, nil
}

// @Tags 群组
// @Summary 查询某人的(公开)群组(新)
// @Accept application/x-www-form-urlencoded
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param userExternalId path string true "用户ExternalId"
// @Success 200 {object} group_cv.TheirGroupsInfo
// @Router /v1/imGroup/theirGroup/{userExternalId} [get]
func GetTheirGroups(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)

	externalId := c.Param("userExternalId")
	if len(externalId) <= 0 {
		return myContext, myerr.NewSysError("userExternalId 为必填项")
	}

	model := domain.CreateModelContext(myContext)

	user, err := user_m.GetUserByExtId(model, externalId)
	if err != nil {
		return myContext, err
	}
	userId := user.ID
	if userId == 0 {
		return myContext, bizerr.InvalidParameter
	}

	data, err := group_m.GetJoinedGroups(model.Db, userId)
	if err != nil {
		return myContext, err
	}

	result := group_cv.TheirGroupsInfo{Total: 0}

	groupIds := make([]string, 0)
	for _, i := range data {
		groupIds = append(groupIds, i.GroupId)
	}
	groupInfo, err := group_m.BatchGetGroupInfo(model, groupIds)
	if err != nil {
		return myContext, err
	}
	owners := make([]uint64, 0)
	for _, i := range groupInfo {
		owners = append(owners, i.Owner)
	}

	theirRoles, err := group_m.GetRolesByUser(model, userId)
	if err != nil {
		return myContext, err
	}
	model.Log.Info("GetTheirGroups: theirRoles - ", theirRoles)

	powerIds, powerNames, powerNameplates, powerGrades, err := group_power_cv.BatchGetGroupPower(model.Db, owners)
	if err != nil {
		return myContext, err
	}
	groupMedals, err := group_m.BatchGetMedals(model.Db, groupIds)
	if err != nil {
		return myContext, err
	}
	resMedal, err := res_m.MedalGetAllMap(model.Db)
	if err != nil {
		return myContext, err
	}

	roomMicUserMap, err := group_m.BatchGetAllMicUser(model, groupIds)
	if err != nil {
		return myContext, err
	}
	model.Log.Infof("GetTheirGroups: groupIds %v, roomMicUserMap %v", groupIds, roomMicUserMap)

	uids := owners
	micUsersMap := make(map[string][]uint64, 0)
	for _, i := range groupIds {
		micUsersMap[i] = make([]uint64, 0)

		u := roomMicUserMap[i]
		if len(u) >= 4 {
			micUsersMap[i] = u[0:4]
		} else {
			micUsersMap[i] = u
		}
		uids = append(uids, micUsersMap[i]...)
	}

	uids = utils.UniqueSliceUInt64(uids)
	userTiny, err := user_cv.GetUserTinyMap(uids)
	if err != nil {
		return myContext, err
	}

	model.Log.Infof("GetTheirGroups: userTinyMap: %v", userTiny)

	roomCount, err := group_m.BatchGetRoomCount(model, groupIds)
	if err != nil {
		return nil, err
	}

	onMic, err := group_cv.CheckMicHasUserByGroup(groupIds)
	if err != nil {
		return myContext, err
	}
	model.Log.Info("GetTheirGroups: onMic - ", onMic)

	countryInfo, err := res_c.GetCountryIconMap(model)
	if err != nil {
		return myContext, err
	}

	supportLevels, err := group_s.NewGroupService(myContext).GetWeekMaxSupportLevelMap()
	if err != nil {
		return myContext, err
	}

	rr := rocket_m.RocketResult{}
	maxStageMap, err := rr.GetMaxStage(mysql.Db, groupIds)
	if err != nil {
		return myContext, err
	}

	visitCount, err := group_m.BatchGetRoomVisitCount(model.Log, groupIds)
	if err != nil {
		return myContext, err
	}

	roomEnterTime, err := room_c.GetUserRoomVisit(userId)
	if err != nil {
		return myContext, err
	}
	//model.Log.Infof("BuildJoinedGroupInfo, roomEnterTime: %v", roomEnterTime)
	// 正在进行的游戏
	games := game_m.GetNotEndGamesMap(model)

	for _, i := range data {
		gi := groupInfo[i.GroupId]
		// 只要公开的群
		if len(gi.Password) == 0 {
			result.Total++

			role := group_e.GROUP_MEMBER
			r, exists := theirRoles[i.GroupId]
			if exists {
				role = r
			}

			micUsers := make([]user_cv.CvUserTiny, 0)
			for _, j := range micUsersMap[i.GroupId] {
				micUsers = append(micUsers, userTiny[j])
			}

			var maxStage *uint16 = nil
			if s, ok := maxStageMap[i.GroupId]; ok {
				maxStage = &s
			}

			medals := make([]medal_cv.PicElement, 0)
			if m, ok := groupMedals[i.GroupId]; ok {
				for _, j := range m {
					mId := uint32(j)
					if e, ok := resMedal[mId]; ok {
						medals = append(medals, medal_cv.PicElement{
							PicUrl: e.PicUrl,
						})
					}
				}
			}
			// 补上房间流水勋章
			var pe *medal_cv.PicElement
			_, pe, err = medal_cv.GetGroupConsumeMedal(model, i.GroupId)
			if err != nil {
				model.Log.Infof("GetTheirGroups: GetGroupConsumeMedal: %s", err.Error())
			} else if pe != nil {
				medals = append(medals, medal_cv.PicElement{PicUrl: pe.PicUrl})
			}

			elem := group_cv.JoinedGroupInfo{
				PopularGroupInfo: group_cv.PopularGroupInfo{
					GroupInfo: group_cv.GroupInfo{
						GroupBasicInfo: group_cv.GroupBasicInfo{
							GroupId:             gi.TxGroupId,
							Name:                gi.Name,
							Introduction:        gi.Introduction,
							Notification:        gi.Notification,
							FaceUrl:             gi.FaceUrl,
							Code:                gi.Code,
							CountryIcon:         countryInfo[gi.Country],
							SupportLevel:        supportLevels[i.GroupId],
							GroupInUserDuration: visitCount[i.GroupId],
							MicNumType:          int(gi.MicNumType),
							GroupMedals:         medals,
						},
						//HasOnMic:       onMicScore[i.GroupId] != 0,
						GroupPowerId:        powerIds[gi.Owner],
						GroupPowerName:      powerNames[gi.Owner],
						GroupPowerNameplate: powerNameplates[gi.Owner],
						GroupPower: group_cv.GroupPower{
							Id:        powerIds[gi.Owner],
							Name:      powerNames[gi.Owner],
							Nameplate: powerNameplates[gi.Owner],
							Grade:     powerGrades[gi.Owner],
						},
					},
					MicUsers:      micUsers,
					RoomUserCount: uint(roomCount[i.GroupId]),
					MaxStage:      maxStage,
					GameTypes:     games[gi.TxGroupId],
				},
				LastEnterTime: roomEnterTime[i.GroupId],
			}

			if role == group_e.GROUP_OWNER {
				result.OwnGroups = append(result.OwnGroups, elem)
			} else {
				result.Groups = append(result.Groups, elem)
			}
		}
	}

	// fixme: 按最近进入时间?
	sort.Slice(result.OwnGroups, func(i, j int) bool {
		return result.OwnGroups[i].LastEnterTime > result.OwnGroups[j].LastEnterTime ||
			result.OwnGroups[i].LastEnterTime == result.OwnGroups[j].LastEnterTime && result.OwnGroups[i].GroupId < result.OwnGroups[j].GroupId
	})
	// fixme:按角色排?
	sort.Slice(result.Groups, func(i, j int) bool {
		return theirRoles[result.Groups[i].GroupId] > theirRoles[result.Groups[j].GroupId] ||
			theirRoles[result.Groups[i].GroupId] == theirRoles[result.Groups[j].GroupId] && result.Groups[i].GroupId < result.Groups[j].GroupId
	})

	resp.ResponseOk(c, result)
	return myContext, nil
}

// @Tags 群组
// @Summary 客户端组件加载完成
// @Accept application/x-www-form-urlencoded
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param groupId path string true "群ID"
// @Success 200
// @Router /v1/imGroup/pluginReady/{groupId} [put]
func PluginReady(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)

	userId, externalId, err := req.GetUserIdAndExtId(c, myContext)
	if err != nil {
		return myContext, err
	}

	groupId := c.Param("groupId")
	if len(groupId) <= 0 {
		return myContext, myerr.NewSysError("groupId 为必填项")
	}

	model := domain.CreateModelContext(myContext)

	groupId, err = group_m.ToImGroupId(model, groupId)
	if err != nil {
		return myContext, err
	}

	model.Log.Infof("PluginReady user %d, %s", userId, externalId)

	/** fixme:这协议没用了?

	go func(gi *group_m.GroupInfo) {
		defer func() {
			if r := recover(); r != nil {
				//打印错误堆栈信息
				mylogrus.MyLog.Errorf("PluginReady SYSTEM ACTION PANIC: %v, stack: %v", r, string(debug.Stack()))
			}
		}()

		if err := myService.OnJoinGroup(externalId, groupId); err != nil {
			model.Log.Infof("PluginReady, OnJoinGroup err %v", err)
		}
	}(groupInfo)*/

	resp.ResponseOk(c, nil)
	return myContext, nil
}

// @Tags 群组
// @Summary 查询房间信息
// @Accept application/x-www-form-urlencoded
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param groupId path string true "群ID"
// @Success 200 {object} group_cv.RoomInfo
// @Router /v1/imGroup/roomInfo/{groupId} [get]
func GetRoomInfo(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)
	start := time.Now()
	groupId := c.Param("groupId")
	if len(groupId) <= 0 {
		return myContext, myerr.NewSysError("groupId 为必填项")
	}

	model := domain.CreateModelContext(myContext)

	groupInfo, err := group_m.GetInfoByTxGroupId(model, groupId)
	if err != nil {
		return myContext, err
	}
	if groupInfo == nil {
		return myContext, bizerr.GroupNotFound
	}
	groupId = groupInfo.ImGroupId

	memberCount, err := group_m.GetMemberCount(model.Db, groupId)
	if err != nil {
		return myContext, err
	}

	countryInfo, err := res_c.GetCountryIconMap(model)
	if err != nil {
		return myContext, err
	}

	// 版本控制
	_deviceType, _ := c.Get(mycontext.DEVICETYPE)
	_appVersion, _ := c.Get(mycontext.APP_VERSION)
	deviceType, appVersion := cast.ToString(_deviceType), cast.ToString(_appVersion)
	vc := version_m.GetVersionControlByPlatform(model, deviceType)
	allow, err := utils.CompareVersion(appVersion, fmt.Sprintf("<= %s", vc.AuditVersion))

	model.Log.Infof("GetRoomInfo cost 0:%v", time.Now().Sub(start).Seconds())

	themeUrl := ""
	var themeId uint64 = 0
	var themeType uint8 = 1
	if groupInfo.ThemeId > 0 {
		//官方主题
		// 找不到的,默认为空(可能被后台删了)
		themeType = 1
		themes, _ := res_m.GroupThemeGetAll(model.Db)
		for _, i := range themes {
			if i.ID == uint64(groupInfo.ThemeId) {
				themeUrl = i.Url
				themeId = i.ID
				break
			}
		}
	} else {
		//可能是自定义主题
		themeId, themeUrl, err = group_m.GetShowCustomTheme(model, groupInfo.ImGroupId)
		if err != nil {
			return myContext, err
		}
		if themeId != 0 {
			themeType = 2
		}
	}

	result := group_cv.RoomInfo{
		GroupBasicInfo: group_cv.GroupBasicInfo{
			GroupId:        groupInfo.TxGroupId,
			Name:           groupInfo.Name,
			Introduction:   groupInfo.Introduction,
			Notification:   groupInfo.Notification,
			FaceUrl:        groupInfo.FaceUrl,
			MemberNum:      memberCount,
			Code:           groupInfo.Code,
			CountryIcon:    countryInfo[groupInfo.Country],
			MicNumType:     int(groupInfo.MicNumType),
			TouristMic:     groupInfo.TouristMic,
			TouristSendMsg: groupInfo.TouristSendMsg,
			TouristSendPic: groupInfo.TouristSendPic,
			MemberFee:      groupInfo.MemberFee,
		},
		MicOn:       groupInfo.MicOn,
		LoadHistory: groupInfo.LoadHistory,
		ThemeId:     themeId,
		ThemeUrl:    themeUrl,
		ThemeType:   themeType,
		Role:        group_e.GROUP_VISITOR,
		GameConfig: &game_m.GameConfig{
			Ludo:   &game_m.GameConfigDiamond{Diamond: game_e.GameLudoDiamondList},
			Uno:    &game_m.GameConfigDiamond{Diamond: game_e.GameLudoDiamondList},
			Domino: &game_m.GameConfigDiamond{Diamond: game_e.GameLudoDiamondList},
		},
	}

	model.Log.Infof("GetRoomInfo cost 1:%v", time.Now().Sub(start).Seconds())
	roles, orderList, err := group_m.GetRolesInGroup(model, groupId)
	if err != nil {
		return myContext, err
	}

	userId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}

	isGroupMember, err := group_m.IsGroupMember(model.Db, groupId, userId)
	if err != nil {
		return myContext, err
	}
	if isGroupMember {
		result.Role = group_e.GROUP_MEMBER
	}

	emptyStr := ""
	if len(groupInfo.Password) > 0 {
		// 代表有密码
		result.Password = &emptyStr
	}

	//userIds := make([]uint64, 0)
	for u, r := range roles {
		//userIds = append(userIds, u)

		if u == userId {
			if r == group_e.GROUP_OWNER || r == group_e.GROUP_MANAGER {
				// 如果用户是OW或经理,可以看到密码,同时设置角色
				result.Role = r

				if len(groupInfo.Password) > 0 {
					result.Password = &groupInfo.Password
				}
			} else if r == group_e.GROUP_ADMIN {
				// 如果用户是管理员,仅设置角色
				result.Role = r
			}
		}
	}

	model.Log.Infof("GetRoomInfo cost 2:%v", time.Now().Sub(start).Seconds())
	users, err := user_cv.GetUserTinyMap(orderList)
	if err != nil {
		return myContext, err
	}

	for _, i := range orderList {
		result.RoleMembers = append(result.RoleMembers, group_cv.SimpleRoleInfo{
			ExternalId: users[i].ExternalId,
			Role:       roles[i],
		})
	}

	// ---- 以下部分相对没那么重要,失败也不返回 ----

	supportLevels, err := group_s.NewGroupService(myContext).GetWeekMaxSupportLevelMap()
	if err != nil {
		model.Log.Infof("GetRoomInfo: GetWeekMaxSupportLevelMap: %s", err.Error())
	}
	result.SupportLevel = supportLevels[groupId]

	result.DiceNum = group_e.GROUP_DICE_NUM_DEFAULT
	result.DiceType = 1 // 1: [0-9] 2:[1-6]
	gs := group_m.GroupSetting{GroupId: groupId}
	err = gs.Get(model.Db)
	if err == nil {
		result.DiceNum = gs.DiceNum
		result.DiceType = gs.DiceType
	} else if err != gorm.ErrRecordNotFound {
		model.Log.Infof("GetRoomInfo: GroupSetting: %s", err.Error())
	}

	result.WelcomeText, _, _, err = group_s.NewGroupService(myContext).GetWelcomeText(groupInfo)
	if err != nil {
		return myContext, err
	}
	result.WelcomeText = strings.ReplaceAll(result.WelcomeText, "@%s", "")

	model.Log.Infof("GetRoomInfo cost 3:%v", time.Now().Sub(start).Seconds())
	// 补上房间流水勋章
	var pe *medal_cv.PicElement
	result.TotalConsume, pe, err = medal_cv.GetGroupConsumeMedal(model, groupId)
	if err != nil {
		model.Log.Infof("GetRoomInfo: GetGroupConsumeMedal: %s", err.Error())
	} else if pe != nil {
		result.GroupMedals = append(result.GroupMedals, medal_cv.PicElement{PicUrl: pe.PicUrl})
	}

	var resMedals []res_m.ResMedal
	err = mysql.Db.Model(&res_m.ResMedal{}).
		Joins("INNER JOIN group_medal m ON m.res_medal_id = res_medal.id").
		Where("m.im_group_id = ?", groupId).
		Where("(expire_at is null or expire_at > ?)", time.Now().Format(utils.DATETIME_FORMAT)).
		Order("res_medal.sort").
		Find(&resMedals).Error
	if err == nil {
		for _, r := range resMedals {
			result.GroupMedals = append(result.GroupMedals, medal_cv.PicElement{
				PicUrl:  r.PicUrl,
				SvgaUrl: r.SvgaUrl,
			})
		}
	} else {
		model.Log.Errorf("GetRoomInfo: group_medal: %s", err.Error())
	}
	model.Log.Infof("GetRoomInfo cost 4:%v", time.Now().Sub(start).Seconds())
	// 获取国家信息
	country, area, err := user_m.GetUserCountryArea(model, userId)
	if err != nil {
		model.Log.Errorf("GetRoomInfo 获取国家资源错误 userId:%d, err:%v", userId, err)
		return myContext, err
	}

	rb := mgr_m.RoomBanner{Status: mysql.YES}
	rows, err := rb.GetRoomBanners(model.Db, allow)
	if err == nil {
		for _, i := range rows {
			// 是否在banner设定区域内
			if (i.Area == 1 || i.Area == 2) && area != i.Area { // 不在区域内
				continue
			}
			if i.Area == 3 && !utils.IsInStringList(country, i.CountryList) {
				continue
			}

			result.Banners = append(result.Banners, group_cv.BannerElement{
				H5Url:     i.Url,
				BannerUrl: i.Image,
				ActionUrl: i.Url, // 支持direction=1横屏
			})
		}
	} else {
		model.Log.Errorf("GetRoomInfo: GetRoomBanners: %s", err.Error())
	}
	model.Log.Infof("GetRoomInfo cost 5:%v", time.Now().Sub(start).Seconds())
	result.LuckyWheel, err = group_cv.GetLuckWheelState(model, groupId) // todo check 耗时
	if err != nil {
		model.Log.Errorf("GetRoomInfo: GetLuckWheelState: %s", err.Error())
	}
	model.Log.Infof("GetRoomInfo cost 6:%v", time.Now().Sub(start).Seconds())
	// 群主的详情
	owner, err := user_cv.GetRoomInfoOwner(model, groupInfo.Owner, userId)
	result.Owner = user_cv.UserDetailToRoomInfoOwner(owner)
	if err != nil {
		model.Log.Errorf("GetRoomInfo: GetUserBase: %s", err.Error())
	}
	model.Log.Infof("GetRoomInfo cost 7:%v", time.Now().Sub(start).Seconds())
	// 进场特效类型

	var entryEffectType int // 进场特效类型 1: CP 2:神秘人 3:贵族 4:vip ,顺序从小到大
	var cpEntryEffect bool
	if cp, _ := rpc.GetUserCpEntryEffect(model, userId); cp != nil {
		cpEntryEffect = cp.CpEntryEffect
		result.CpLevel = cp.CpLevel
		result.CpUserAvatar = cp.CpUserAvatar
	}
	if user, _ := user_cv.GetUserDetail(model, userId, userId); user != nil {
		if user.IsVip {
			entryEffectType = 4
		}
		if user.Noble.Level > 0 {
			entryEffectType = 3
		}
		for _, v := range user.Svip.Privileges {
			if len(v.MysteryCode) > 0 {
				entryEffectType = 2
			}
		}
	}
	if cpEntryEffect {
		entryEffectType = 1
	}
	result.EntryEffectType = entryEffectType
	model.Log.Infof("GetRoomInfo cost 8:%v", time.Now().Sub(start).Seconds())

	resp.ResponseOk(c, result)
	return myContext, nil
}

// @Tags 群组
// @Summary 查询群管理角色(经理、管理)
// @Accept application/x-www-form-urlencoded
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param groupId path string true "群ID"
// @Success 200 {object} group_cv.MemberDetail
// @Router /v1/imGroup/admin/{groupId} [get]
func GetGroupAdmin(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)

	groupId := c.Param("groupId")
	if len(groupId) <= 0 {
		return myContext, myerr.NewSysError("groupId 为必填项")
	}

	myUserId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}

	model := domain.CreateModelContext(myContext)

	groupId, err = group_m.ToImGroupId(model, groupId)
	if err != nil {
		return myContext, err
	}

	roles, orderList, err := group_m.GetRolesInGroup(model, groupId)
	if err != nil {
		return myContext, err
	}

	userIds := make([]uint64, 0)
	for _, i := range orderList {
		if roles[i] == group_e.GROUP_MANAGER || roles[i] == group_e.GROUP_ADMIN {
			userIds = append(userIds, i)
		}
	}

	result := make([]group_cv.MemberDetail, 0)
	if len(userIds) > 0 {
		users, err := user_cv.BatchGetUserExtend(model, userIds, myUserId)
		if err != nil {
			return myContext, err
		}

		for _, i := range userIds {
			result = append(result, group_cv.MemberDetail{
				CvUserExtend: users[i],
				Role:         roles[i],
			})
		}
	}

	resp.ResponseOk(c, result)
	return myContext, nil
}

// @Tags 群组
// @Summary 修改群消息设置(默认,静音,免打扰)(obsolete)
// @Accept application/x-www-form-urlencoded
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param groupId formData string true "群ID"
// @Param status formData string true "消息状态 0:默认 1:静音 2:免打扰"
// @Success 200
// @Router /v1/imGroup/user/msg/status [put]
func GroupUserMsg(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)
	userId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}

	status, err := strconv.ParseUint(c.PostForm("status"), 10, 64)
	if err != nil {
		return myContext, err
	}
	groupId := c.PostForm("groupId")
	if err := group_s.NewGroupService(myContext).GroupUserMsgStatus(userId, groupId, mysql.Type(status)); err != nil {
		return myContext, err
	}
	resp.ResponseOk(c, nil)
	return myContext, nil
}

// @Tags 群组
// @Summary 举报
// @Accept application/x-www-form-urlencoded
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param groupId formData string true "群ID"
// @Param reasonType formData int false "投诉原因 1:语言骚扰(verbal harassment)2:Nudity 3:不良消息 4:不雅图片" Enums(1, 2, 3, 4)
// @Param imageUrl formData string false "证据图片url"
// @Param reason formData string false "投诉内容"
// @Success 200
// @Router /v1/imGroup/report [post]
func GroupReport(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)
	userId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}

	groupId := c.PostForm("groupId")
	if len(groupId) <= 0 {
		return myContext, bizerr.InvalidParameter
	}

	model := domain.CreateModelContext(myContext)
	groupId, err = group_m.ToImGroupId(model, groupId)
	if err != nil {
		return myContext, err
	}

	reasonTypeStr := c.PostForm("reasonType")
	var reasonType uint64 = 0
	if reasonTypeStr != "" {
		reasonType, err = strconv.ParseUint(reasonTypeStr, 10, 64)
		if err != nil {
			return myContext, myerr.WrapErr(err)
		}
	}
	imageUrl := c.PostForm("imageUrl")
	reason := c.PostForm("reason")
	if err = group_s.NewGroupService(myContext).ReportGroup(userId, groupId, mgr_e.ReportReasonType(reasonType), imageUrl, reason); err != nil {
		return myContext, err
	}
	resp.ResponseOk(c, nil)
	return myContext, nil
}

type GroupBanner struct {
	//h5链接
	H5Url string `json:"h5Url"`
	//图片地址
	BannerUrl string `json:"bannerUrl"`
	//群主Id
	GroupId string `json:"groupId"`
	//统跳地址 http开头:web页面,query参数direction(0:竖屏 1:横屏); hilo开头:原生页面
	ActionUrl string `json:"actionUrl"`
}

// @Tags 群组
// @Summary banner列表
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Success 200 {object} GroupBanner
// @Router /v1/imGroup/banner/list [get]
func GroupBannerList(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)
	var groupBanners []GroupBanner
	userId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}

	mgrGroupBanners := []mgr_m.GroupBanner{}
	if err := mysql.Db.Model(&mgr_m.GroupBanner{}).Where(&mgr_m.GroupBanner{
		Status: mysql.YES,
	}).Order("n desc").Find(&mgrGroupBanners).Error; err != nil {
		return myContext, err
	}
	_deviceType, _ := c.Get(mycontext.DEVICETYPE)
	_appVersion, _ := c.Get(mycontext.APP_VERSION)
	model := domain.CreateModelNil()
	deviceType, appVersion := cast.ToString(_deviceType), cast.ToString(_appVersion)
	vc := version_m.GetVersionControlByPlatform(model, deviceType)
	allow, err := utils.CompareVersion(appVersion, fmt.Sprintf("<= %s", vc.AuditVersion))
	model.Log.Infof("CompareVersion appVersion:%v,auditVersion:%v,allow:%v,err:%v", appVersion, vc.AuditVersion, allow, err)

	// 获取国家信息
	country, area, err := user_m.GetUserCountryArea(model, userId)
	if err != nil {
		model.Log.Errorf("GroupBannerList 获取国家资源错误 userId:%d, err:%v", userId, err)
		return myContext, err
	}

	groupCodes := []string{}
	for i := 0; i < len(mgrGroupBanners); i++ {
		groupCodes = append(groupCodes, mgrGroupBanners[i].GroupCode)
	}

	groupMap := map[string]string{}

	if len(groupCodes) > 0 {
		var groupInfos []group_m.GroupInfo
		if err := mysql.Db.Model(&group_m.GroupInfo{}).Where("code in (?) ", groupCodes).Find(&groupInfos).Error; err != nil {
			return myContext, err
		}
		for i := 0; i < len(groupInfos); i++ {
			groupMap[groupInfos[i].Code] = groupInfos[i].TxGroupId
		}
	}

	nowUnix := time.Now().Unix()
	for _, v := range mgrGroupBanners {
		if !allow && v.Url == "https://h5.whoisamy.shop/action/hiloHtml/22_05_30_recharge/topup.html" {
			model.Log.Infof("CompareVersion appVersion:%v,auditVersion:%v,allow:%v,err:%v,skip top recharge", appVersion, vc.AuditVersion, allow, err)
			continue
		}
		// 版本控制
		if !allow && (v.ID == 2301 || v.ID == 4711 || v.ID == 2321 || v.ID == 5341 || v.ID == 5331 || v.ID == 5631 || v.ID == 5471 ||
			v.ID == 5461 || v.ID == 5771 || v.ID == 5811 || v.ID == 5881 || v.ID == 8781 || v.ID == 10351) {
			model.Log.Infof("CompareVersion appVersion:%v,auditVersion:%v,allow:%v,err:%v,skip top recharge", appVersion, vc.AuditVersion, allow, err)
			continue
		}
		// 是否在上架时间内
		if (v.StartAt > 0 && v.EndAt > 0) && (nowUnix < v.StartAt || nowUnix > v.EndAt) {
			continue
		}
		// 是否在上架区域内
		if (v.Area == 1 || v.Area == 2) && area != v.Area { // 不在区域内
			continue
		}
		if v.Area == 3 && !utils.IsInStringList(country, v.CountryList) {
			continue
		}
		actionUrl := v.Url
		if uri, err := url.Parse(actionUrl); err == nil {
			query := uri.RawQuery
			queryArr := strings.Split(query, "&")
			v := url.Values{}
			if len(queryArr) > 0 {
				for _, q := range queryArr {
					if arr := strings.Split(q, "="); len(arr) == 2 {
						v.Add(arr[0], arr[1])
					}
				}
			}
			//v.Add("direction", "1") // 横屏的需要这样
			uri.RawQuery = v.Encode()
			actionUrl = uri.String()
		}
		groupBanners = append(groupBanners, GroupBanner{
			H5Url:     v.Url,
			BannerUrl: v.Image,
			GroupId:   groupMap[v.GroupCode],
			ActionUrl: actionUrl,
		})
	}
	resp.ResponseOk(c, groupBanners)
	return myContext, nil
}

type RoomBanner struct {
	H5Url     string `json:"h5Url"`     // h5链接
	BannerUrl string `json:"bannerUrl"` // 图片地址
	ActionUrl string `json:"actionUrl"` // 统跳地址 http开头:web页面,query参数direction(0:竖屏 1:横屏); hilo开头:原生页面
}

// @Tags 群组
// @Summary banner列表
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Success 200 {object} RoomBanner
// @Router /v1/imGroup/roomBanners [get]
func RoomBannerList(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)
	model := domain.CreateModelContext(myContext)

	userId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}

	// 版本控制
	_deviceType, _ := c.Get(mycontext.DEVICETYPE)
	_appVersion, _ := c.Get(mycontext.APP_VERSION)
	deviceType, appVersion := cast.ToString(_deviceType), cast.ToString(_appVersion)
	vc := version_m.GetVersionControlByPlatform(model, deviceType)
	allow, err := utils.CompareVersion(appVersion, fmt.Sprintf("<= %s", vc.AuditVersion))
	model.Log.Infof("CompareVersion appVersion:%v,auditVersion:%v,allow:%v,err:%v", appVersion, vc.AuditVersion, allow, err)

	result := make([]RoomBanner, 0)

	// 获取国家信息
	country, area, err := user_m.GetUserCountryArea(model, userId)
	if err != nil {
		model.Log.Errorf("GroupBannerList 获取国家资源错误 userId:%d, err:%v", userId, err)
		return myContext, err
	}

	rb := mgr_m.RoomBanner{Status: mysql.YES}
	rows, err := rb.GetRoomBanners(model.Db, allow)
	if err != nil {
		return myContext, err
	}

	for _, i := range rows {
		// 是否在banner设定区域内
		if (i.Area == 1 || i.Area == 2) && area != i.Area { // 不在区域内
			continue
		}
		if i.Area == 3 && !utils.IsInStringList(country, i.CountryList) {
			continue
		}

		result = append(result, RoomBanner{
			H5Url:     i.Url,
			BannerUrl: i.Image,
			ActionUrl: i.Url,
		})
	}
	resp.ResponseOk(c, result)
	return myContext, nil
}

// @Tags 群组
// @Summary 通知客户端banner列表更新
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Success 200
// @Router /v1/imGroup/roomBanners [put]
func NotifyRoomBannerListChange(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)

	userId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}

	if !user_m.IsSuperUser(userId) {
		return myContext, bizerr.NoPrivileges
	}

	if err = rpc.SendConfigChange(userId, rpc.RoomBannerChange); err != nil {
		return myContext, err
	}
	resp.ResponseOk(c, nil)
	return myContext, nil
}

// @Tags 群组
// @Summary 获取房间内的在线用户
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param groupId query string true "群ID"
// @Success 200 {object} OnlineUserResult
// @Router /v1/imGroup/online/users [get]
func GroupInUsers(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)
	groupId := c.Query("groupId")
	if len(groupId) <= 0 {
		return myContext, bizerr.InvalidParameter
	}

	model := domain.CreateModelContext(myContext)
	groupId, err := group_m.ToImGroupId(model, groupId)
	if err != nil {
		return myContext, err
	}

	myContext.Log.WithField("groupId", groupId)
	userIds, err := group_m.RoomLivingExistsUserId(groupId)
	if err != nil {
		return myContext, err
	}
	myContext.Log.Infof("GroupInUsers userIds:%v", userIds)
	cacheDate, err := redisCli.GetRedis().Get(context.Background(), redis_key.GetGroupOnlineUser(groupId)).Result()
	if err != nil {
		//myContext.Log.Errorf("GroupInUsers GetRedis Get err:%v, groupId:%v", err, groupId)
		//查找数据库
		return GroupInUsersDb(c, myContext, userIds, groupId)
	} else if cacheDate == "" {
		//查找数据库
		return GroupInUsersDb(c, myContext, userIds, groupId)
	} else {
		onlineUserResult := OnlineUserResult{}
		if err := json.Unmarshal([]byte(cacheDate), &onlineUserResult); err != nil {
			myContext.Log.Errorf("GroupInUsers Unmarshal err:%v", err)
			//查找数据库
			return GroupInUsersDb(c, myContext, userIds, groupId)
		}
		//对比数量
		if len(userIds) != len(onlineUserResult.RedisUserIds) {
			myContext.Log.Infof("GroupInUsers cache groupId:%v size:%v RedisUserIds size:%v", groupId, len(userIds), len(onlineUserResult.RedisUserIds))
			//查找数据库
			return GroupInUsersDb(c, myContext, userIds, groupId)
		} else {
			//对比顺序
			for i, _ := range userIds {
				//出现不一致
				if userIds[i] != onlineUserResult.RedisUserIds[i] {
					myContext.Log.Infof("GroupInUsers cache groupId:%v RedisUserIds userId no same", groupId)
					return GroupInUsersDb(c, myContext, userIds, groupId)
				}
			}
			myContext.Log.Infof("GroupInUsers cache groupId:%v hit", groupId)
			resp.ResponseOk(c, onlineUserResult)
			return myContext, nil
		}

	}

}

type OnlineUserResult struct {
	Total       int                   `json:"total"`
	Users       []*user_cv.CvUserBase `json:"users"`
	ServiceTime int64                 `json:"serviceTime"`
	//利用这个来判断是否要更新缓存
	RedisUserIds []uint64 `json:"redisUserIds"`
}

//显示的最大数量是15个。
func GroupInUsersDb(c *gin.Context, myContext *mycontext.MyContext, userIds []uint64, groupId string) (*mycontext.MyContext, error) {
	userId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}
	if len(userIds) == 0 {
		if err != nil {
			return myContext, err
		}
		resp.ResponseOk(c, OnlineUserResult{
			Total: len(userIds),
			Users: nil,
		})
		return myContext, nil
	}

	//贵族
	nobleLevelMap, err := noble_m.BatchGetNobleLevel(mysql.Db, userIds)
	if err != nil {
		return myContext, err
	}

	vipMap, err := user_m.BatchGetVips(userIds)
	if err != nil {
		return myContext, err
	}
	//
	var maxNum int = 15
	vipList := make([]uint64, 0, maxNum)
	userList := make([]uint64, 0, maxNum)
	//认为需求中,不会再增加贵族6,贵族7了
	nobleLevel5 := make([]uint64, 0, maxNum)
	nobleLevel4 := make([]uint64, 0, maxNum)
	nobleLevel3 := make([]uint64, 0, maxNum)
	nobleLevel2 := make([]uint64, 0, maxNum)
	nobleLevel1 := make([]uint64, 0, maxNum)
	//
	for i, userId := range userIds {
		if len(vipList)+len(userList) >= maxNum {
			break
		}
		if level, flag := nobleLevelMap[userId]; flag {
			if level == 5 {
				nobleLevel5 = append(nobleLevel5, userId)
			} else if level == 4 {
				nobleLevel4 = append(nobleLevel4, userId)
			} else if level == 3 {
				nobleLevel3 = append(nobleLevel3, userId)
			} else if level == 2 {
				nobleLevel2 = append(nobleLevel2, userId)
			} else if level == 1 {
				nobleLevel1 = append(nobleLevel1, userId)
			} else if level == 0 {
				if vipMap[userIds[i]] != nil {
					vipList = append(vipList, userIds[i])
				} else {
					userList = append(userList, userIds[i])
				}
			}
		}
	}
	myContext.Log.Infof("GroupInUsers vip and noble ready")

	//进入房间时间排序
	roomUsers := []uint64{}
	if err := mysql.Db.Model(&group_m.GroupUser{}).Select("user_id").Where(&group_m.GroupUser{
		GroupId: groupId,
	}).Where("user_id IN (?)", userList).Order("in_room_time DESC").Find(&roomUsers).Error; err != nil {
		return myContext, err
	}
	//转换成map
	roomUserMap := map[uint64]struct{}{}
	for i, _ := range roomUsers {
		roomUserMap[roomUsers[i]] = struct{}{}
	}

	//
	resultList := make([]uint64, 0, maxNum)
	resultList = append(resultList, nobleLevel5...)
	resultList = append(resultList, nobleLevel4...)
	resultList = append(resultList, nobleLevel3...)
	resultList = append(resultList, nobleLevel2...)
	resultList = append(resultList, nobleLevel1...)
	resultList = append(resultList, vipList...)
	resultList = append(resultList, roomUsers...)
	//预防groupRoom中没有数据的,毕竟进入房间的时候,异常没有处理
	for i, _ := range userList {
		if _, flag := roomUserMap[userList[i]]; !flag {
			resultList = append(resultList, userList[i])
		}
	}
	myContext.Log.Infof("GroupInUsers resultList ready")

	userBaseMap, err := user_cv.GetUserBaseMap(resultList, userId)
	if err != nil {
		return myContext, err
	}

	users := make([]*user_cv.CvUserBase, 0, maxNum)
	for i, _ := range resultList {
		if u, flag := userBaseMap[resultList[i]]; flag {
			users = append(users, u)
		}
	}
	onlineUserResult := OnlineUserResult{
		Total:        len(userIds),
		Users:        users,
		ServiceTime:  time.Now().Unix(),
		RedisUserIds: userIds,
	}
	resp.ResponseOk(c, onlineUserResult)

	if bytes, err := json.Marshal(onlineUserResult); err != nil {
		myContext.Log.Errorf("GroupInUsers Marshal err:%v", err)
	} else {
		if _, err := redisCli.GetRedis().Set(context.Background(), redis_key.GetGroupOnlineUser(groupId), string(bytes), 5*time.Minute).Result(); err != nil {
			myContext.Log.Infof("GroupInUsers cache Set groupId:%v, err:%v", groupId, err)
		} else {
			myContext.Log.Infof("GroupInUsers cache success groupId:%v", groupId)
		}
	}
	return myContext, nil
}

// @Tags 群组
// @Summary 获取房间内的在线用户
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param groupId query string true "群ID"
// @Success 200 {object} group_m.RoomOnlineUser
// @Router /v1/imGroup/online/users/new [get]
func GroupInUserNew(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)
	groupId := c.Query("groupId")
	if len(groupId) <= 0 {
		return myContext, bizerr.InvalidParameter
	}

	model := domain.CreateModelContext(myContext)
	groupId, err := group_m.ToImGroupId(model, groupId)
	if err != nil {
		return myContext, err
	}

	roomOnlineUser, err := group_m.GetRoomOnlineUser(myContext, groupId)
	//roomOnlineUser, err := cv.GetGroupInUser(myContext, groupId)
	if err != nil {
		return myContext, err
	}
	resp.ResponseOk(c, roomOnlineUser)
	return myContext, nil
}

// @Tags 群组
// @Summary 获取勋章
// @Accept application/x-www-form-urlencoded
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param groupId query string true "群ID"
// @Success 200 {object} ResultGroupTheme
// @Router /v1/imGroup/medal/all [get]
func GroupMedalAll(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)
	groupId := c.Query("groupId")
	if len(groupId) <= 0 {
		return myContext, bizerr.InvalidParameter
	}

	model := domain.CreateModelContext(myContext)
	groupId, err := group_m.ToImGroupId(model, groupId)
	if err != nil {
		return myContext, err
	}

	resMedals := []res_m.ResMedal{}
	if err := mysql.Db.Model(&res_m.ResMedal{}).Joins("inner join group_medal m on m.res_medal_id = res_medal.id").
		Where("m.im_group_id = ?", groupId).
		Where("(expire_at is null or expire_at > ?)", time.Now().Format(utils.DATETIME_FORMAT)).
		Order("res_medal.sort").Find(&resMedals).Error; err != nil {
		return myContext, err
	}

	returnGroupMedals := make([]medal_cv.ReturnGroupMedal, 0, len(resMedals)+1)

	// 补上房间流水勋章
	var pe *medal_cv.PicElement
	_, pe, err = medal_cv.GetGroupConsumeMedal(model, groupId)
	if err != nil {
		model.Log.Infof("GetGroupConsumeMedal: %s", err.Error())
	} else if pe != nil {
		returnGroupMedals = append(returnGroupMedals, medal_cv.ReturnGroupMedal{PicUrl: pe.PicUrl})
	}

	for _, r := range resMedals {
		returnGroupMedals = append(returnGroupMedals, medal_cv.ReturnGroupMedal{
			PicUrl:  r.PicUrl,
			SvgaUrl: r.SvgaUrl,
		})
	}

	resp.ResponseOk(c, returnGroupMedals)
	return myContext, nil
} // @Tags 群组

// @Tags 群组
// @Summary 查询房间勋章
// @Accept application/x-www-form-urlencoded
// @Param token header string true "token"
// @Param nonce header string true "随机数字"
// @Param groupId query string false "群ID"
// @Success 200 {object} group_cv.RoomMedalInfo
// @Router /v1/imGroup/medal/room [get]
func GetRoomMedal(c *gin.Context) (*mycontext.MyContext, error) {
	myContext := mycontext.CreateMyContext(c.Keys)

	userId, err := req.GetUserId(c)
	if err != nil {
		return myContext, err
	}

	groupId := c.Query("groupId")
	result := make([]group_cv.RoomMedalInfo, 0)

	model := domain.CreateModelContext(myContext)

	var consumeTotal uint64
	if len(groupId) > 0 {
		groupId, err = group_m.ToImGroupId(model, groupId)
		if err != nil {
			return myContext, bizerr.GroupNotFound
		}
	} else {
		groups, err := group_m.FindGroupByOwner(model, userId)
		if err != nil {
			return myContext, err
		}
		if len(groups) > 0 {
			groupId = groups[0].ImGroupId
		}
	}

	if len(groupId) > 0 {
		consumeTotal, err = gift_cv.GetGroupConsumeTotal(model, groupId)
		if err != nil {
			return myContext, err
		}
	}
	model.Log.Infof("Room %s totoal consume = %d", groupId, consumeTotal)

	rec, err := res_m.GetRoomMedalConfig(model.Db)
	if err != nil {
		return myContext, err
	}

	ui, err := user_m.GetUser(model, userId)
	if err != nil {
		return myContext, err
	}
	lang := utils.DEFAULT_LANG
	if len(ui.Language) > 0 {
		lang = ui.Language
	}
	template, err := res_m.GetResMultiTextBy(model.Db, msg_e.MSG_ID_ROOM_MEADAL, lang)
	if err != nil {
		template = nil
	}

	for _, i := range rec {
		previewUrl := i.InactiveUrl
		if consumeTotal >= i.Threshold {
			previewUrl = i.ActiveUrl
		}

		desc := i.Desc
		if template != nil {
			num := strconv.Itoa(int(i.Threshold)/1000000) + "M"
			desc = fmt.Sprintf(template.Content, num)
		}

		result = append(result, group_cv.RoomMedalInfo{
			Level:      i.Level,
			PreviewUrl: previewUrl,
			PicUrl:     i.ActiveUrl,
			Desc:       desc,
		})
	}

	resp.ResponseOk(c, result)
	return myContext, nil
}