package model

import (
	"errors"
	"fmt"
	"math/rand"
	"time"

	"github.com/globalsign/mgo/bson"
	"mongo.games.com/goserver/core/logger"
)

// 礼包码
var (
	chars = []rune{
		'4', '1', '7', '2', '3', '6', '5', '8', '9',
		'q', 'n', 'c', 'r', 't', 'm', 'a', 'd', 'e', 'h', 'j', 'l', 'f', 'b', 'g',
		'Q', 'N', 'C', 'R', 'T', 'M', 'A', 'D', 'E', 'H', 'J', 'L', 'F', 'B', 'G',
	}
	divider            = 'i' // 分割标识(区分补位，应该是chars里面没出现的字符)
	charLen            = uint64(len(chars))
	charIndexMap       = make(map[rune]int, len(chars)) //  初始化就完成写入 不存在并发读写问题
	Keystart     int64 = 100000                         // 单兑换码上限十万

)

var (
	ErrJYBCode       = errors.New("CodeExist")
	ErrJYBRpc        = errors.New("Rpcisnil")
	ErrJYBPlCode     = errors.New("PlayCodeExist")    // 已经兑换过该礼包
	ErrJybISReceive  = errors.New("jyb is receive")   // 该兑换码已被使用
	ErrJybTsTimeErr  = errors.New("jyb ts is not")    // 该兑换码已过期
	ErrJybIsNotExist = errors.New("jyb is not exist") // 请输入正确的兑换码
)

func init() {
	for i, char := range chars {
		charIndexMap[char] = i
	}
}

type JybInfoAward struct {
	Item    []*Item // 道具
	Coin    int64   // 金币
	Diamond int64   // 钻石
}

type JybInfos struct {
	Jybs []*JybInfo
}

type JybInfo struct {
	JybId     bson.ObjectId    `bson:"_id"` // 礼包ID
	Platform  string           //平台
	Name      string           // 礼包名称
	CodeType  int32            // 礼包类型 1 通用 2 特殊
	StartTime int64            // 开始时间 Unix
	EndTime   int64            // 结束时间
	Content   string           // 礼包内容
	Max       int32            // 总个数
	Receive   int32            // 领取个数
	CodeStart int64            // Code生成起始
	Code      map[string]int32 // 礼包码
	Award     *JybInfoAward    // 礼包内东西
	CreateTs  int64
}

// JybKey 自增
type JybKey struct {
	KeyId    bson.ObjectId `bson:"_id"`
	Keyint   int64
	Plakeyid string            // keystart+Platform
	GeCode   map[string]string // 通用礼包
}

// 验证玩家是否领取礼包  可能存在map并发问题
type JybUserInfo struct {
	JybUserId bson.ObjectId    `bson:"_id"`
	SnId      int32            // 玩家id
	Platform  string           //平台
	JybInfos  map[string]int32 //已领取礼包 web和world使用 注意线程安全
}

type GetJybInfoArgs struct {
	Id       string //  礼包ID
	Plt      string //  平台
	UseCode  string //  礼包码
	SnId     int32  // 玩家id
	CodeType int32
}

type InitJybInfoArgs struct {
	Plt string //  平台
}

type VerifyUpJybInfoArgs struct {
	UseCode   string //  礼包码
	CodeStart int64  // Code生成起始
	Plt       string // 平台
	SnId      int32  // 玩家id
	CodeType  int32
}

type CreateJyb struct {
	*JybInfo
	Codelen int32
	Num     uint64
}

// NewJybUserInfo 初始化
func NewJybKey(plt string) *JybKey {
	jk := &JybKey{KeyId: bson.NewObjectId(), Keyint: Keystart, Plakeyid: fmt.Sprintf("%d_%s", Keystart, plt)}
	return jk
}

// sync.Map  new(sync.Map)
// NewJybUserInfo 初始化
func NewJybUserInfo(sid int32, plt string) *JybUserInfo {
	jUserInfo := &JybUserInfo{JybUserId: bson.NewObjectId(), SnId: sid, Platform: plt, JybInfos: make(map[string]int32)}
	return jUserInfo
}

func NewJybInfo(plt, name, content, code string, startTs, endTs int64, max, codetype int32, award *JybInfoAward) *JybInfo {
	jyb := &JybInfo{
		JybId:     bson.NewObjectId(),
		Platform:  plt,
		Name:      name,
		Content:   content,
		CodeType:  codetype,
		StartTime: startTs,
		EndTime:   endTs,
		Max:       max,
		Receive:   0,
		Code:      make(map[string]int32),
		Award:     award,
		CreateTs:  time.Now().Unix(),
	}
	if codetype == 1 {
		jyb.Code[code] = 1
	}
	return jyb
}

func NewJybCode(jyb *JybInfo, codelen int32, num uint64) {
	cl := int(codelen)
	num += uint64(jyb.CodeStart)
	for i := uint64(jyb.CodeStart); i != num; i++ {
		key := Id2Code(i, cl, true)
		jyb.Code[key] = 1
	}
}

func CreateJybInfo(args *CreateJyb) error {
	if rpcCli == nil {
		return ErrJYBRpc
	}
	ret := &JybInfo{}
	err := rpcCli.CallWithTimeout("JybSvc.CreateJybItem", args, &ret, time.Second*30)
	if err != nil {
		logger.Logger.Warnf("CreateJybInfo err:%v Id:%v ", err, args.JybId)
		return err
	}
	//args.JybInfo = ret // 获取code
	return nil
}

func UpgradeJybUser(args *GetJybInfoArgs) *JybInfo {
	if rpcCli == nil {
		return nil
	}
	ret := &JybInfo{}
	err := rpcCli.CallWithTimeout("JybUserSvc.UpgradeJybUser", args, ret, time.Second*30)
	if err != nil {
		logger.Logger.Warnf("UpgradeJybUser err:%v Id:%v ", err, args.UseCode)
		return nil
	}
	return ret
}

func VerifyUpJybInfo(args *VerifyUpJybInfoArgs) (*JybInfo, error) {
	if rpcCli == nil {
		return nil, ErrJYBRpc
	}
	ret := &JybInfo{}
	err := rpcCli.CallWithTimeout("JybUserSvc.VerifyUpJybUser", args, ret, time.Second*30)
	if err != nil {
		logger.Logger.Warnf("VerifyUpJybUser err:%v Id:%v ", err, args.UseCode)
		return nil, err
	}
	return ret, err
}

func DelJybInfo(args *GetJybInfoArgs) error {

	if rpcCli == nil {
		return nil
	}

	ret := false
	err := rpcCli.CallWithTimeout("JybSvc.DelJyb", args, &ret, time.Second*30)
	if err != nil {
		logger.Logger.Warnf("DelJybInfo err:%v Id:%v ", err, args.Id)
	}

	return err
}

func InitJybItem(args *InitJybInfoArgs) *JybInfos {

	if rpcCli == nil {
		return nil
	}

	ret := &JybInfos{}
	err := rpcCli.CallWithTimeout("JybSvc.InitJybItem", args, ret, time.Second*30)
	if err != nil {
		logger.Logger.Warnf("InitJybItem err:%v Id:%v ", err, args.Plt)
		return nil
	}

	return ret
}

func reverseSlice(strSlice []rune) []rune {
	sLen := len(strSlice)
	for i := 0; i < sLen/2; i++ {
		strSlice[i], strSlice[sLen-i-1] = strSlice[sLen-i-1], strSlice[i]
	}
	return strSlice
}

// Id2Code 把id转为兑换码
func Id2Code(id uint64, codeMixLength int, isRandomFix bool) string {
	code := make([]rune, 0, codeMixLength)
	for id/charLen > 0 {
		code = append(code, chars[id%charLen])
		id /= charLen
	}

	code = append(code, chars[id%charLen]) // 处理未除尽的余数
	// slice.ReverseSlice(code)
	reverseSlice(code)
	fixLen := codeMixLength - len(code) // 需要补码的长度
	if fixLen > 0 {
		rand.Seed(time.Now().UnixNano())
		code = append(code, divider)
		for i := 0; i < fixLen-1; i++ {
			// 每次固定，如果需要变的话，后面补码的内容可以改变
			if isRandomFix {
				code = append(code, chars[rand.Intn(int(charLen))])
			} else {
				code = append(code, chars[i])
			}
		}
	}
	return string(code)
}

func Code2Id(code string) (uint64, error) {
	if len(code) == 0 {
		return 0, nil
	}
	var id uint64 = 0
	codeRuneList := []rune(code)
	for i := range codeRuneList {
		// 如果是补码标志直接退出
		if codeRuneList[i] == divider {
			break
		}
		charIndex, ok := charIndexMap[codeRuneList[i]]
		if !ok {
			return 0, errors.New("code有误，解码失败")
		}
		if i > 0 {
			id = id*charLen + uint64(charIndex)
		} else {
			id = uint64(charIndex)
		}
	}
	return id, nil
}
