package svc

import (
	"bytes"
	"errors"
	"math/rand"
	"net/rpc"
	"sync"

	"github.com/globalsign/mgo"
	"github.com/globalsign/mgo/bson"
	"mongo.games.com/game/dbproxy/mongo"
	"mongo.games.com/game/model"
)

/*
 邀请码生成规则
 1. 邀请码长度为8位，由数字和字母组成
 2. 0-9，a-z，A-Z随机组合
*/

var (
	InviteCodeDBName   = "user"
	InviteCodeCollName = "user_invitecode"
	InviteCodeColError = errors.New("InviteCode collection open failed")
	InviteCodeMutex    = sync.Mutex{}
)

func InviteCodeCollection(plt string) *mongo.Collection {
	s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, InviteCodeDBName)
	if s != nil {
		c, first := s.DB().C(InviteCodeCollName)
		if first {
			c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Unique: true, Background: true, Sparse: true})
			c.EnsureIndex(mgo.Index{Key: []string{"code"}, Unique: true, Background: true, Sparse: true})
		}
		return c
	}

	return nil
}

func GetInviteCode(plt string, snid int32) (string, error) {
	c := InviteCodeCollection(plt)
	if c == nil {
		return "", InviteCodeColError
	}

	col := new(model.InviteCode)
	err := c.Find(bson.M{"snid": snid}).One(col)
	notFound := errors.Is(err, mgo.ErrNotFound)
	if err != nil && !notFound {
		return "", err
	}

	if notFound || col.Code == "" {
		InviteCodeMutex.Lock()
		defer InviteCodeMutex.Unlock()
		// 创建
		var n int
		for i := 0; i < 100; i++ {
			code := getInviteCode(8)
			n, err = c.Find(bson.M{"code": code}).Count()
			if err != nil {
				return "", err
			}
			if n > 0 {
				continue
			}
			col.Id = bson.NewObjectId()
			col.SnId = snid
			col.Code = code
			_, err = c.Upsert(bson.M{"snid": snid}, col)
			break
		}
	}

	return col.Code, err
}

const StringList = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func getInviteCode(n int) string {
	b := bytes.NewBuffer(make([]byte, 0, n))
	for i := 0; i < n; i++ {
		b.WriteByte(StringList[rand.Intn(len(StringList))])
	}
	return b.String()
}

type InviteCodeSvc struct {
}

func (i *InviteCodeSvc) GetSnIdByCode(req *model.InviteSnIdReq, ret *model.InviteSnIdRet) error {
	c := InviteCodeCollection(req.Platform)
	if c == nil {
		return InviteCodeColError
	}
	col := new(model.InviteCode)
	err := c.Find(bson.M{"code": req.Code}).One(col)
	if err != nil && !errors.Is(err, mgo.ErrNotFound) {
		return err
	}
	ret.SnId = col.SnId
	return nil
}

func init() {
	rpc.Register(new(InviteCodeSvc))
}
