package user_m

import (
	"git.hilo.cn/hilo-common/domain"
	"git.hilo.cn/hilo-common/resource/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"hilo-group/_const/enum/user_e"
	"hilo-group/myerr"
	"time"
)

//用户Vip
type UserVip struct {
	mysql.Entity
	*domain.Model       `gorm:"-"`
	UserId              mysql.ID
	ExpireAt            time.Time          //结束时间
	Type                user_e.UserVipType //来源类型
	Platform            mysql.Platform
	VipSubscribeOrderId mysql.ID //最后的订单ID
}

// 检查某用户是否Vip
func (user *User) CheckVip() (bool, error) {
	rows := make([]UserVip, 0)
	err := user.Model.Db.Where("user_id = ? AND expire_at >= NOW()", user.ID).Find(&rows).Error
	if err != nil {
		return false, err
	}
	if len(rows) > 0 {
		return true, nil
	}
	return false, nil
}

// 检查某用户是否Vip
func IsVip(userId uint64) (bool, *int64, error) {
	uv, err := GetVip(mysql.Db, userId)
	if err != nil {
		return false, nil, err
	}
	if uv == nil {
		return false, nil, nil
	}
	ts := uv.ExpireAt.Unix()
	return true, &ts, nil
}

func GetVip(db *gorm.DB, userId uint64) (*UserVip, error) {
	rows := make([]UserVip, 0)
	err := db.Where("user_id = ? AND expire_at >= NOW()", userId).Find(&rows).Error
	if err != nil {
		return nil, err
	}
	if len(rows) > 0 {
		return &rows[0], nil
	}
	return nil, nil
}

func BatchGetVips(userIds []uint64) (map[uint64]*int64, error) {
	rows := make([]UserVip, 0)
	err := mysql.Db.Where("user_id IN ?", userIds).Find(&rows).Error
	if err != nil {
		return nil, err
	}

	result := make(map[uint64]*int64, 0)
	for _, i := range userIds {
		result[i] = nil
	}
	now := time.Now()
	for _, i := range rows {
		if i.ExpireAt.After(now) {
			ts := i.ExpireAt.Unix()
			result[i.UserId] = &ts
		}
	}
	return result, nil
}

// 查询当前有效的vips(google)
func GetValidVipsGoogle(db *gorm.DB, userId uint64) (*UserVip, error) {
	rows := make([]UserVip, 0)
	err := db.Table("user_vip AS u").
		Joins("INNER JOIN vip_subscribe_order_id AS s ON u.vip_subscribe_order_id = s.id").
		Where("platform = ? and expire_at >= NOW()", user_e.Google).Find(&rows).Error
	if err != nil {
		return nil, err
	}
	if len(rows) > 0 {
		return &rows[0], nil
	}
	return nil, nil
}

//初始化用户Vip, 存在则抛出错误
func InitUserVip(model *domain.Model, userId uint64) (*UserVip, error) {
	var n int64
	if err := model.Db.Model(&UserVip{}).Where(&UserVip{
		UserId: userId,
	}).Count(&n).Error; err != nil {
		return nil, myerr.WrapErr(err)
	}
	if n > 0 {
		return nil, myerr.NewSysErrorF("该用户已经拥有/曾经拥有vip")
	}
	return &UserVip{
		Model:  model,
		UserId: userId,
	}, nil
}

//获取UserVip 不存在则抛出错误
func GetUserVip(model *domain.Model, userId uint64) (*UserVip, error) {
	userVip := UserVip{}
	if err := model.Db.Where(&UserVip{
		UserId: userId,
	}).First(&userVip).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, myerr.NewSysErrorF("userVip userId:%v 不存在", userId)
		} else {
			return nil, myerr.WrapErr(err)
		}
	}
	userVip.Model = model
	return &userVip, nil
}

func GetUserVipNil(model *domain.Model, userId uint64) (*UserVip, error) {
	userVip := UserVip{}
	if err := model.Db.Where(&UserVip{
		UserId: userId,
	}).First(&userVip).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		} else {
			return nil, myerr.WrapErr(err)
		}
	}
	userVip.Model = model
	return &userVip, nil
}

//设置userVip来源于管理者
func (userVip *UserVip) SetOriginByMgr(expireAt time.Time) *UserVip {
	userVip.Type = user_e.UserVipTypeGive
	userVip.Platform = 0
	userVip.VipSubscribeOrderId = 0
	userVip.ExpireAt = expireAt
	return userVip
}

//设置userVip来源于管理人
func (userVip *UserVip) AddDaysByMgr(days uint64) *UserVip {
	userVip.Type = user_e.UserVipTypeGive
	userVip.Platform = 0
	userVip.VipSubscribeOrderId = 0
	userVip.ExpireAt = userVip.ExpireAt.AddDate(0, 0, int(days))
	return userVip
}

//删除userVip来源于管理人,支付购买的,无法删除
func (userVip *UserVip) DelByMgr() (*UserVip, error) {
	if userVip.Type == user_e.UserVipTypeBuy {
		return nil, myerr.NewSysError("真金白银购买的VIP不能删除")
	} else {
		if userVip.ExpireAt.After(time.Now()) {
			userVip.ExpireAt = time.Now()
		}
	}
	return userVip, nil
}

func MakeVip(db *gorm.DB, uvType user_e.UserVipType, userId uint64, expiredTime time.Time, subscribeOrderId uint64, platform mysql.Platform) error {
	uv := UserVip{
		UserId:              userId,
		ExpireAt:            expiredTime,
		Type:                uvType,
		Platform:            platform,
		VipSubscribeOrderId: subscribeOrderId,
	}
	return db.Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "user_id"}},
		DoUpdates: clause.Assignments(map[string]interface{}{
			"type":                   uvType,
			"platform":               platform, // TODO:如何处理切换平台订阅的问题?
			"expire_at":              expiredTime,
			"vip_subscribe_order_id": subscribeOrderId}),
	}).Create(&uv).Error
}

func ExtendVip(db *gorm.DB, userId uint64, expireAt time.Time) error {
	return db.Model(&UserVip{}).Where("user_id = ?", userId).Update("expire_at", expireAt).Error
}

// 检查一批用户中有没有一个是Vip
func CheckVipByUserIds(userIds []uint64) (bool, error) {
	rows := make([]UserVip, 0)
	err := mysql.Db.Where("user_id IN ?", userIds).Find(&rows).Error
	if err != nil {
		return false, err
	}
	now := time.Now()
	for _, i := range rows {
		if i.ExpireAt.After(now) {
			return true, nil
		}
	}
	return false, nil
}

type VipSubscribeApple struct {
	mysql.Entity
	*domain.Model         `gorm:"-"`
	UserId                mysql.ID
	Receipt               mysql.Str
	TransactionId         mysql.Str
	OriginalTransactionId mysql.Str
}

func (v *VipSubscribeApple) Get() (*VipSubscribeApple, error) {
	rows := make([]VipSubscribeApple, 0)
	err := mysql.Db.Where(v).Find(&rows).Error
	if err != nil {
		return nil, err
	}
	if len(rows) <= 0 {
		return nil, nil
	}
	return &rows[0], nil
}

// 添加ios支付凭证
// 1. 检查旧订单(同origin_transaction_id)
// 2. 处理完全相同的transaction_id订单,更新对应的user_id
// 3. 若无相同transaction_id,则插入新的记录
// return: vip_subscribe_apple的唯一id, 旧的单子, error
func (v *VipSubscribeApple) Add(model *domain.Model) (uint64, []VipSubscribeApple, error) {
	// 检查,订单是否已经存在
	// 订阅的单子,需要看original_transaction_id判断
	var olds []VipSubscribeApple
	if err := model.Db.Model(&VipSubscribeApple{}).Where(&VipSubscribeApple{
		//TransactionId: v.TransactionId,
		OriginalTransactionId: v.OriginalTransactionId,
	}).Find(&olds).Error; err != nil {
		return 0, nil, myerr.WrapErr(err)
	}
	for _, old := range olds {
		// 完全是拿旧的transactionId
		if old.TransactionId == v.TransactionId {
			if err := model.Db.Model(&VipSubscribeApple{}).Where("transaction_id = ?", v.TransactionId).Update("user_id", v.UserId).Error; err != nil {
				return 0, nil, err
			} else {
				return old.ID, olds, nil
			}
		}
	}
	if err := model.Db.Model(&VipSubscribeApple{}).Create(&v).Error; err != nil {
		return 0, nil, myerr.WrapErr(err)
	}
	return v.ID, olds, nil
}

type VipSubscribeGoogle struct {
	mysql.Entity
	*domain.Model  `gorm:"-"`
	UserId         mysql.ID
	PackageName    mysql.Str
	SubscriptionID mysql.Str
	PurchaseToken  mysql.Str
}

func GetGoogleSubscribe(model *domain.Model, packageName, subscriptionID, purchaseToken mysql.Str) (*VipSubscribeGoogle, error) {
	q := VipSubscribeGoogle{
		Model:       model,
		PackageName: packageName, SubscriptionID: subscriptionID, PurchaseToken: purchaseToken}
	return q.Get()
}

func BatchGetGoogleSubscribe(db *gorm.DB, ids []uint64) ([]VipSubscribeGoogle, error) {
	rows := make([]VipSubscribeGoogle, 0)
	err := db.Model(&VipSubscribeGoogle{}).Where("id IN ?", ids).Find(&rows).Error
	if err != nil {
		return nil, err
	}
	return rows, nil
}

func (v *VipSubscribeGoogle) Get() (*VipSubscribeGoogle, error) {
	rows := make([]VipSubscribeGoogle, 0)
	err := v.Db.Where(v).Find(&rows).Error
	if err != nil {
		return nil, err
	}
	if len(rows) <= 0 {
		return nil, nil
	}
	return &rows[0], nil
}

func (v *VipSubscribeGoogle) Add() (uint64, error) {
	err := v.Db.Model(&VipSubscribeGoogle{}).Create(&v).Error
	if err == nil {
		return v.ID, nil
	}
	return 0, err
}

type GoogleSubscribeState struct {
	mysql.Entity
	*domain.Model              `gorm:"-"`
	PurchaseToken              mysql.Str
	OrderId                    mysql.Str
	CountryCode                mysql.Str
	PriceAmountMicros          int64
	PriceCurrencyCode          mysql.Str
	StartTimeMillis            int64
	ExpiryTimeMillis           int64
	AutoRenewing               bool
	LinkedPurchaseToken        mysql.Str
	PaymentState               int64
	CancelReason               int64
	UserCancellationTimeMillis int64
}

func (gss *GoogleSubscribeState) Set() (uint64, error) {
	err := gss.Db.Clauses(clause.OnConflict{
		UpdateAll: true,
	}).Create(gss).Error
	if err == nil {
		return gss.ID, nil
	}
	return 0, err
}