package model

import (
	"sync/atomic"
	"time"

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

	"mongo.games.com/game/common"
)

var (
	CoinLogDBName        = "log"
	CoinLogCollName      = "log_coinex"
	TopicProbeCoinLogAck = "ack_logcoin"
)

var COINEX_GLOBAL_SEQ = int64(0)

type CoinLog struct {
	LogId        bson.ObjectId `bson:"_id"`
	SnId         int32         //玩家id
	Platform     string        //平台名称
	Channel      string        //渠道名称
	Promoter     string        //推广员
	PackageTag   string        //推广包标识
	Count        int64         //帐变数量
	RestCount    int64         //钱包余额
	SafeBoxCount int64         //保险箱余额
	DiamondCount int64         //钻石余额
	Oper         string        //操作者
	Remark       string        //备注
	Time         time.Time     //时间戳
	InGame       int32         //0：其他 1~N：具体游戏id
	Ver          int32         //数据版本(暂时不用)
	CoinType     int32         //账变类型 common.BillTypeCoin...
	LogType      int32         //账变类型 common.GainWay_NewPlayer...
	RoomId       int32         //房间id
	SeqNo        int64         //流水号(隶属于进程)
	Ts           int64         //时间戳
	CoinAdd      int64         // 金币加成
	DiamondAdd   int64         // 钻石加成
	GameFreeId   int64         // 场次id
	BaseCoin     int64         // 底分
}

type CoinLogV1 struct {
	LogId      bson.ObjectId `bson:"_id"`
	SnId       int32         //玩家id
	Platform   string        //平台名称
	Channel    string        //渠道名称
	Promoter   string        //推广员
	PackageTag string        //推广包标识
	CoinType   int32         // 账变类型 common.BillTypeCoin...
	LogType    int32         // 账变类型 common.GainWay_NewPlayer...
	Count      int64         // 帐变数量
	Add        int64         // 加成
	RestCount  int64         // 余额
	Oper       string        //操作者
	Remark     string        //备注
	Time       time.Time     //时间戳
	InGame     int32         //0：其他 1~N：具体游戏id
	Ver        int32         //数据版本
	SeqNo      int64         //流水号(隶属于进程)
	Ts         int64         //时间戳
	GameFreeId int64         // 场次id
	BaseCoin   int64         // 底分
}

func NewCoinLog() *CoinLog {
	log := &CoinLog{LogId: bson.NewObjectId()}
	return log
}

type CoinLogParam struct {
	Platform   string // 平台id
	SnID       int32  // 玩家id
	ChangeType int32  // 变化类型 common.BillTypeCoin 金币 common.BillTypeDiamond 钻石
	ChangeNum  int64  // 变化数量
	RemainNum  int64  // 余额
	Add        int64  // 加成
	LogType    int32  // 日志类型
	GameID     int64  // 游戏id
	GameFreeID int64  // 场次id
	BaseCoin   int64  // 底注
	Operator   string // 操作人
	Remark     string // 备注
}

func NewCoinLogEx(param *CoinLogParam) *CoinLog {
	tNow := time.Now()
	cl := NewCoinLog()
	cl.SnId = param.SnID
	cl.Platform = param.Platform
	cl.Count = param.ChangeNum
	cl.Oper = param.Operator
	cl.Remark = param.Remark
	cl.InGame = int32(param.GameID)
	cl.CoinType = param.ChangeType
	cl.LogType = param.LogType
	cl.SeqNo = atomic.AddInt64(&COINEX_GLOBAL_SEQ, 1)
	cl.Time = tNow
	cl.Ts = tNow.Unix()
	cl.GameFreeId = param.GameFreeID
	cl.BaseCoin = param.BaseCoin

	//todo 后期修改数据结构,余额,加成，各用一个字段存储; NewCoinLogExV2
	switch param.ChangeType {
	case common.BillTypeCoin:
		cl.RestCount = param.RemainNum
		cl.CoinAdd = param.Add
	case common.BillTypeDiamond:
		cl.DiamondCount = param.RemainNum
		cl.DiamondAdd = param.Add
	}

	return cl
}

// NewCoinLogExV1 账变记录
func NewCoinLogExV1(param *CoinLogParam) *CoinLogV1 {
	now := time.Now()
	cl := CoinLogV1{
		LogId:      bson.NewObjectId(),
		SnId:       param.SnID,
		Platform:   param.Platform,
		Channel:    "",
		Promoter:   "",
		PackageTag: "",
		CoinType:   param.ChangeType,
		LogType:    param.LogType,
		Count:      param.ChangeNum,
		Add:        param.Add,
		RestCount:  param.RemainNum,
		Oper:       param.Operator,
		Remark:     param.Remark,
		Time:       now,
		InGame:     int32(param.GameID),
		Ver:        1,
		SeqNo:      atomic.AddInt64(&COINEX_GLOBAL_SEQ, 1),
		Ts:         now.Unix(),
		GameFreeId: param.GameFreeID,
		BaseCoin:   param.BaseCoin,
	}
	return &cl
}

type GetCoinLogBySnidAndLessTsArg struct {
	Plt  string
	SnId int32
	Ts   int64
}

func GetCoinLogBySnidAndLessTs(plt string, id int32, ts int64) (ret []CoinLog, err error) {
	if rpcCli == nil {
		logger.Logger.Error("model.GetCoinLogBySnidAndLessTs rpcCli == nil")
		return
	}
	args := &GetCoinLogBySnidAndLessTsArg{
		Plt:  plt,
		SnId: id,
		Ts:   ts,
	}
	err = rpcCli.CallWithTimeout("CoinLogSvc.GetCoinLogBySnidAndLessTs", args, &ret, time.Second*30)
	if err != nil {
		logger.Logger.Warn("GetCoinLogBySnidAndLessTs error:", err)
	}
	return
}

type GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeArg struct {
	Plt      string
	SnId     int32
	LogType  int
	BillType int
	FromIdx  int
	ToIdx    int
	StartTs  int64
	EndTs    int64
}
type GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeRet struct {
	Logs  []CoinLog
	Count int
}

func GetCoinLogBySnidAndTypeAndInRangeTsLimitByRange(plt string, id int32, billType, logType int, startts, endts int64, fromIndex, toIndex int) (logs []CoinLog, count int, err error) {
	if rpcCli == nil {
		logger.Logger.Error("model.GetCoinLogBySnidAndInGameAndGreaterTs rpcCli == nil")
		return
	}

	args := &GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeArg{
		Plt:      plt,
		SnId:     id,
		BillType: billType,
		LogType:  logType,
		FromIdx:  fromIndex,
		ToIdx:    toIndex,
		StartTs:  startts,
		EndTs:    endts,
	}
	var ret GetCoinLogBySnidAndTypeAndInRangeTsLimitByRangeRet
	err = rpcCli.CallWithTimeout("CoinLogSvc.GetCoinLogBySnidAndTypeAndInRangeTsLimitByRange", args, &ret, time.Second*30)
	if err != nil {
		logger.Logger.Warn("GetCoinLogBySnidAndTypeAndInRangeTsLimitByRange error:", err)
		return
	}

	logs = ret.Logs
	count = ret.Count
	return
}

type GetCoinLogGameReq struct {
	Plt      string
	SnId     int32
	BillType int
	FromIdx  int
	ToIdx    int
	StartTs  int64
	EndTs    int64
}
type GetCoinLogGameRet struct {
	Logs  []CoinLog
	Count int
}

func GetCoinLogGame(plt string, id int32, billType int, startts, endts int64, fromIndex, toIndex int) (logs []CoinLog, count int, err error) {
	if rpcCli == nil {
		logger.Logger.Error("model.GetCoinLogGame rpcCli == nil")
		return
	}

	args := &GetCoinLogGameReq{
		Plt:      plt,
		SnId:     id,
		BillType: billType,
		FromIdx:  fromIndex,
		ToIdx:    toIndex,
		StartTs:  startts,
		EndTs:    endts,
	}
	var ret GetCoinLogGameRet
	err = rpcCli.CallWithTimeout("CoinLogSvc.GetCoinLogGame", args, &ret, time.Second*30)
	if err != nil {
		logger.Logger.Warn("GetCoinLogGame error:", err)
		return
	}

	logs = ret.Logs
	count = ret.Count
	return
}

func InsertCoinLog(log *CoinLog) (err error) {
	if rpcCli == nil {
		logger.Logger.Error("model.InsertCoinLog rpcCli == nil")
		return
	}

	var ret bool
	err = rpcCli.CallWithTimeout("CoinLogSvc.InsertCoinLog", log, &ret, time.Second*30)
	if err != nil {
		logger.Logger.Warn("InsertCoinLog error:", err)
	}
	return
}
func RemoveCoinLog(ts int64) (chged *mgo.ChangeInfo, err error) {
	if rpcCli == nil {
		logger.Logger.Error("model.RemoveAPILog rpcCli == nil")
		return
	}
	args := &RemoveCoinLogOneArg{
		Plt: "1",
		Ts:  ts,
	}
	err = rpcCli.CallWithTimeout("CoinLogSvc.RemoveCoinLog", args, &chged, time.Second*30)
	if err != nil {
		logger.Logger.Warn("RemoveCoinLog error:", err)
	}
	return
}

type RemoveCoinLogOneArg struct {
	Plt string
	Id  bson.ObjectId
	Ts  int64
}

func RemoveCoinLogOne(plt string, id bson.ObjectId) error {
	if rpcCli == nil {
		logger.Logger.Error("model.RemoveCoinLogOne rpcCli == nil")
		return nil
	}

	args := &RemoveCoinLogOneArg{
		Plt: plt,
		Id:  id,
	}
	var ret bool
	err := rpcCli.CallWithTimeout("CoinLogSvc.RemoveCoinLogOne", args, &ret, time.Second*30)
	if err != nil {
		logger.Logger.Warn("RemoveCoinLogOne error:", err)
	}
	return err
}

type UpdateCoinLogRemarkArg struct {
	Plt    string
	Id     bson.ObjectId
	Remark string
}

func UpdateCoinLogRemark(plt string, id bson.ObjectId, remark string) error {
	if rpcCli == nil {
		logger.Logger.Error("model.UpdateCoinLogRemark rpcCli == nil")
		return nil
	}

	args := &UpdateCoinLogRemarkArg{
		Plt:    plt,
		Id:     id,
		Remark: remark,
	}
	var ret bool
	err := rpcCli.CallWithTimeout("CoinLogSvc.UpdateCoinLogRemark", args, &ret, time.Second*30)
	if err != nil {
		logger.Logger.Warn("UpdateCoinLogRemark error:", err)
	}
	return err
}
