package svc

import (
	"encoding/gob"
	"errors"
	"fmt"
	"net/rpc"
	"strings"
	"time"

	"github.com/globalsign/mgo"
	"github.com/globalsign/mgo/bson"
	"mongo.games.com/game/dbproxy/mongo"
	"mongo.games.com/game/mgrsrv/api"
	"mongo.games.com/game/model"
	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/netlib"
	"mongo.games.com/goserver/core/profile"
	"mongo.games.com/goserver/core/schedule"
	"mongo.games.com/goserver/core/transact"
	"mongo.games.com/goserver/core/utils"
)

var (
	MonitorErr = errors.New("monitor_data open failed.")
)

func MonitorDataCollection(cname string) *mongo.Collection {
	s := mongo.MgoSessionMgrSington.GetGlobal(model.MonitorDBName)
	if s != nil {
		c, first := s.DB().C(fmt.Sprintf("%v_%v", model.MonitorPrefixName, strings.ToLower(cname)))
		if first {
			c.EnsureIndex(mgo.Index{Key: []string{"srvid"}, Background: true, Sparse: true})
			c.EnsureIndex(mgo.Index{Key: []string{"srvtype"}, Background: true, Sparse: true})
			c.EnsureIndex(mgo.Index{Key: []string{"key"}, Background: true, Sparse: true})
			c.EnsureIndex(mgo.Index{Key: []string{"time"}, Background: true, Sparse: true})
		}
		return c
	}
	return nil
}

type MonitorDataSvc struct {
}

func (svc *MonitorDataSvc) InsertMonitorData(args *model.MonitorDataArg, ret *bool) (err error) {
	clog := MonitorDataCollection(args.Name)
	if clog == nil {
		return
	}

	err = clog.Insert(args.Log)
	if err != nil {
		logger.Logger.Warnf("InsertMonitorData(%v) error:%v", args.Name, err)
		return
	}
	*ret = true
	return
}

func (svc *MonitorDataSvc) UpsertMonitorData(args *model.MonitorDataArg, ret *bool) (err error) {
	clog := MonitorDataCollection(args.Name)
	if clog == nil {
		return
	}

	var existLog model.MonitorData
	err = clog.Find(bson.M{"srvid": args.Log.SrvId, "srvtype": args.Log.SrvType, "key": args.Log.Key}).One(&existLog)
	if err == nil {
		args.Log.LogId = existLog.LogId
		err = clog.Update(bson.M{"_id": args.Log.LogId}, args.Log)
		if err != nil {
			logger.Logger.Warnf("UpsertMonitorData(%v) Update error:%v", args.Name, err)
			return
		}
	} else {
		if err == mgo.ErrNotFound {
			err = clog.Insert(args.Log)
			if err != nil {
				logger.Logger.Warnf("UpsertMonitorData(%v) Insert error:%v", args.Name, err)
				return
			}
		}
	}
	*ret = true
	return
}

func (svc *MonitorDataSvc) RemoveMonitorData(t time.Time, changes *[]*mgo.ChangeInfo) (err error) {
	s := mongo.MgoSessionMgrSington.GetGlobal(model.MonitorDBName)
	if s != nil {
		db := s.DB()
		if db != nil {
			cnames, err := db.CollectionNames()
			if err == nil {
				if len(cnames) != 0 {
					for _, name := range cnames {
						c, _ := db.C(name)
						if c != nil {
							chginfo, err := c.RemoveAll(bson.M{"time": bson.M{"$lt": t}})
							if err == nil {
								*changes = append(*changes, chginfo)
							}
						}
					}
				}
			}
		}
	}
	return
}

func init() {
	//gob registe
	// gob:go语言特有的编解码工具，Register需要在初始化时或提前调用
	// rpc编解码依赖gob,rpc需要知道怎么编解码接口数据(客户端服务端都需要知道)，以下结构体在rpc参数中被转成了interface,所以这里需要告诉gob接口类型的具体结构
	gob.Register(model.PlayerOLStats{})
	gob.Register(map[string]*model.APITransactStats{})
	gob.Register(api.ApiStats{}) // 这里依赖了mgrsrv的结构体，会导致mgrsrv初始化，最好是把结构体放到一个公共包里，不要两个不相关的程序有直接依赖
	gob.Register(map[string]api.ApiStats{})
	gob.Register(mgo.Stats{})
	gob.Register(profile.TimeElement{})
	gob.Register(map[string]profile.TimeElement{})
	gob.Register(netlib.ServiceStats{})
	gob.Register(map[int]netlib.ServiceStats{})
	gob.Register(schedule.TaskStats{})
	gob.Register(map[string]schedule.TaskStats{})
	gob.Register(transact.TransStats{})
	gob.Register(map[int]transact.TransStats{})
	gob.Register(utils.PanicStackInfo{})
	gob.Register(map[string]utils.PanicStackInfo{})
	gob.Register(basic.CmdStats{})
	gob.Register(map[string]basic.CmdStats{})
	gob.Register(utils.RuntimeStats{})
	//gob registe

	rpc.Register(new(MonitorDataSvc))
}
