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/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
}

type GroupPowerOnMic struct {
	Date         string
	GroupPowerId mysql.ID
	UserId       mysql.ID
	Seconds      int64
	LastCalTs    int64
	CreatedTime  time.Time `gorm:"->"`
	UpdatedTime  time.Time `gorm:"->"`
}

type GroupPowerOnMicDetail struct {
	Date         string
	GroupPowerId mysql.ID
	UserId       mysql.ID
	Minute       int
	CreatedTime  time.Time `gorm:"->"`
	UpdatedTime  time.Time `gorm:"->"`
}

// 增加家族经验
// 达到经验值之后升级
// 单进程同步执行,不考虑并发
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 := &GroupPowerGrade{
		GroupPowerId: groupPowerId,
		Exp:          exp,
		Grade:        0,
		ExpireAt:     time.Time{},
	}
	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 GetGroupPowerUserOnMicDetails(model *domain.Model, groupPowerId, userId mysql.ID) ([]*GroupPowerOnMicDetail, error) {
	var res []*GroupPowerOnMicDetail
	date := time.Now().Format("2006-01-02")
	if err := model.DB().Model(GroupPowerOnMicDetail{}).Where("date = ? AND group_power_id = ? AND user_id = ?", date, groupPowerId, userId).Find(&res).Error; err != nil {
		return res, err
	}
	return res, nil
}

// 获取势力用户上麦记录
func GetGroupPowerOnMic(model *domain.Model, groupPowerId, userId mysql.ID) (*GroupPowerOnMic, error) {
	gpom := new(GroupPowerOnMic)
	date := time.Now().Format("2006-01-02")
	if err := model.DB().Model(GroupPowerOnMic{}).Where("date = ? AND group_power_id = ? AND user_id = ?", date, groupPowerId, userId).First(gpom).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return &GroupPowerOnMic{Date: date, GroupPowerId: groupPowerId, UserId: userId}, nil
		}
		return nil, err
	}
	return gpom, nil
}

const MaxMinuteTimes = 18

// 增加势力上麦经验
// 事务操作
func IncrGroupPowerExpOnMic(model *domain.Model, groupPowerId, userId mysql.ID, joinMicTimestamp int64) error {
	return model.Transaction(func(model *domain.Model) error {
		// 获取用户上麦奖励历史
		onMicDetails, err := GetGroupPowerUserOnMicDetails(model, groupPowerId, userId)
		if err != nil {
			return err
		}
		numDetails := len(onMicDetails)
		if numDetails >= MaxMinuteTimes {
			// 上麦经验贡献值最多1800,1分钟100
			return nil
		}
		onMic, err := GetGroupPowerOnMic(model, groupPowerId, userId)
		if err != nil {
			return err
		}
		nowTs := time.Now().Unix()
		curTs := joinMicTimestamp
		day0Ts := utils.GetZeroTime(time.Now()).Unix()
		if joinMicTimestamp < onMic.LastCalTs {
			curTs = onMic.LastCalTs
		}
		// 跨天
		if curTs < day0Ts {
			curTs = day0Ts
		}
		onMicSeconds := nowTs - curTs
		var moreDetails []*GroupPowerOnMicDetail
		totalMinuteTimes := int((onMic.Seconds + onMicSeconds) / 600) // 今天实际能加经验次数
		if totalMinuteTimes >= MaxMinuteTimes {
			totalMinuteTimes = MaxMinuteTimes
		}
		if totalMinuteTimes > numDetails {
			// 续上上一次的时间,从numDetails开始
			for mt := numDetails + 1; mt <= totalMinuteTimes; mt++ {
				moreDetails = append(moreDetails, &GroupPowerOnMicDetail{
					Date:         time.Now().Format("2006-01-02"),
					GroupPowerId: groupPowerId,
					UserId:       userId,
					Minute:       mt * 10, // 转换分钟
				})
			}
		}
		// 有更多麦上10分钟,可以加经验
		if len(moreDetails) > 0 {
			for _, detail := range moreDetails {
				// 添加明细,避免重复计算
				if err := model.DB().Model(GroupPowerOnMicDetail{}).Create(detail).Error; err != nil {
					return err
				}
				// 每10分钟增加100点经验
				if err := IncrGroupPowerExp(model, groupPowerId, 100, userId, "上麦10分钟"); err != nil {
					return err
				}
			}
			// 更新micExp信息
			onMic.Seconds = onMic.Seconds + onMicSeconds
			onMic.LastCalTs = nowTs
			if err := model.DB().Model(GroupPowerOnMic{}).Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "date"}, {Name: "group_power_id"}},
				DoUpdates: clause.Assignments(
					map[string]interface{}{
						"seconds":     onMic.Seconds,
						"last_cal_ts": nowTs,
					},
				)}).
				Create(onMic).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

// 增加势力上麦时长-家族之星
// 事务操作
func IncrGroupPowerStarOnMicMonth(model *domain.Model, groupPowerId, userId mysql.ID, joinMicTimestamp int64) error {
	return model.Transaction(func(model *domain.Model) error {
		// 月统计
		star, err := GetGroupPowerMonthStar(model, groupPowerId, userId, groupPower_e.GroupPowerStarTypeActive)
		curTs := joinMicTimestamp
		nowTs := time.Now().Unix()
		month0Ts := now.BeginningOfMonth().Unix()
		if err != nil && err != gorm.ErrRecordNotFound {
			return err
		}
		if star != nil && joinMicTimestamp < star.LastCalTs { // 加入的时间比上次计算时间小
			curTs = star.LastCalTs
		}
		// 跨月
		if curTs < month0Ts {
			curTs = month0Ts
		}
		score := nowTs - curTs
		err = IncrGroupPowerMonthStarScore(model, groupPowerId, userId, groupPower_e.GroupPowerStarTypeActive, mysql.Num(score), nowTs)
		return err
	})
}

// 增加势力上麦时长-家族之星
// 事务操作
func IncrGroupPowerStarOnMicDay(model *domain.Model, groupPowerId, userId mysql.ID, joinMicTimestamp int64) error {
	return model.Transaction(func(model *domain.Model) error {
		// 日统计
		star, err := GetGroupPowerDayStar(model, groupPowerId, userId, groupPower_e.GroupPowerStarTypeActive)
		curTs := joinMicTimestamp
		nowTs := time.Now().Unix()
		day0Ts := now.BeginningOfDay().Unix()
		if err != nil && err != gorm.ErrRecordNotFound {
			return err
		}
		if star != nil && joinMicTimestamp < star.LastCalTs { // 加入的时间比上次计算时间小
			curTs = star.LastCalTs
		}
		// 跨天
		if curTs < day0Ts {
			curTs = day0Ts
		}
		score := nowTs - curTs
		err = IncrGroupPowerDayStarScore(model, groupPowerId, userId, groupPower_e.GroupPowerStarTypeActive, mysql.Num(score), nowTs)
		return err
	})
}

// 清理所有家族的经验
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
}