package groupPower_m

import (
	"git.hilo.cn/hilo-common/domain"
	"git.hilo.cn/hilo-common/resource/mysql"
	"git.hilo.cn/hilo-common/utils"
	"github.com/jinzhu/now"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"hilo-group/_const/enum/groupPower_e"
	"hilo-group/_const/redis_key/mic_k"
	"hilo-group/domain/event/group_power_ev"
	"time"
)

type GroupPowerDayExp struct {
	Date         string
	GroupPowerId mysql.ID
	Exp          mysql.Num
	CreatedTime  time.Time `gorm:"->"`
	UpdatedTime  time.Time `gorm:"->"`
}

type GroupPowerGrade struct {
	GroupPowerId mysql.ID
	Exp          mysql.Num
	Grade        groupPower_e.GroupPowerGrade
	ExpireAt     time.Time
	CreatedTime  time.Time `gorm:"->"`
	UpdatedTime  time.Time `gorm:"->"`
}

type GroupPowerExpDetail struct {
	mysql.Entity
	GroupPowerId mysql.ID
	UserId       mysql.ID
	Exp          mysql.Num
	AddReduce    mysql.AddReduce
	BefNum       mysql.Num
	AftNum       mysql.Num
	Remark       string
}

// 增加家族经验
// 达到经验值之后升级
// 单进程同步执行,不考虑并发
func IncrGroupPowerExp(txModel *domain.Model, groupPowerId mysql.ID, exp mysql.Num, userId mysql.ID, remark string) error {
	var err error
	defer func() {
		if err != nil {
			txModel.Log.Errorf("IncrGroupPowerExp fail,id:%v,exp:%v,err:%v", groupPowerId, exp, err)
		}
	}()
	// 增加家族经验-天
	date := time.Now().Format("2006-01-02")
	gpe := &GroupPowerDayExp{
		Date:         date,
		GroupPowerId: groupPowerId,
		Exp:          exp,
	}
	if err = txModel.DB().Model(GroupPowerDayExp{}).Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "date"}, {Name: "group_power_id"}},
		DoUpdates: clause.Assignments(map[string]interface{}{
			"exp": gorm.Expr("exp + ?", gpe.Exp)})}).Create(gpe).Error; err != nil {
		return err
	}
	// 增加家族经验-总
	gpg := new(GroupPowerGrade)
	// insert
	if err := txModel.DB().Model(GroupPowerGrade{}).Where("group_power_id = ?", groupPowerId).First(gpg).Error; err != nil {
		if err != gorm.ErrRecordNotFound {
			return err
		}
		// gorm.ErrRecordNotFound
		gpg = &GroupPowerGrade{
			GroupPowerId: groupPowerId,
			Exp:          exp,
			Grade:        0,
			ExpireAt:     time.Time{},
		}
		if err = txModel.DB().Create(gpg).Error; err != nil {
			return err
		}
	} else {
		// or update
		if err := txModel.DB().Model(GroupPowerGrade{}).Where("group_power_id = ?", groupPowerId).
			UpdateColumn("exp", gorm.Expr("exp + ?", exp)).Error; err != nil {
			return err
		}
	}
	//if err = txModel.DB().Model(GroupPowerGrade{}).Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "group_power_id"}},
	//	DoUpdates: clause.Assignments(map[string]interface{}{
	//		"exp": gorm.Expr("exp + ?", gpg.Exp)})}).Create(gpg).Error; err != nil {
	//	return err
	//}
	// 当前写后读
	latestGrade := new(GroupPowerGrade)
	if err = txModel.DB().Model(GroupPowerGrade{}).Where("group_power_id = ?", groupPowerId).First(latestGrade).Error; err != nil {
		return err
	}
	// 记录明细
	detail := &GroupPowerExpDetail{
		GroupPowerId: groupPowerId,
		UserId:       userId,
		Exp:          exp,
		AddReduce:    mysql.ADD,
		BefNum:       latestGrade.Exp - exp,
		AftNum:       latestGrade.Exp,
		Remark:       remark,
	}
	if err = txModel.DB().Model(GroupPowerExpDetail{}).Create(detail).Error; err != nil {
		return err
	}
	// 达到经验值之后升级
	for grade := groupPower_e.GroupPowerGradeMax; grade >= groupPower_e.GroupPowerGrade0; grade-- {
		if latestGrade.Exp > groupPower_e.GroupPowerGradeExp[grade] {
			if latestGrade.Grade < grade { // 升级
				expireAt := now.EndOfMonth()
				expireAt = utils.AddDate(expireAt, 0, 1) // 等级有效期到下个月月底
				updateAttrs := map[string]interface{}{
					"grade":     grade,
					"expire_at": expireAt,
				}
				if err = txModel.DB().Model(GroupPowerGrade{}).Where("group_power_id = ?", latestGrade.GroupPowerId).UpdateColumns(updateAttrs).Error; err != nil {
					return err
				}
				// 升级发事件
				_ = group_power_ev.PublishGroupPowerUpgrade(txModel, &group_power_ev.GroupPowerUpgradeEvent{
					GroupPowerId: groupPowerId,
				})
			}
			break
		}
	}
	return nil
}

// 增加势力上麦经验
// 事务操作
func IncrGroupPowerExpOnMic(model *domain.Model, groupPowerId, userId mysql.ID) error {
	key := mic_k.GetMicGroupPowerOnMic(userId)
	minutes, err := model.Redis.IncrBy(model, key, 1).Result()
	if err != nil {
		model.Log.Errorf("IncrGroupPowerExpOnMic fail:%v", err)
		return err
	}
	model.Redis.Expire(model, key, time.Hour*24) // ttl 一天
	// 每日最多1800经验 = 18个10分钟 = 180分钟
	if minutes > 180 {
		return nil
	}
	if minutes%10 == 0 {
		return IncrGroupPowerExp(model, groupPowerId, 100, userId, "上麦10分钟")
	}
	return nil
}

// 清理所有家族的经验
func ClearGroupPowerExp(model *domain.Model) error {
	var groupPowerGrades []*GroupPowerGrade
	if err := model.DB().Model(GroupPowerGrade{}).Where("exp > 0").Find(&groupPowerGrades).Error; err != nil {
		return err
	}
	return model.Transaction(func(model *domain.Model) error {
		for _, grade := range groupPowerGrades {
			if err := model.DB().Model(GroupPowerGrade{}).Where("group_power_id = ?", grade.GroupPowerId).UpdateColumn("exp", 0).Error; err != nil {
				return err
			}
			// 记录明细
			detail := &GroupPowerExpDetail{
				GroupPowerId: grade.GroupPowerId,
				UserId:       0,
				Exp:          0,
				AddReduce:    mysql.SET,
				BefNum:       grade.Exp,
				AftNum:       0,
				Remark:       "每月清零",
			}
			if err := model.DB().Model(GroupPowerExpDetail{}).Create(detail).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

// 清理过期家族等级
func ClearGroupPowerGrade(model *domain.Model) error {
	var groupPowerGrades []*GroupPowerGrade
	if err := model.DB().Model(GroupPowerGrade{}).Where("expire_at < ?", time.Now()).Find(&groupPowerGrades).Error; err != nil {
		return err
	}
	return model.Transaction(func(model *domain.Model) error {
		for _, grade := range groupPowerGrades {
			updateAttrs := map[string]interface{}{
				"grade":     0,
				"expire_at": time.Time{},
			}
			if err := model.DB().Model(GroupPowerGrade{}).Where("group_power_id = ?", grade.GroupPowerId).Updates(updateAttrs).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

// 批量获取家族等级
func MGetGroupPowerGrade(model *domain.Model, groupPowerIds []mysql.ID) (map[mysql.ID]GroupPowerGrade, error) {
	var rows []GroupPowerGrade
	res := make(map[mysql.ID]GroupPowerGrade)
	if err := model.DB().Model(GroupPowerGrade{}).Where("group_power_id in ?", groupPowerIds).Find(&rows).Error; err != nil {
		model.Log.Errorf("MGetGroupPowerGrade fail:%v", err)
		return res, err
	}
	for i, v := range rows {
		res[v.GroupPowerId] = rows[i]
	}
	return res, nil
}