package group_s

import (
	"context"
	"encoding/json"
	"fmt"
	"git.hilo.cn/hilo-common/domain"
	"git.hilo.cn/hilo-common/mycontext"
	"git.hilo.cn/hilo-common/mylogrus"
	"git.hilo.cn/hilo-common/resource/config"
	"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/sdk/agora"
	"git.hilo.cn/hilo-common/sdk/tencentyun"
	"git.hilo.cn/hilo-common/sdk/trtc"
	"git.hilo.cn/hilo-common/utils"
	"gorm.io/gorm"
	"hilo-group/_const/enum/group_e"
	"hilo-group/_const/enum/msg_e"
	"hilo-group/_const/redis_key"
	"hilo-group/_const/redis_key/group_k"
	"hilo-group/common"
	"hilo-group/cv/property_cv"
	"hilo-group/domain/cache/group_c"
	"hilo-group/domain/cache/tim_c"
	"hilo-group/domain/event/group_ev"
	"hilo-group/domain/model/group_m"
	"hilo-group/domain/model/noble_m"
	"hilo-group/domain/model/res_m"
	"hilo-group/domain/model/user_m"
	"hilo-group/domain/service/group_mic_s"
	"hilo-group/domain/service/signal_s"
	"hilo-group/myerr"
	"hilo-group/myerr/bizerr"
	"strconv"
	"strings"
	"time"
)

// 创建群组
func (s *GroupService) CreateGroup(userId uint64, g *group_m.GroupInfo) error {
	return s.svc.Transactional(func() error {
		model := domain.CreateModel(s.svc.CtxAndDb)
		if err := group_m.CreateGroup(model, g); err != nil {
			return err
		}
		if err := group_m.CreateGroupRole(model, g.ImGroupId, userId, group_e.GROUP_OWNER); err != nil {
			return err
		}
		//// 新房间标记成trtc房间
		//if err := group_m.InitTRTC(model, g.ImGroupId); err != nil {
		//	model.Log.Errorf("CreateGroup g:%+v, err:%v", g, err)
		//}
		return nil
	})
}

// 退群的一系列操作
func (s *GroupService) LeaveGroup(model *domain.Model, groupId string, userId uint64, externalId string) error {
	//获取用户是否在麦上, 让用户离开麦
	micUser, err := group_m.GetMicUserByExternalId(model, externalId)
	if err != nil {
		return err
	}
	if micUser != nil {
		if err = micUser.LeaveByUser(userId, externalId); err != nil {
			return err
		}
	}

	// 退群后删除管理角色
	if err = group_m.RemoveGroupRole(model, groupId, userId); err != nil {
		mylogrus.MyLog.Warnf("Can't remove group %s user %d's role.", groupId, userId)
	}

	// 退群后删除它(可能)作为经理设置的欢迎语
	gwt := group_m.GroupWelcomeText{}
	if err = gwt.Remove(model.Db, groupId, userId); err != nil {
		mylogrus.MyLog.Warnf("Can't remove group %s user %d's welcome text.", groupId, userId)
	}

	// 退群后删除小闹钟
	groupUser := group_m.GroupUser{Model: model, GroupId: groupId, UserId: userId}
	if err = groupUser.Delete(); err != nil {
		mylogrus.MyLog.Warnf("Can't remove group %s user %d's option.", groupId, userId)
	}

	// 清理相关缓存
	group_c.ClearGroupMemberCount(groupId)
	group_c.RemoveGroupMember(groupId, externalId)

	return nil
}

// 退出永久会员的一系列操作
func (s *GroupService) LeaveGroupMember(model *domain.Model, groupId string, userId uint64, externalId string) error {
	gm := group_m.GroupMember{
		GroupId: groupId,
		UserId:  userId,
	}
	if err := gm.Remove(model.Db); err != nil {
		return err
	}

	// 退群后删除管理角色
	if err := group_m.RemoveGroupRole(model, groupId, userId); err != nil {
		mylogrus.MyLog.Warnf("Can't remove group %s user %d's role.", groupId, userId)
	}

	// 退群后删除它(可能)作为经理设置的欢迎语
	gwt := group_m.GroupWelcomeText{}
	if err := gwt.Remove(model.Db, groupId, userId); err != nil {
		mylogrus.MyLog.Warnf("Can't remove group %s user %d's welcome text.", groupId, userId)
	}

	// 退群后删除小闹钟
	groupUser := group_m.GroupUser{Model: model, GroupId: groupId, UserId: userId}
	if err := groupUser.Delete(); err != nil {
		mylogrus.MyLog.Warnf("Can't remove group %s user %d's option.", groupId, userId)
	}

	// 清理相关缓存
	group_c.ClearGroupMemberCount(groupId)
	group_c.RemoveGroupMember(groupId, externalId)

	return nil
}

//进入房间, 返回channelId, err
func (s *GroupService) GroupIn(userId uint64, externalId string, groupUuid string, password, imei, ip string, provider group_e.GroupProvider, roomId int64) (string, string, error) {
	var channelId string
	var token string
	var rideId uint64
	err := s.svc.Transactional(func() error {
		//检查群组是否存在, 没有真正的domel,直接service上怼
		model := domain.CreateModel(s.svc.CtxAndDb)
		// 群是否被封禁
		banned := group_m.GroupBanned{ImGroupId: groupUuid}
		if err := banned.Get(model); err != gorm.ErrRecordNotFound {
			return bizerr.GroupIsBanned
		}
		var groupInfo group_m.GroupInfo
		if err := model.Db.Where(&group_m.GroupInfo{
			ImGroupId: groupUuid,
		}).First(&groupInfo).Error; err != nil {
			return myerr.WrapErr(err)
		}

		if userId != groupInfo.Owner && len(groupInfo.Password) > 0 && password != groupInfo.Password {
			return bizerr.IncorrectPassword
		}

		//检查群组中是否有拉黑名单
		//var n int64
		//if err := model.Db.Model(&group_m.GroupBlacklist{}).Where(&group_m.GroupBlacklist{
		//	ImGroupId: groupUuid,
		//	UserId:    userId,
		//}).Count(&n).Error; err != nil {
		//	return nil, nil, myerr.WrapErr(err)
		//}
		//if n != 0 {
		//	return nil, nil, bizerr.InBlacklist
		//}
		svip, _ := rpc.GetUserSvip(model, userId)
		if userId != groupInfo.Owner {
			// 是否超管
			isM, err := user_m.IsSuperManager(model, userId)
			if err != nil {
				model.Log.Errorf("GroupIn err:%v", err)
				return myerr.NewSysError("not super manager")
			}
			// 不是超管 且 用户是否在群的黑名单中
			if !isM && group_m.InGroupBlackList(model, groupUuid, imei, ip, userId) {
				// 特殊处理的拉黑列表
				blackMap := map[string]uint64{"HTGS#a17058241": 3058361}
				if bUid, ok := blackMap[groupUuid]; ok && bUid == userId {
					return bizerr.InBlacklist
				}
				if svip.SvipLevel < 6 { // svip6暂时不判断GroupBlackList
					return bizerr.InBlacklist
				}
			}
		}
		//是否被踢出
		if i, err := redisCli.GetRedis().Exists(context.Background(), redis_key.GetGroupKickGroupUuidUserId(groupUuid, userId)).Result(); err != nil {
			return myerr.WrapErr(err)
		} else if i == 0 {
			user, err := user_m.GetUser(model, userId)
			if err != nil {
				return err
			}
			if provider == group_e.GroupProvider_TRTC {
				channelId = groupInfo.ChannelId
				token = trtc.CreateGroupTRTCUserSig(userId, config.GetTRTCConfig())
				model.Log.Infof("enter trtc group userId:%v, groupId:%v", userId, groupUuid)
			} else {
				channelId, token, err = agora.CreateGroupAgora(groupInfo.ChannelId, uint32(userId))
			}
			if err != nil {
				return err
			} else {
				//加入房间
				group_m.RoomLivingIn(model, groupUuid, userId, externalId, false)

				groupUser, err := group_m.GetGroupUserOrInit(model, groupUuid, userId)
				if err != nil {
					model.Log.Errorf("GroupIn GetGroupUserOrInit err:%v, groupId:%v, userId:%v", err, groupUuid, userId)
				}
				if groupUser != nil {
					if err := groupUser.SetRoomInTime().Persistent(); err != nil {
						model.Log.Errorf("GroupIn groupUser Persistent err:%v, groupId:%v, userId:%v", err, groupUuid, userId)
					}
				}

				// 发送进群TIM信令。fixme: 应该发在event处理
				isVip, err := user.CheckVip()
				if err != nil {
					return err
				}

				nobleLevel, err := noble_m.GetNobleLevel(mysql.Db, userId)
				if err != nil {
					return err
				}

				type UserParam struct {
					Nick              string     `json:"nick"`
					UserAvatar        string     `json:"userAvatar"`
					IsVip             bool       `json:"isVip"`
					RideId            uint64     `json:"rideId"`
					RideUrl           string     `json:"rideUrl"`
					RideEffectUrl     string     `json:"rideEffectUrl"`
					RidSenderAvatar   string     `json:"ridSenderAvatar"`
					RidReceiverAvatar string     `json:"ridReceiverAvatar"`
					NobleLevel        uint16     `json:"nobleLevel"`
					SvipLevel         int        `json:"svipLevel"`
					Svip              rpc.CvSvip `json:"svip"`
					CpLevel           int        `json:"cpLevel"`         // cp等级
					CpUserAvatar      string     `json:"cpUserAvatar"`    // cp用户头像
					EntryEffectType   int        `json:"entryEffectType"` // 进场特效类型 1: CP 2:神秘人 3:贵族 4:vip
				}

				up := user_m.UserProperty{}
				rides, err := up.BatchGet(mysql.Db, []uint64{userId})
				if err != nil {
					return err
				}

				//rp := res_m.ResProperty{}
				//properties, err := rp.GetAll(mysql.Db)
				properties, err := property_cv.GetExtendedProperty(mysql.Db)
				if err != nil {
					return err
				}
				var cpLevel int
				var cpUserAvatar string
				var cpEntryEffect bool
				if cp, _ := rpc.GetUserCpEntryEffect(model, userId); cp != nil {
					cpLevel = cp.CpLevel
					cpUserAvatar = cp.CpUserAvatar
					cpEntryEffect = cp.CpEntryEffect
				}
				r := UserParam{
					Nick:              user.Nick,
					UserAvatar:        user.Avatar,
					IsVip:             isVip,
					RideId:            rides[userId],
					RideUrl:           properties[rides[userId]].PicUrl,
					RideEffectUrl:     properties[rides[userId]].EffectUrl, // todo replace ui svga
					RidSenderAvatar:   properties[rides[userId]].SenderAvatar,
					RidReceiverAvatar: properties[rides[userId]].ReceiverAvatar,
					NobleLevel:        nobleLevel,
					Svip:              rpc.CopySimpleSvip(svip),
					CpLevel:           cpLevel,
					CpUserAvatar:      cpUserAvatar,
				}
				rideId = r.RideId
				// 进场特效类型
				var entryEffectType int // 进场特效类型 1: CP 2:神秘人 3:贵族 4:vip ,顺序从小到大
				if r.IsVip {
					entryEffectType = 4
				}
				if r.NobleLevel > 0 {
					entryEffectType = 3
				}
				for _, v := range r.Svip.Privileges {
					if len(v.MysteryCode) > 0 {
						entryEffectType = 2
					}
				}
				if cpEntryEffect {
					entryEffectType = 1
				}
				r.EntryEffectType = entryEffectType

				buf, err := json.Marshal(r)
				if err == nil {
					//发送腾讯云IM系统消息
					signal_s.SendSignalMsg(model,
						groupUuid, group_m.GroupSystemMsg{
							MsgId:   group_e.GroupInSignal,
							Source:  user.ExternalId,
							Content: string(buf),
						}, true)
				}

				isMember, _ := group_m.IsGroupMember(model.Db, groupUuid, userId)
				return group_ev.PublishGroupIn(model, &group_ev.GroupInEvent{
					GroupId:    groupUuid,
					UserId:     userId,
					ExternalId: user.ExternalId,
					Nick:       user.Nick,
					Avatar:     user.Avatar,
					IsMember:   isMember,
					IsVip:      isVip,
					NobleLevel: nobleLevel,
				})
			}
		} else {
			return bizerr.GroupInKick
		}
	})
	if err != nil {
		return "", "", err
	} else {
		//go dealActDataAfterEnterRoom(s.svc.MyContext, userId, rideId, roomId)
		return channelId, token, nil
	}
}

func (s *GroupService) JoinGroup(userId uint64, externalId string, groupId string) error {
	model := domain.CreateModel(s.svc.CtxAndDb)
	model.Log.Infof("Async: user %d(%s) is joining group %s", userId, externalId, groupId)

	for i := 1; i < 3; i++ {
		info, err, errCode := tencentyun.AddGroup(model, groupId, []string{externalId})
		if err == nil && info != nil {
			for i, r := range info {
				if i == externalId {
					if r == group_e.ADD_GROUP_DONE || r == group_e.ADD_GROUP_DUPLICATE {
						// 加群成功后,马上发送通知给客户端,让他们拉取历史消息
						err = rpc.SendJoinGroup(userId, externalId, groupId)
						model.Log.Infof("joinGroup: SendJoinGroup err = %v", err)
					}

					if r == group_e.ADD_GROUP_DONE {
						group_c.ClearGroupMemberCount(groupId)
						tim_c.AddGroupMember(model, groupId, externalId)
					}
				}
				return nil
			}
		} else if errCode == 10014 || errCode == 10038 {
			// 如果群成员达到上限,删除一个(10014 达到群上限;10038 达到APP上限)
			extId, err := s.RemoveZombie(model, groupId)
			if err != nil {
				return err
			}
			model.Log.Infof("JoinGroup %s limit reached, errCode = %d, %s kicked", groupId, errCode, extId)
		}

	}
	return fmt.Errorf("failed 3 times")
}

// 清除TIM群的僵尸
func (s *GroupService) RemoveZombie(model *domain.Model, groupId string) (string, error) {
	rows, err := group_m.GetMembers(model.Db, groupId)
	if err != nil {
		return "", err
	}
	userIds := make([]uint64, 0)
	for _, i := range rows {
		userIds = append(userIds, i.UserId)
	}
	model.Log.Infof("JoinGroup %s: members: %v", groupId, userIds)

	roomUids, err := group_m.RoomLivingExistsUserId(groupId)
	if err != nil {
		return "", err
	}
	model.Log.Infof("JoinGroup %s: roomUids: %v", groupId, roomUids)

	userIds = append(userIds, roomUids...)
	userIds = utils.UniqueSliceUInt64(userIds)

	m, err := user_m.GetUserMapByIds(model, userIds)
	if err != nil {
		return "", err
	}
	extIdsMap := make(map[string]uint64)
	for _, i := range m {
		extIdsMap[i.ExternalId] = i.ID
	}
	model.Log.Infof("JoinGroup %s: extIdsMap: %v", groupId, extIdsMap)

	_, gm, err := tencentyun.GetGroupMemberInfo(model, groupId)
	if err != nil {
		return "", err
	}

	zombie := ""
	for _, i := range gm {
		if _, ok := extIdsMap[i.Member_Account]; !ok {
			model.Log.Infof("JoinGroup %s: to kick %s", groupId, i.Member_Account)

			tencentyun.LeaveGroup(groupId, []string{i.Member_Account})
			zombie = i.Member_Account
			break
		}
	}
	return zombie, nil
}

//离开房间
func (s *GroupService) GroupLeave(userId uint64, externalId string, groupId string) error {
	model := domain.CreateModelContext(s.svc.MyContext)
	// check cp麦位置
	group_mic_s.NewGroupPowerService(s.svc.MyContext).CheckCpLeaveMic(groupId, userId)
	_, err := group_m.RoomLivingLeave(model, userId, externalId, groupId)
	return err
}

//踢人
func (s *GroupService) GroupKick(groupUuid string, userId uint64, userExternalId string, userNick string, avatar string, beKickUserId uint64, beKickExternalId string, beKickUserNick string, beKickUserAvatar, lang string) error {
	return s.svc.Transactional(func() error {
		model := domain.CreateModel(s.svc.CtxAndDb)
		//木有model层给我,直接server怼了

		//被踢的人不能是超级管理人
		if flag, err := user_m.IsSuperManager(model, beKickUserId); err != nil {
			return err
		} else if flag {
			return bizerr.OfficialStaffLimit
		}

		//超级管理人,无敌状态
		if flag, err := user_m.IsSuperManagerV2(model, userId, beKickUserId); err != nil {
			return err
		} else if !flag {
			//判断权限
			if err := group_m.MgrPermission(model, groupUuid, userId, beKickUserId); err != nil {
				return err
			}
			//检查是否是贵族
			if flag, err := noble_m.CheckNobleLevel(model.Db, beKickUserId, 5); err != nil {
				return err
			} else if flag {
				return myerr.WrapErr(res_m.GetErrByLanguage(model.Db, common.MSG_ID_KICK_NOBLE_5, lang, bizerr.NobleNoKickOverLevel5))
				//return bizerr.NobleNoKickLevel5
			}
		}
		//踢人10分钟
		_, err := redisCli.GetRedis().Set(context.Background(), redis_key.GetGroupKickGroupUuidUserId(groupUuid, beKickUserId), strconv.Itoa(int(beKickUserId)), time.Minute*10).Result()
		if err != nil {
			return myerr.WrapErr(err)
		}

		//删除房间中redis
		//group_m.RoomLivingLeave(model, groupUuid, beKickUserId)
		group_m.RoomLivingLeaveByKick(model, groupUuid, beKickUserId, beKickExternalId, userExternalId)
		// 发信令,让前端重新拉取,接受容错,
		/*		SendSignalMsg(groupUuid, group_m.GroupSystemMsg{
				MsgId:  group_m2.GroupKickOut,
				Source: userExternalId,
				Target: beKickExternalId,
			})*/

		//记录踢人(非事务性, 错误不进行处理)
		if err := group_m.AddGroupKickRecord(model, groupUuid, userId, beKickUserId); err != nil {
			model.Log.Errorln(err)
		}

		/*		//获取用户是否在麦上, 让用户离开麦
				micUser, err := group_m.GetMicUserByExternalId(model, beKickExternalId)
				if err != nil {
					return nil, nil, err
				}
				if micUser != nil {
					micUser.LeaveByUser(userId, userExternalId)
				}*/

		return group_ev.PublishGroupKickOut(model, &group_ev.GroupKickOutEvent{
			GroupId:            groupUuid,
			OperatorExternalId: userExternalId,
			OperatorName:       userNick,
			OperatorFaceUrl:    avatar,
			MemberExternalId:   beKickExternalId,
			MemberName:         beKickUserNick,
			MemberAvatar:       beKickUserAvatar,
		})
	})
}

// 群管理人清理公屏
func (s *GroupService) GroupClearScreenByMgr(groupId string, userId uint64) error {
	model := domain.CreateModelContext(s.svc.MyContext)
	//检查权限
	if err := group_m.CheckPermission(model, groupId, userId); err != nil {
		return err
	}
	systemMsg := group_m.GroupSystemMsg{MsgId: group_e.GroupClearScreen, Source: "", Content: ""}
	signal_s.SendSignalMsg(model, groupId, systemMsg, false)
	return nil
}

func dealActDataAfterEnterRoom(myContext *mycontext.MyContext, userId, rideId uint64, roomId int64) {
	defer utils.CheckGoPanic()
	if rideId == 1261 {
		// 处理活动数据
		go rpc.AddActPoint(domain.CreateModelContext(myContext), userId, 5, roomId)
	}
}

// 创建房间,慎用
func (s *GroupService) CreateGroupMulByUid(userId uint64, num int, micNumType group_e.GroupMicNumType) error {
	model := domain.CreateModel(s.svc.CtxAndDb)

	if lock := redisCli.Lock(group_k.GetGroupLockKey(userId), time.Second*5); !lock {
		return bizerr.ReqTooFrequent
	}

	user, err := user_m.GetUser(model, userId)
	if err != nil {
		model.Log.Errorf("CreateGroupMulByUid err:%v", err)
		return err
	}

	// 强行修正为英语,以保证建群成功
	user.Language = utils.DEFAULT_LANG

	// 群组的头像默认为用户头像,群组的名称默认为用户昵称,群简介默认为“s%的群组”,群公告默认为“欢迎来到Hilo”
	name := ""
	text := res_m.ResMultiText{MsgId: msg_e.MSG_ID_GROUP_NAME, Language: user.Language}
	if text.Get(model.Db) == nil {
		name = strings.ReplaceAll(text.Content, "{nick}", user.Nick)
	}

	introduction := ""
	text2 := res_m.ResMultiText{MsgId: msg_e.MSG_ID_GROUP_INTRODUCTION, Language: user.Language}
	if text2.Get(model.Db) == nil {
		introduction = strings.ReplaceAll(text2.Content, "{nick}", user.Nick)
	}

	notification := ""
	text3 := res_m.ResMultiText{MsgId: msg_e.MSG_ID_GROUP_NOTIFICATION, Language: user.Language}
	if text3.Get(model.Db) == nil {
		notification = strings.ReplaceAll(text3.Content, "{nick}", user.Nick)
	}

	faceUrl := user.Avatar

	for idx := 0; idx < num; idx++ {
		code := group_m.GenerateGroupCode(group_e.GROUP_DEFAULT_CODE_LENGTH)
		i := 0
		for ; i < group_e.CREATE_GROUP_MAX_ATTEMPT; i++ {
			success, err := res_m.CheckCode(model, code)
			if err != nil || !success {
				break
			}
			r, err := group_m.FindGroupByCode(model, code)
			if err == nil && r == nil {
				break
			}
			code = group_m.GenerateGroupCode(group_e.GROUP_DEFAULT_CODE_LENGTH)
		}

		if i >= group_e.CREATE_GROUP_MAX_ATTEMPT {
			return myerr.NewSysError("Failed in generating groupId")
		}

		groupId := group_e.OverseaGroupNamePrefix + code
		groupId, err = tencentyun.CreateGroup(name, groupId)
		if err != nil {
			model.Log.Errorf("CreateGroupMulByUid err:%v", err)
			return err
		}

		channelId := utils.GetUUID()
		_, _, err = agora.CreateGroupAgora(channelId, uint32(userId))

		if err != nil {
			// 回滚,删除刚刚建立的TX群组
			tencentyun.DestroyGroup(groupId, false)
			model.Log.Errorf("CreateGroupMulByUid err:%v", err)
			return err
		}

		roomType := group_e.OverseaRoom
		g := group_m.GroupInfo{
			ImGroupId:      groupId,
			TxGroupId:      groupId,
			Type:           uint16(roomType),
			Code:           code,
			OriginCode:     code,
			Owner:          userId,
			Name:           name,
			Introduction:   introduction,
			Notification:   notification,
			FaceUrl:        faceUrl,
			Country:        user.Country,
			ChannelId:      channelId,
			MicOn:          true,
			LoadHistory:    false,
			MicNumType:     micNumType,
			TouristMic:     1,
			TouristSendMsg: 1,
			TouristSendPic: 1,
			IsGameRoom:     1,
		}
		if err := s.CreateGroup(userId, &g); err != nil {
			// 回滚,删除刚刚建立的TX群组
			tencentyun.DestroyGroup(groupId, false)
			model.Log.Errorf("CreateGroupMulByUid err:%v", err)
			return err
		}

		gm := group_m.GroupMember{
			GroupId: groupId,
			UserId:  userId,
		}
		if err = gm.Create(model.Db); err != nil {
			model.Log.Warnf("Failed to set GroupMember +%v", gm)
		}
		time.Sleep(time.Millisecond * 30)
	}
	return nil
}