package group_s

import (
	"context"
	"encoding/json"
	"fmt"
	"git.hilo.cn/hilo-common/domain"
	"git.hilo.cn/hilo-common/mylogrus"
	"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/utils"
	"gorm.io/gorm"
	"hilo-group/_const/enum/group_e"
	"hilo-group/_const/redis_key"
	"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/user_m"
	"hilo-group/domain/service/signal_s"
	"hilo-group/myerr"
	"hilo-group/myerr/bizerr"
	"strconv"
	"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
		}
		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) (string, string, error) {
	var channelId string
	var token string
	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) {
				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
			}
			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 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"`
				}

				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
				}
				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),
				}

				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 {
		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)

	_, 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 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.IsSuperManager(model, userId); 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 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
}