package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/globalsign/mgo/bson"
	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/i18n"
	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/module"
	"mongo.games.com/goserver/core/task"

	"mongo.games.com/game/model"
	hall_proto "mongo.games.com/game/protocol/gamehall"
	"mongo.games.com/game/protocol/rankmatch"
	"mongo.games.com/game/srvdata"
	"mongo.games.com/game/worldsrv/internal"
)

var RankMgrSingleton = &RankMatchMgr{
	seasons:       make(map[string]*RankSeasonId),
	playerSeasons: make(map[int32]*PlayerRankSeason),
}

// RankSeasonId 赛季配置
type RankSeasonId struct {
	Dirty bool
	*model.RankSeasonId
}

// PlayerRankSeason 玩家排位信息
type PlayerRankSeason struct {
	Dirty bool
	*model.PlayerRankSeason
}

func (this *PlayerRankSeason) sendEmailAward(rankType int32) {
	lv, _ := RankMgrSingleton.GetPlayerRankLV(rankType, this.SnId)

	for _, item := range RankMgrSingleton.GetRankAwardList(rankType) {
		if lv >= item.Lv {
			var has bool
			for _, v := range this.RankType[rankType].Awards {
				if v.Id == item.Id {
					has = true
					break
				}
			}
			if !has {
				this.RankType[rankType].Awards = append(this.RankType[rankType].Awards, &model.RankAward{
					Id: item.Id,
					Ts: time.Now().Unix(),
				})

				var coin int64
				var diamond int64
				var otherParams []int32
				for _, v := range item.Awards {
					switch v.Id {
					case 1: // 金币
						coin = int64(v.Num)
					case 2: // 钻石
						diamond = int64(v.Num)
					default:
						// 道具
						otherParams = []int32{v.Id, v.Num}
					}
				}

				// 邮件发送奖励
				var newMsg *model.Message
				index := item.Id
				task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
					var rankName []string
					for _, v := range []string{"zh", "vi", "en", "kh"} {
						rankName = append(rankName, i18n.Tr(v, fmt.Sprintf("RankReward_%d_%d", rankType, index)))
					}
					title := i18n.Tr("languages", "RankAwardTitle", rankName)
					content := i18n.Tr("languages", "RankAward")

					newMsg = model.NewMessage("", 0, "", this.SnId, model.MSGTYPE_RANK_REWARD,
						title, content, coin, diamond, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", otherParams, this.Platform, model.HallTienlen)

					return model.InsertMessage(this.Platform, newMsg)
				}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) {
					if data == nil {
						p := PlayerMgrSington.GetPlayerBySnId(this.SnId)
						if p != nil {
							p.AddMessage(newMsg)
						}
					}
				}), "AddMailByItem").StartByFixExecutor("SendMail")
			}
		}
	}
}

// CheckNewSeason 检查赛季继承
func (this *PlayerRankSeason) CheckNewSeason() {
	var n int
	var f func()
	f = func() {
		cfg := RankMgrSingleton.GetSeasonConfig(this.Platform)
		if cfg.SeasonId > this.SeasonId {
			this.Dirty = true
			this.SeasonId++
			// 领取的奖励发邮件
			for k := range this.RankType {
				this.sendEmailAward(k)
			}

			old, err := json.Marshal(this.RankType)
			if err != nil {
				logger.Logger.Errorf("json.Marshal(this.RankType) err: %v", err)
				return
			}
			this.LastRankType = make(map[int32]*model.PlayerRankInfo)
			if err = json.Unmarshal(old, &this.LastRankType); err != nil {
				logger.Logger.Errorf("json.Unmarshal(old,&this.LastRankType) err: %v", err)
				return
			}
			for k, v := range this.RankType {
				this.RankType[k].Score = RankMgrSingleton.NewSeasonScore(v.Score)
				this.RankType[k].Awards = nil
			}

			f()
			n++
			if n > 1000 { // 防止死循环
				return
			}
		}
	}
	f()

	// 通知积分变更
	if this.Dirty {
		if p := PlayerMgrSington.GetPlayerBySnId(this.SnId); p != nil {
			p.SendRankSeason()
		}
		for k := range this.RankType {
			this.rankLog(k)
			this.CheckShowRed(k)
		}
	}
}

func (this *PlayerRankSeason) Update(rankScore map[int32]int64) {
	this.CheckNewSeason()

	for k, v := range this.RankType {
		if v != nil && v.Score != rankScore[k] {
			v.Score = rankScore[k]
			this.Dirty = true
			this.CheckShowRed(k)
			this.rankLog(k)
		}
	}

	for k, v := range rankScore {
		info := this.RankType[k]
		if info == nil {
			info = &model.PlayerRankInfo{
				Score: v,
			}
			this.RankType[k] = info
			this.Dirty = true
			this.CheckShowRed(k)
			this.rankLog(k)
		}
	}

	if this.Dirty {
		if p := PlayerMgrSington.GetPlayerBySnId(this.SnId); p != nil {
			p.SendRankSeason()
		}
	}
}

// CheckShowRed 检查是否显示红点
func (this *PlayerRankSeason) CheckShowRed(rankType int32) {
	lv, _ := RankMgrSingleton.GetPlayerRankLV(rankType, this.SnId)
	info := this.RankType[rankType]
	var has bool // 有未领取的
here:
	for _, item := range RankMgrSingleton.GetRankAwardList(rankType) {
		if lv >= item.Lv { // 可领取
			if info == nil {
				has = true
				break
			}
			if len(info.Awards) == 0 {
				has = true
				break
			}
			for _, v := range info.Awards {
				if v != nil && v.Id == item.Id { // 领过了
					continue here
				}
			}
			has = true
			break
		}
	}
	if has {
		p := PlayerMgrSington.GetPlayerBySnId(this.SnId)
		if p != nil {
			p.SendShowRed(hall_proto.ShowRedCode_RankReward, rankType, 1)
		}
	}
}

func (this *PlayerRankSeason) rankLog(rankType int32) {
	p := PlayerMgrSington.GetPlayerBySnId(this.SnId)
	if p == nil {
		return
	}
	lv, score := RankMgrSingleton.GetPlayerRankLV(rankType, this.SnId)
	// 排行榜
	log := &model.PlayerRankScore{
		Platform: this.Platform,
		SnId:     this.SnId,
		RankType: rankType,
		SeasonId: this.SeasonId,
		Lv:       lv,
		Score:    score,
		Name:     p.Name,
		Sex:      p.Sex,
		HeadUrl:  p.HeadUrl,
		Coin:     p.Coin,
		UpdateTs: time.Now().Unix(),
		ModId:    p.PlayerData.GetRoleId(),
	}
	LogChannelSingleton.WriteLog(log)
}

type RankMatchMgr struct {
	BaseClockSinker
	failSeason    []string
	seasons       map[string]*RankSeasonId    // 当前赛季 key平台id
	playerSeasons map[int32]*PlayerRankSeason // 玩家排位信息 key玩家id
}

// 从数据库读取赛季配置
func (r *RankMatchMgr) load(platform string) {
	var ret *model.RankSeasonId
	var err error
	task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
		ret, err = model.FindOneRankSeasonId(platform)
		if err != nil {
			logger.Logger.Errorf("RankMatchMgr.Load err: %v", err)
			return err
		}
		return nil
	}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
		if err == nil {
			if ret.SeasonId > 0 {
				r.SetSeasonConfig(&RankSeasonId{
					Dirty:        false,
					RankSeasonId: ret,
				})
			} else {
				season := r.fromFile(platform)
				if season != nil {
					season.Dirty = true
					r.SetSeasonConfig(season)
				}
			}
			r.saveRankSeason(platform, true, true)
		} else {
			r.failSeason = append(r.failSeason, platform)
		}
	})).StartByFixExecutor(fmt.Sprintf("platform%s", platform))
}

var ErrTimeStr = errors.New("time str err")

func (r *RankMatchMgr) strToTime(s string) (time.Time, error) {
	t := time.Time{}
	ls := strings.Split(s, "/")
	if len(ls) != 3 {
		return t, ErrTimeStr
	}

	s = ls[0] + fmt.Sprintf("/%02s", ls[1]) + fmt.Sprintf("/%02s", ls[2])
	if len(s) != 10 {
		return t, ErrTimeStr
	}

	return time.ParseInLocation("2006/01/02", s, time.Local)
}

// fromFile 从文件中读取赛季配置
func (r *RankMatchMgr) fromFile(platform string) *RankSeasonId {
	now := time.Now().Unix()
	for _, v := range srvdata.PBDB_RankCycleMgr.Datas.Arr {
		st, err := r.strToTime(v.Start)
		if err != nil {
			logger.Logger.Errorf("RankMatchMgr.fromFile start time err:%v", err)
			continue
		}
		et, err := r.strToTime(v.End)
		if err != nil {
			logger.Logger.Errorf("RankMatchMgr.fromFile end time err:%v", err)
			continue
		}
		if now >= st.Unix() && now < et.AddDate(0, 0, 1).Unix() {
			return &RankSeasonId{
				Dirty: false,
				RankSeasonId: &model.RankSeasonId{
					Platform: platform,
					SeasonId: v.Id,
					StartTs:  st.Unix(),
					EndTs:    et.Unix(),
					UpdateTs: now,
				},
			}
		}
	}
	return nil
}

// saveRankSeason 保存赛季配置
func (r *RankMatchMgr) saveRankSeason(platform string, isSync bool, force bool) {
	ret, ok := r.seasons[platform]
	if !ok || ret == nil || (!ret.Dirty && !force) {
		return
	}

	//todo 需要拷贝一份数据再保存吗

	var res bool
	f := func() {
		res = model.UpsertRankSeasonId(ret.RankSeasonId)
	}
	cf := func() {
		if res {
			ret.Dirty = false
		}
	}

	if isSync {
		f()
		cf()
		return
	}

	task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
		f()
		return nil
	}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
		cf()
	})).StartByFixExecutor(fmt.Sprintf("platform%s", platform))
}

// checkNewSeason 是否新赛季
func (r *RankMatchMgr) checkNewSeason(platform string) bool {
	newSeason := r.fromFile(platform)
	oldSeason := r.GetSeasonConfig(platform)
	var oldId, newId int32
	if oldSeason != nil {
		oldId = oldSeason.SeasonId
	}
	if newSeason != nil {
		newId = newSeason.SeasonId
	}

	if newId > 0 && oldId != newId {
		// 新赛季
		newSeason.Dirty = true
		r.SetSeasonConfig(newSeason)

		// 玩家段位继承
		for _, v := range r.playerSeasons {
			v.CheckNewSeason()
		}

		return true
	}
	return false
}

// GetSeasonConfig 获取当前赛季配置
func (r *RankMatchMgr) GetSeasonConfig(platform string) *RankSeasonId {
	return r.seasons[platform]
}

// SetSeasonConfig 设置当前赛季配置
func (r *RankMatchMgr) SetSeasonConfig(config *RankSeasonId) {
	if config == nil {
		return
	}
	r.seasons[config.Platform] = config

	r.checkNewSeason(config.Platform)
}

func (r *RankMatchMgr) GetPlayerSeason(snid int32) *PlayerRankSeason {
	ret := r.playerSeasons[snid]
	if ret == nil {
		return nil
	}
	return ret
}

func (r *RankMatchMgr) GetPlayerRankScore(snid int32) map[int32]int64 {
	ret := make(map[int32]int64)
	if rank := r.GetPlayerSeason(snid); rank != nil && rank.PlayerRankSeason != nil {
		for k, v := range rank.RankType {
			if v != nil {
				ret[k] = v.Score
			}
		}
	}
	return ret
}

func (r *RankMatchMgr) GetPlayerRankScoreInt64(snid int32) map[int64]int64 {
	ret := make(map[int64]int64)
	if rank := r.GetPlayerSeason(snid); rank != nil && rank.PlayerRankSeason != nil {
		for k, v := range rank.RankType {
			if v != nil {
				ret[int64(k)] = v.Score
			}
		}
	}
	return ret
}

func (r *RankMatchMgr) CheckShowRed(snid int32) {
	p := r.GetPlayerSeason(snid)
	if p == nil {
		return
	}
	for k := range p.RankType {
		p.CheckShowRed(k)
	}
}

func (r *RankMatchMgr) UpdatePlayerSeason(snid int32, rankScore map[int32]int64) {
	season := r.GetPlayerSeason(snid)
	if season != nil {
		season.Update(rankScore)
	}
}

func (r *RankMatchMgr) UpdateRobotSeason(platform string, snid int32, rankType int32, score int64,
	name string, sex int32, headUrl string, coin int64, roleId int32) {

	log := &model.PlayerRankScore{
		Platform: platform,
		SnId:     snid,
		RankType: rankType,
		SeasonId: r.GetSeasonConfig(platform).SeasonId,
		Lv:       srvdata.RankLevelMgr.GetRankLevel(rankType, score),
		Score:    score,
		Name:     name,
		Sex:      sex,
		HeadUrl:  headUrl,
		Coin:     coin,
		IsRobot:  true,
		UpdateTs: time.Now().Unix(),
		ModId:    roleId,
	}
	LogChannelSingleton.WriteLog(log)
}

func (r *RankMatchMgr) SetPlayerSeason(config *PlayerRankSeason) {
	if config == nil {
		return
	}

	r.playerSeasons[config.SnId] = config
	// 段位继承
	config.CheckNewSeason()
}

func (r *RankMatchMgr) ModuleName() string {
	return "RankMatchMgr"
}

func (r *RankMatchMgr) Init() {
	for _, platform := range PlatformMgrSingleton.GetPlatforms() {
		if platform.IdStr == DefaultPlatform {
			continue
		}
		r.load(platform.IdStr)
	}
}

func (r *RankMatchMgr) Update() {
	// 数据库查询失败重新查询
	if len(r.failSeason) > 0 {
		ls := r.failSeason
		r.failSeason = make([]string, 0)
		for _, v := range ls {
			r.load(v)
		}
	}
}

func (r *RankMatchMgr) Shutdown() {
	for _, platform := range PlatformMgrSingleton.GetPlatforms() {
		if platform.IdStr == DefaultPlatform {
			continue
		}
		r.saveRankSeason(platform.IdStr, true, true)
	}

	module.UnregisteModule(r)
}

// NewSeasonScore 赛季继承积分
func (r *RankMatchMgr) NewSeasonScore(old int64) int64 {
	return srvdata.RankLevelMgr.GetNewScore(old)
}

func (r *RankMatchMgr) GetPlayerRankLV(rankType, snid int32) (int32, int64) {
	if rankType <= 0 {
		return 0, 0
	}

	rank := r.GetPlayerSeason(snid)
	if rank == nil || rank.RankType == nil || rank.RankType[rankType] == nil {
		return 0, 0
	}

	score := rank.RankType[rankType].Score

	return srvdata.RankLevelMgr.GetRankLevel(rankType, score), score
}

func (r *RankMatchMgr) GetPlayerLastRankLV(rankType, snid int32) (int32, int64) {
	if rankType <= 0 {
		return 0, 0
	}

	rank := r.GetPlayerSeason(snid)
	if rank == nil || rank.LastRankType == nil || rank.LastRankType[rankType] == nil {
		return 0, 0
	}

	score := rank.LastRankType[rankType].Score

	return srvdata.RankLevelMgr.GetRankLevel(rankType, score), score
}

func (r *RankMatchMgr) GetRankList(rankType int32) []*rankmatch.RankItem {
	var list []*rankmatch.RankItem

	// todo 优化
	for _, v := range srvdata.PBDB_RankLevelMgr.Datas.Arr {
		if rankType < v.GetRankType() {
			continue
		}
		if rankType > v.GetRankType() {
			break
		}

		list = append(list, &rankmatch.RankItem{
			Id: v.Id,
			Lv: v.Level,
		})
	}
	sort.Slice(list, func(i, j int) bool {
		return list[i].Lv > list[j].Lv
	})
	return list
}

func (r *RankMatchMgr) getRankScore(rankType int32, lv int32) int64 {
	for _, v := range srvdata.PBDB_RankLevelMgr.Datas.Arr {
		if rankType < v.GetRankType() {
			continue
		}
		if rankType > v.GetRankType() {
			break
		}
		if v.Level == lv {
			return v.Score
		}
	}
	return 0
}

func (r *RankMatchMgr) GetRankAwardList(rankType int32) []*rankmatch.AwardItem {
	var list []*rankmatch.AwardItem

	for _, v := range srvdata.PBDB_RankRewardMgr.Datas.Arr {
		if rankType < v.GetRankType() {
			continue
		}
		if rankType > v.GetRankType() {
			break
		}

		item := &rankmatch.AwardItem{
			Id:    v.Id,
			Lv:    v.Level,
			Score: r.getRankScore(rankType, v.Level),
		}
		item.Awards = []*rankmatch.Award{}
		if v.Award1Num > 0 {
			item.Awards = append(item.Awards, &rankmatch.Award{
				Id:  v.Award1Id,
				Num: v.Award1Num,
			})
		}
		if v.Award2Num > 0 {
			item.Awards = append(item.Awards, &rankmatch.Award{
				Id:  v.Award2Id,
				Num: v.Award2Num,
			})
		}
		if v.Award3Num > 0 {
			item.Awards = append(item.Awards, &rankmatch.Award{
				Id:  v.Award3Id,
				Num: v.Award3Num,
			})
		}
		list = append(list, item)
	}
	sort.Slice(list, func(i, j int) bool {
		return list[i].Lv > list[j].Lv
	})
	return list
}

//========================implement ClockSinker ==============================

func (r *RankMatchMgr) InterestClockEvent() int {
	return 1 << CLOCK_EVENT_DAY
}

func (r *RankMatchMgr) OnDayTimer() {
	logger.Logger.Info("(this *RankMatchMgr) OnDayTimer")
	for _, platform := range PlatformMgrSingleton.GetPlatforms() {
		if platform.IdStr == DefaultPlatform {
			continue
		}
		// 检查新赛季
		r.checkNewSeason(platform.IdStr)
		// 保存赛季配置
		r.saveRankSeason(platform.IdStr, false, false)
	}
}

//========================implement IPlayerLoad ==============================

func (r *RankMatchMgr) Load(platform string, snid int32, player any) *internal.PlayerLoadReplay {
	args := &model.FindPlayerRankSeasonArgs{
		Platform: platform,
		Id:       []int32{snid},
	}
	ret, err := model.FindPlayerRankSeason(args)
	return &internal.PlayerLoadReplay{
		Platform: platform,
		Snid:     snid,
		Err:      err,
		Data:     ret.List,
	}
}

func (r *RankMatchMgr) Callback(player any, ret *internal.PlayerLoadReplay) {
	if ret == nil {
		return
	}
	if ret.Err != nil || ret.Data == nil {
		logger.Logger.Errorf("RankMatchMgr load player rankseason err:%v", ret.Err)
		return
	}

	ls := ret.Data.([]*model.PlayerRankSeason)
	if len(ls) == 0 {
		// 初始化
		s := r.GetSeasonConfig(ret.Platform)
		if s == nil {
			return
		}
		r.SetPlayerSeason(&PlayerRankSeason{
			Dirty: true,
			PlayerRankSeason: model.NewPlayerRankSeason(&model.NewPlayerRankSeasonArgs{
				Platform: ret.Platform,
				SnId:     ret.Snid,
				SeasonId: s.SeasonId,
			}),
		})
	} else {
		r.SetPlayerSeason(&PlayerRankSeason{
			Dirty:            false,
			PlayerRankSeason: ls[0],
		})
	}
}

func (r *RankMatchMgr) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay {
	return nil
}

func (r *RankMatchMgr) CallbackAfter(ret *internal.PlayerLoadReplay) {
}

func (r *RankMatchMgr) Save(platform string, snid int32, isSync, force bool) {
	ret, ok := r.playerSeasons[snid]
	if !ok || ret == nil || (!ret.Dirty && !force) {
		return
	}

	var res bool
	f := func() {
		if ret.Id == "" {
			ret.Id = bson.NewObjectId()
		}
		res = model.UpsertPlayerRankSeason(ret.PlayerRankSeason)
	}
	cf := func() {
		if res {
			ret.Dirty = false
		}
	}

	if isSync {
		f()
		cf()
		return
	}

	task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
		f()
		return nil
	}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
		cf()
	})).StartByFixExecutor(fmt.Sprintf("platform%s", ret.Platform))
}

func (r *RankMatchMgr) Release(platform string, snid int32) {
	delete(r.playerSeasons, snid)
}

func init() {
	module.RegisteModule(RankMgrSingleton, time.Minute, 0)
	ClockMgrSington.RegisteSinker(RankMgrSingleton)
	internal.RegisterPlayerLoad(RankMgrSingleton)
}
