package cp_m

import (
	"fmt"
	"git.hilo.cn/hilo-common/domain"
	"git.hilo.cn/hilo-common/resource/mysql"
	"git.hilo.cn/hilo-common/rpc"
	"git.hilo.cn/hilo-common/txop/headwear_tx"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"hilo-user/_const/enum/cp_e"
	"hilo-user/_const/enum/gift_e"
	"hilo-user/domain/model/user_m"
	"hilo-user/myerr"
	"time"
)

// cp等级
type CpLevel struct {
	mysql.Entity
	CpId     mysql.ID
	UserId1  mysql.ID
	UserId2  mysql.ID
	Points   mysql.Num
	Level    cp_e.CpLevel
	ExpireAt time.Time
}

// cp等级积分明细
type CpLevelDetail struct {
	mysql.Entity
	CpId      mysql.ID
	UserId1   mysql.ID
	UserId2   mysql.ID
	AddReduce mysql.AddReduce
	Num       mysql.Num
	BefNum    mysql.Num
	AftNum    mysql.Num
	Remark    string
}

// 获取cp等级
func GetCpLevel(model *domain.Model, cpId mysql.ID) CpLevel {
	var level CpLevel
	if err := model.DB().Model(CpLevel{}).Where("cp_id = ?", cpId).First(&level).Error; err != nil {
		model.Log.Errorf("GetCpLevel fail:%v", err)
	}
	return level
}

// 批量获取cp等级
func MGetCpLevel(model *domain.Model, cpIds []mysql.ID) map[mysql.ID]CpLevel {
	var res = make(map[mysql.ID]CpLevel)
	var level []CpLevel
	if err := model.DB().Model(CpLevel{}).Where("cp_id in ?", cpIds).Find(&level).Error; err != nil {
		model.Log.Errorf("MGetCpLevel fail:%v", err)
	}
	for i, v := range level {
		res[v.CpId] = level[i]
	}
	return res
}

// 添加cp等级积分增减明细
func AddCpLevelDetail(model *domain.Model, detail CpLevelDetail) error {
	return model.DB().Create(&detail).Error
}

// 获取cpRelation
func GetCpRelation(model *domain.Model, userId mysql.ID) (cpRelation CpRelation, exits bool) {
	if err := model.DB().Model(CpRelation{}).Where("user_id1 = ? or user_id2 = ?", userId, userId).First(&cpRelation).Error; err != nil {
		if err != gorm.ErrRecordNotFound {
			model.Log.Errorf("GetCpRelation fail:%v", err)
			return
		}
	} else {
		exits = true
	}
	return
}

// 获取cpRelation pair
func GetCpRelationPair(model *domain.Model, userId1, userId2 mysql.ID) (cpRelation CpRelation, exits bool) {
	if err := model.DB().Model(CpRelation{}).Where("user_id1 = ? AND user_id2 = ?", userId1, userId2).First(&cpRelation).Error; err != nil {
		if err != gorm.ErrRecordNotFound {
			model.Log.Errorf("GetCpRelation fail:%v", err)
			return
		} else {
			// gorm.ErrRecordNotFound
			if err := model.DB().Model(CpRelation{}).Where("user_id1 = ? AND user_id2 = ?", userId2, userId1).First(&cpRelation).Error; err != nil {
				if err != gorm.ErrRecordNotFound {
					model.Log.Errorf("GetCpRelation fail:%v", err)
					return
				}
			} else {
				exits = true
			}
		}
	} else {
		exits = true
	}
	return
}

// 获取cpRelation pair
func MGetCpRelation(model *domain.Model, userIds []mysql.ID) (cpRelation []CpRelation) {
	if err := model.DB().Model(CpRelation{}).Where("user_id1 in ? or user_id2 in ?", userIds, userIds).Find(&cpRelation).Error; err != nil {
		model.Log.Errorf("GetCpRelation fail:%v", err)
	}
	return
}

// 获取是否申请解绑中
func GetApplyToUnbind(model *domain.Model, userId, cpUserId mysql.ID) bool {
	var total int64
	if err := model.DB().Model(CpCancel{}).Where("user_id = ? AND rec_user_id = ? AND status = 1", userId, cpUserId).Count(&total).Error; err != nil {
		model.Log.Errorf("GetApplyToUnbind fail:%v", err)
	}
	return total > 0
}

// 初始化cpLevel
func InitCpLevel(model *domain.Model, cpId, userId1, userId2 mysql.ID) error {
	return model.DB().Model(CpLevel{}).Clauses(clause.OnConflict{DoNothing: true}).Create(&CpLevel{
		CpId:     cpId,
		UserId1:  userId1,
		UserId2:  userId2,
		ExpireAt: time.Now().AddDate(0, 1, 0),
	}).Error
}

// 增加cp等级积分
// 此函数并发不安全,利用mysql事件串行执行保证顺序
// 送礼1钻石=1点数
// condition
//	 1.记录不存在,首充加points计算level增加90天有效期
//	 2.记录存在
//		 2.1 在有效期内,直接加points后判断新level,升级需要更新有效期
//		 2.2 不有效期内,算首充,重置points后判断新level,升级需要更新有效期
func AddCpLevelPoints(model *domain.Model, cpRelation CpRelation, points mysql.Num, sceneType gift_e.GiftOperateSceneType, sceneUid string) (err error) {
	start := time.Now()
	defer func() {
		model.Log.Infof("AddCpLevelPoints cost:%v,err:%v", time.Now().Sub(start), err)
	}()
	var oldLevel cp_e.CpLevel
	var cpLevel CpLevel
	var cpLevelDetails []CpLevelDetail
	if err := model.DB().Model(CpLevel{}).Where("cp_id = ?", cpRelation.Id).First(&cpLevel).Error; err != nil {
		if err != gorm.ErrRecordNotFound {
			return myerr.WrapErr(err)
		}
		// 明细
		cpLevelDetails = append(cpLevelDetails, CpLevelDetail{
			CpId:      cpRelation.Id,
			UserId1:   cpRelation.UserId1,
			UserId2:   cpRelation.UserId2,
			AddReduce: mysql.ADD,
			Num:       points,
			BefNum:    0,
			AftNum:    points,
			Remark:    fmt.Sprintf("send %d gift diamonds", points),
		})
		//  1.记录不存在,首充加points计算level增加90天有效期
		var level cp_e.CpLevel
		for l := cp_e.CpLevelMax; l >= cp_e.CpLevel0; l-- {
			if cp_e.CpLevelPoints[l] <= points {
				level = l
				break
			}
		}
		if level > 0 {
			points = points - cp_e.CpLevelPoints[level] // 减去用于已用于升级的积分
			// 明细
			cpLevelDetails = append(cpLevelDetails, CpLevelDetail{
				CpId:      cpRelation.Id,
				UserId1:   cpRelation.UserId1,
				UserId2:   cpRelation.UserId2,
				AddReduce: mysql.REDUCE,
				Num:       cp_e.CpLevelPoints[level],
				BefNum:    cp_e.CpLevelPoints[level] + points,
				AftNum:    points,
				Remark:    fmt.Sprintf("Become LEVEL%d", level),
			})
		}
		cpLevel = CpLevel{
			CpId:     cpRelation.Id,
			UserId1:  cpRelation.UserId1,
			UserId2:  cpRelation.UserId2,
			Points:   points,
			Level:    level,
			ExpireAt: time.Now().AddDate(0, 0, cp_e.EffectDays),
		}
	} else {
		// 2.记录存在
		// 2.1 在有效期内,直接加points后判断新level,升级需要更新有效期
		if cpLevel.ExpireAt.After(time.Now()) {
			cpLevel.Points += points
			// 明细
			cpLevelDetails = append(cpLevelDetails, CpLevelDetail{
				CpId:      cpRelation.Id,
				UserId1:   cpRelation.UserId1,
				UserId2:   cpRelation.UserId2,
				AddReduce: mysql.ADD,
				Num:       points,
				BefNum:    cpLevel.Points - points,
				AftNum:    cpLevel.Points,
				Remark:    fmt.Sprintf("send %d gift diamonds", points),
			})
			oldLevel = cpLevel.Level
			levelPoint := cp_e.CpLevelPoints[oldLevel] // 已经用于升级的积分
			for level := cp_e.CpLevelMax; level > oldLevel; level-- {
				if cp_e.CpLevelPoints[level] <= cpLevel.Points+levelPoint {
					cpLevel.Level = level
					break
				}
			}
			// 升级
			if oldLevel != cpLevel.Level {
				// 减去已用于升级的积分
				cpLevel.Points = cpLevel.Points - (cp_e.CpLevelPoints[cpLevel.Level] - cp_e.CpLevelPoints[oldLevel])
				cpLevel.ExpireAt = time.Now().AddDate(0, 0, cp_e.EffectDays)
				// 明细
				cpLevelDetails = append(cpLevelDetails, CpLevelDetail{
					CpId:      cpRelation.Id,
					UserId1:   cpRelation.UserId1,
					UserId2:   cpRelation.UserId2,
					AddReduce: mysql.REDUCE,
					Num:       cp_e.CpLevelPoints[cpLevel.Level] - cp_e.CpLevelPoints[oldLevel],
					BefNum:    cpLevel.Points + cp_e.CpLevelPoints[cpLevel.Level] - cp_e.CpLevelPoints[oldLevel],
					AftNum:    cpLevel.Points,
					Remark:    fmt.Sprintf("Become LEVEL%d", cpLevel.Level),
				})
			}
		} else {
			// 2.2 不有效期内,算首充,重置points后判断新level,更新有效期30天
			oldPoints := cpLevel.Points
			cpLevel.Points = points
			// 明细
			cpLevelDetails = append(cpLevelDetails, CpLevelDetail{
				CpId:      cpRelation.Id,
				UserId1:   cpRelation.UserId1,
				UserId2:   cpRelation.UserId2,
				AddReduce: mysql.ADD,
				Num:       points,
				BefNum:    oldPoints,
				AftNum:    cpLevel.Points,
				Remark:    fmt.Sprintf("send %d gift diamonds", points),
			})
			for level := cp_e.CpLevelMax; level >= cp_e.CpLevel0; level-- {
				if cp_e.CpLevelPoints[level] <= cpLevel.Points {
					cpLevel.Level = level
					break
				}
			}
			if cpLevel.Level > 0 {
				cpLevel.Points -= cp_e.CpLevelPoints[cpLevel.Level] // 减去已用于升级的积分
				// 明细
				cpLevelDetails = append(cpLevelDetails, CpLevelDetail{
					CpId:      cpRelation.Id,
					UserId1:   cpRelation.UserId1,
					UserId2:   cpRelation.UserId2,
					AddReduce: mysql.REDUCE,
					Num:       cp_e.CpLevelPoints[cpLevel.Level],
					BefNum:    cpLevel.Points + cp_e.CpLevelPoints[cpLevel.Level],
					AftNum:    cpLevel.Points,
					Remark:    fmt.Sprintf("Become SVIP%d", cpLevel.Level),
				})
			}
			cpLevel.ExpireAt = time.Now().AddDate(0, 0, cp_e.EffectDays)
		}
	}
	// 顺序增加明细
	for _, detail := range cpLevelDetails {
		if err := AddCpLevelDetail(model, detail); err != nil {
			return myerr.WrapErr(err)
		}
	}
	// 赠送cp头像头饰
	if oldLevel < 3 && cpLevel.Level >= 3 {
		if err := headwear_tx.SendHeadwear(model, cpRelation.UserId1, cp_e.CpHeadwearId, 30); err != nil {
			return err
		}
		if err := headwear_tx.SendHeadwear(model, cpRelation.UserId2, cp_e.CpHeadwearId, 30); err != nil {
			return err
		}
	}
	// 群组中送礼升级
	if oldLevel != cpLevel.Level && sceneType == gift_e.GroupSceneType {
		go func() {
			userId1, userId2 := cpRelation.UserId1, cpRelation.UserId2
			model := domain.CreateModelContext(model.MyContext)
			users, err := user_m.GetUserMapByIds(model, []mysql.ID{userId1, userId2})
			if err != nil {
				model.Log.Errorf("")
				return
			}
			if err := rpc.SendCpUpgrade(users[userId1].Nick, users[userId2].Nick, users[userId1].Avatar, users[userId2].Avatar, uint32(cpLevel.Level), sceneUid); err != nil {
				model.Log.Errorf("SendCpUpgrade fail:%v", err)
			}
		}()
	}
	return cpLevel.Persistence(model)
}

// 清理过期svip积分
// 降级保级: 积分清零,svip去到大于0的等级,有效期30天
// svip0:积分清零,有效期保持过期
func ClearExpireCpPoints(model *domain.Model) error {
	var cpLevels []*CpLevel
	// 过期 + (积分 or level) 大于0
	if err := model.DB().Table("cp_level").Joins("INNER JOIN cp_relation r ON cp_id = r.id").Where("expire_at < ? AND (points > 0 or level > 0) ", time.Now()).Find(&cpLevels).Error; err != nil {
		return myerr.WrapErr(err)
	}
	for _, cpLevel := range cpLevels {
		model.Log.Infof("ClearExpireCpPoints %v", *cpLevel)
		var doubleCheck CpLevel
		if err := model.DB().Model(CpLevel{}).Where("id = ?", cpLevel.ID).First(&doubleCheck).Error; err != nil {
			model.Log.Errorf("double check fail:%v", err)
			continue
		}
		if doubleCheck.ExpireAt.After(time.Now()) {
			continue
		}
		oldPoints := cpLevel.Points
		cpLevel.Level, cpLevel.Points = cp_e.CpLevel0, 0 // 清零
		var newLevel cp_e.CpLevel
		// 0级不刷新30天有效期
		for level := cp_e.CpLevelMax; level > cp_e.CpLevel0; level-- {
			if cp_e.CpLevelPoints[level] <= oldPoints {
				newLevel = level
				break
			}
		}
		// 降级/保级刷新30天有效期
		if newLevel > 0 {
			cpLevel.Level = newLevel
			cpLevel.ExpireAt = time.Now().AddDate(0, 0, cp_e.EffectDays)
			// 明细
			if err := AddCpLevelDetail(model, CpLevelDetail{
				CpId:      cpLevel.CpId,
				UserId1:   cpLevel.UserId1,
				UserId2:   cpLevel.UserId2,
				AddReduce: mysql.REDUCE,
				Num:       cp_e.CpLevelPoints[newLevel],
				BefNum:    oldPoints,
				AftNum:    0,
				Remark:    fmt.Sprintf("Become LEVEL%d", newLevel),
			}); err != nil {
				model.Log.Errorf("AddCpLevelDetail fail:%v", err)
			}
		}
		if err := cpLevel.Persistence(model); err != nil {
			model.Log.Errorf("cpLevel persistence fail:%v", err)
		}
		// 明细
		if err := AddCpLevelDetail(model, CpLevelDetail{
			CpId:      cpLevel.CpId,
			UserId1:   cpLevel.UserId1,
			UserId2:   cpLevel.UserId2,
			AddReduce: mysql.SET,
			Num:       oldPoints - cp_e.CpLevelPoints[newLevel],
			BefNum:    oldPoints - cp_e.CpLevelPoints[newLevel],
			AftNum:    0,
			Remark:    fmt.Sprintf("Expired clear"),
		}); err != nil {
			model.Log.Errorf("AddCpLevelDetail fail:%v", err)
		}
		// 保级续送cp头像头饰
		if newLevel >= 3 {
			if err := headwear_tx.SendHeadwear(model, cpLevel.UserId1, cp_e.CpHeadwearId, 30); err != nil {
				model.Log.Errorf("SendHeadwear fail:%v", err)
			}
			if err := headwear_tx.SendHeadwear(model, cpLevel.UserId2, cp_e.CpHeadwearId, 30); err != nil {
				model.Log.Errorf("SendHeadwear fail:%v", err)
			}
		} else {
			// 否则删除cp头像头饰
			if err := headwear_tx.DelHeadwear(model, cpLevel.UserId1, cp_e.CpHeadwearId); err != nil {
				model.Log.Errorf("DelHeadwear fail:%v", err)
			}
			if err := headwear_tx.DelHeadwear(model, cpLevel.UserId2, cp_e.CpHeadwearId); err != nil {
				model.Log.Errorf("DelHeadwear fail:%v", err)
			}
		}
	}
	return nil
}