package models

import (
	"errors"
	"fmt"
	orm "go-admin/common/global"
	"go-admin/logger"
	"gorm.io/gorm"
	"time"
)

const (
	VmEventBuyGoods              = "buy_goods"               // 购买商品积分抵扣
	VmEventBuyGoodsReject        = "buy_goods_reject"        // 购买商品积分抵扣取消
	VmEventOpenMember            = "open_member"             // 开通会员奖励
	VmEventInvite1Member         = "invite_1_member"         // 邀请会员奖励
	VmEventInvite2Member         = "invite_2_member"         // 邀请会员奖励
	VmEventUserShareCard         = "user_share_card"         // 用户共享卡收益
	VmEventAttendance            = "attendance"              // 连续签到获取积分
	VmEventErpOrderSale          = "erp_order_sale"          // 零售销售获得积分
	VmEventErpOrderReject        = "erp_order_reject"        // 零售退货扣除积分
	VmEventExpired               = "vm_expired"              // 积分过期扣减
	VmEventCancelMember          = "cancel_member"           // 取消租卡会员
	VmEventCancelPrivilegeMember = "cancel_privilege_member" // 取消尊享会员
)

// 用户积分
// gen:qs
//
//go:generate goqueryset -in user_vm.go
type UserVm struct {
	Model

	Uid uint32 `json:"uid" gorm:"column:uid;unique_index"`
	Vm  uint32 `json:"vm"`

	//UserVm uint32 `json:"user_vm"`
}

// 用户积分-变动记录
// gen:qs
type UserVmRecord struct {
	Model

	Uid        uint32    `json:"uid" gorm:"column:uid;unique_index"` // 用户ID
	BeforeVm   uint32    `json:"before_vm"`                          // 变动前
	AfterVm    uint32    `json:"after_vm"`                           // 变动后
	Alter      int       `json:"alter"`                              // 数值
	Event      string    `json:"event" gorm:"type:varchar(100)"`     // 事件
	Describe   string    `json:"describe" gorm:"type:text"`          // 描述
	ErpOrderId uint32    `json:"erp_order_id" gorm:"index"`          // 零售订单id
	BillSn     string    `json:"bill_sn"`                            // 零售订单编号
	ExpiryDate time.Time `json:"expiry_date"`                        // 积分过期时间
	UsedVm     int       `json:"used_vm"`                            // 已使用积分
	ExpiryVm   int       `json:"expiry_vm"  gorm:"-"`                // 即将过期的积分
	User       *UserInfo `json:"user,omitempty" gorm:"-"`            // 用户信息
}

func UserVmUpdate(gdb *gorm.DB, billSn string, uid uint32, amount int, event, describe string) error {
	var userVm UserVm
	err := orm.Eloquent.Table("user_vm").
		Where("uid=?", uid).Find(&userVm).Error
	if err != nil {
		logger.Error("share card retrieve cards err:", logger.Field("err", err))
		return err
	}

	// 变动前的积分
	nBeforeVm := userVm.Vm

	flag := false
	begin := gdb
	if gdb == nil {
		flag = true
		begin = orm.Eloquent.Begin()
	}

	if userVm.Uid == 0 { // 没有积分记录
		if amount < 0 {
			begin.Rollback()
			logger.Error("amount lt 0")
			return errors.New("amount lt 0")
		}
		userVm = UserVm{
			Uid: uid,
			Vm:  uint32(amount),
		}
		err = begin.Create(&userVm).Error
		if err != nil {
			begin.Rollback()
			logger.Error("err:", logger.Field("err", err))
			return err
		}
	} else {
		// 如果用户积分不够抵扣，则扣到0为止
		if int(userVm.Vm)+amount <= 0 {
			sql := fmt.Sprintf("UPDATE user_vm SET vm = ? WHERE uid=?")
			err = begin.Exec(sql, 0, uid).Error
		} else {
			sql := fmt.Sprintf("UPDATE user_vm SET vm = vm+? WHERE uid=?")
			err = begin.Exec(sql, amount, uid).Error
		}
		if err != nil {
			begin.Rollback()
			logger.Error("err:", logger.Field("err", err))
			return err
		}
	}

	vmRecord := &UserVmRecord{
		Uid:      uid,
		BeforeVm: uint32(nBeforeVm),
		AfterVm:  0, // 默认值为 0
		Alter:    amount,
		Event:    event,
		Describe: describe,
		BillSn:   billSn,
	}

	if newValue := int(userVm.Vm) + amount; newValue > 0 {
		vmRecord.AfterVm = uint32(newValue)
	}

	// 如果是正积分，则记录该订单对应积分的有效时间
	if amount > 0 {
		vmRecord.ExpiryDate = time.Now().AddDate(1, 0, 0)
		if event == VmEventBuyGoodsReject { // 使用积分购买商品后取消
			err = RefundPoints(gdb, uid, amount)
			if err != nil {
				begin.Rollback()
				logger.Error("err:", logger.Field("err", err))
				return err
			}
		}
	} else { // 如果是负积分，则记录积分使用情况；优先扣除最早获得的积分
		if event == VmEventBuyGoods || event == VmEventErpOrderReject { // 使用积分购买商品，或者零售退货扣除积分
			err = UsePoints(gdb, uid, -amount)
			if err != nil {
				begin.Rollback()
				logger.Error("err:", logger.Field("err", err))
				return err
			}
		}
	}

	err = begin.Create(vmRecord).Error
	if err != nil {
		begin.Rollback()
		logger.Error("err:", logger.Field("err", err))
		return err
	}

	if flag {
		err = begin.Commit().Error
		if err != nil {
			begin.Rollback()
			logger.Error("err:", logger.Field("err", err))
			return err
		}
	}

	return nil
}

// GetUserAvailablePointsRecords 查找用户的所有可用积分记录，按时间排序
func GetUserAvailablePointsRecords(uid uint32) []UserVmRecord {
	var userVmRecord []UserVmRecord
	err := orm.Eloquent.Table("user_vm_record").Where("uid = ? and event in (?)",
		uid, []string{VmEventOpenMember, VmEventInvite1Member, VmEventInvite2Member, VmEventUserShareCard,
			VmEventAttendance, VmEventErpOrderSale}).
		Order("created_at DESC").
		Find(&userVmRecord).Error
	if err != nil {
		logger.Error("get user_vm_record err:", logger.Field("err", err))
		return []UserVmRecord{}
	}

	return userVmRecord
}

func UsePoints(gdb *gorm.DB, uid uint32, points int) error {
	// 1. 查找用户的所有可用积分记录，按时间排序
	records := GetUserAvailablePointsRecords(uid)

	remaining := points

	for _, record := range records {
		available := record.Alter - record.UsedVm // 计算该订单剩余可用积分

		if available >= remaining {
			// 该订单的剩余积分足够
			record.UsedVm += remaining
			// 更新积分记录
			err := gdb.Omit("created_at").Save(record).Error
			if err != nil {
				logger.Error("update user_vm_record err:", logger.Field("err", err))
				return err
			}
			break
		} else {
			// 该订单的剩余积分不足，全部使用，继续扣减下一条记录
			record.UsedVm = record.Alter
			remaining -= available
			// 更新积分记录
			err := gdb.Omit("created_at").Save(record).Error
			if err != nil {
				logger.Error("update user_vm_record err:", logger.Field("err", err))
				return err
			}
		}
	}

	return nil
}

// GetUserUsedPointsRecords 查找用户的已使用积分记录，按使用时间倒序排序
func GetUserUsedPointsRecords(uid uint32) []UserVmRecord {
	var userVmRecord []UserVmRecord
	err := orm.Eloquent.Table("user_vm_record").Where("uid = ? and event in (?)",
		uid, []string{VmEventOpenMember, VmEventInvite1Member, VmEventInvite2Member, VmEventUserShareCard,
			VmEventAttendance, VmEventErpOrderSale}).
		Where("used_vm != 0").
		Order("created_at DESC").
		Find(&userVmRecord).Error
	if err != nil {
		logger.Error("get user_vm_record err:", logger.Field("err", err))
		return []UserVmRecord{}
	}

	return userVmRecord
}

// RefundPoints 查找用户的已使用积分记录，按使用时间倒序排序
func RefundPoints(gdb *gorm.DB, uid uint32, points int) error {
	// 1. 查找用户的已使用积分记录，按使用时间倒序排序
	usedRecords := GetUserUsedPointsRecords(uid)

	remaining := points

	for _, record := range usedRecords {
		used := record.UsedVm // 获取该订单已使用的积分

		if used >= remaining {
			// 该订单已使用的积分足够返还
			record.UsedVm -= remaining
			// 更新积分记录
			err := gdb.Omit("created_at").Save(record).Error
			if err != nil {
				logger.Error("RefundPoints update user_vm_record err:", logger.Field("err", err))
				return err
			}
			break
		} else {
			// 该订单已使用的积分不足以完全返还，全部返还后继续处理下一条记录
			record.UsedVm = 0
			remaining -= used
			// 更新积分记录
			err := gdb.Omit("created_at").Save(record).Error
			if err != nil {
				logger.Error("RefundPoints update user_vm_record err:", logger.Field("err", err))
				return err
			}
		}
	}

	return nil
}
