package models

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

	mhayaTime "github.com/mhaya/extend/time"
	"github.com/mhaya/game/game_cluster/internal/data"
	"github.com/mhaya/game/game_cluster/internal/guid"
	clog "github.com/mhaya/logger"
)

type PlayerLevelStat struct {
	Platform    string         `bson:"platform"  json:"platform"` // platform: 用户所在的平台，例如“Android”或“IOS”
	Channel     string         `bson:"channel" json:"channel"`    // channel: 用户来源渠道
	ServerLevel map[string]int `json:"serverLevel" bson:"serverLevel"`
	UpdateTime  int64          `bson:"updateTime" json:"updateTime"`
}

type PlayerServerLoadStat struct {
	Name       string            `bson:"name" json:"name"`
	TotalUser  int64             `bson:"totalUser" json:"totalUser"`
	Load       map[string]string `bson:"load" json:"load"`
	UpdateTime int64             `bson:"updateTime" json:"updateTime"`
}

type PlayerCountryStat struct {
	Daily                 int64          `bson:"daily" json:"daily"`                                 // 日期
	Platform              string         `bson:"platform"  json:"platform"`                          // platform: 用户所在的平台，例如“Android”或“IOS”
	Channel               string         `bson:"channel" json:"channel"`                             // channel: 用户来源渠道
	PlayerRegisterCountry map[string]int `bson:"playerRegisterCountry" json:"playerRegisterCountry"` // playerRegisterCountry 玩家注册国家
	UpdateTime            int64          `bson:"updateTime" json:"updateTime"`
}

type Preserve struct {
	ID    int64  `json:"id"`
	Num   int    `json:"num"`
	Ratio string `json:"ratio"`
}

var preserveConfig = []int64{2, 3, 7, 14, 30}

// GetPlayerPreserve 根据开始和结束时间获取玩家的保留数据。
// 该函数会检查时间参数是否合理，并限制查询时间范围不超过一周。
// 参数:
//
//	startTime - 查询的开始时间戳。
//	EndTime - 查询的结束时间戳。
//
// 返回值:
//
//	一个map，键是日期的时间戳，值是该日期玩家的保留数据列表。
//	如果查询时间超过当前时间或查询范围超过一周，返回nil。
func GetPlayerPreserve(startTime, EndTime int64) map[int64][]*Preserve {
	// 检查开始和结束时间是否在将来，如果是，返回nil。
	if startTime > mhayaTime.Now().Unix() || EndTime > mhayaTime.Now().Unix() {
		return nil
	}

	// 将开始时间转换为Timestamp类型。
	sTime := mhayaTime.CreateFromTimestamp(startTime).TOTimeStamp()

	// 如果结束时间超过当前时间，则将结束时间设置为当前时间。
	if EndTime > mhayaTime.Now().Unix() {
		EndTime = mhayaTime.Now().Unix()
	}

	// 将结束时间转换为Timestamp类型。
	eTime := mhayaTime.CreateFromTimestamp(EndTime).TOTimeStamp()

	// 计算开始时间和结束时间的天数差。
	day := mhayaTime.CreateFromTimestamp(sTime).DiffInDays(mhayaTime.CreateFromTimestamp(eTime))

	// 如果天数差超过7天，返回nil。
	if day > 7 {
		return nil
	}

	// 初始化返回的map。
	var ret = make(map[int64][]*Preserve, 0)

	// 遍历每一天，获取每一天的玩家保留数据。
	for i := 0; i < int(day); i++ {
		// 计算当前日期的时间戳。
		curTime := mhayaTime.CreateFromTimestamp(sTime).Add(time.Duration(i) * 24 * time.Hour).Unix()

		// 获取并存储当前日期的玩家保留数据。
		ret[curTime] = PlayerPreserve(curTime)
	}

	// 返回包含玩家保留数据的map。
	return ret
}

// PlayerPreserve 计算玩家保留数据。
//
// 参数:
//
//	startTime - 统计周期的开始时间，用于确定从何时开始统计玩家保留数据。
//
// 返回值:
//
//	[]*Preserve - 一个指向Preserve结构体切片的指针，其中每个Preserve结构体包含玩家保留的相关信息。
func PlayerPreserve(startTime int64) []*Preserve {
	// 初始化保存保留数据的切片。
	var preserve []*Preserve

	// 获取新的玩家映射和当前周期内的新注册玩家数量。
	cur, curNum := GetNewPlayerMap(startTime, DailyRecordNewRegistered)

	// 将第一天的新玩家保留数据添加到结果集中。
	preserve = append(preserve, &Preserve{ID: 1, Num: curNum, Ratio: "100%"})

	// 遍历保留配置，计算每个周期的保留数据。
	for _, v := range preserveConfig {
		// 计算下一个统计周期的开始时间。
		nextTime := mhayaTime.CreateFromTimestamp(startTime).Add(time.Duration(v-1) * 24 * time.Hour).Unix()
		// 获取下一个周期内的老玩家映射和该周期内的老玩家数量。
		next, nextNum := GetNewPlayerMap(nextTime, DailyRecordOldLogin)

		// 如果下一个周期内没有老玩家登录，则将保留数据的数目记为0。
		if nextNum == 0 {
			preserve = append(preserve, &Preserve{ID: v, Num: 0, Ratio: ""})
		} else {
			// 计算在当前周期和下一个周期都出现过的玩家数量。
			var num int
			for key, _ := range next {
				if _, ok := cur[key]; ok {
					num++
				}
			}
			// 计算并添加该周期的保留数据到结果集中。
			preserve = append(preserve, &Preserve{ID: v, Num: num, Ratio: fmt.Sprintf("%.2f", float64(num)/float64(curNum)*100)})
		}
	}

	// 返回计算得到的保留数据。
	return preserve
}

func GetNewPlayerMap(startTime int64, op int) (map[string]struct{}, int) {
	var user = make(map[string]struct{})

	platformConfig := data.PlatformConfig.GatMap()
	channelConfig := data.ChannelConfig.GatMap()

	for _, v := range platformConfig {
		for _, v2 := range channelConfig {
			ret, err := GetAppointDailyRecordUserHash(v.Name, v2.Name, startTime, op)
			if err != nil {
				continue
			}
			for k, _ := range ret {
				if _, ok := user[k]; !ok {
					user[k] = struct{}{}
				}
			}

		}
	}
	return user, len(user)
}

func TestPreserve(startTime int64, num int) {
	sTime := mhayaTime.CreateFromTimestamp(startTime).TOTimeStamp()
	for i := 0; i < num; i++ {
		userID := guid.Next()
		clog.Debugf("num=%v, user =%v", i, userID)
		SetAppointDailyRecordNewUserRegisterHash("ios", "tg_sign", userID, "1", sTime, DailyRecordNewRegistered)
		for _, v := range preserveConfig {
			r := rand.Intn(100)
			if r < 80 {
				nextTime := mhayaTime.CreateFromTimestamp(sTime).Add(time.Duration(v-1) * 24 * time.Hour).Unix()
				SetAppointDailyRecordNewUserRegisterHash("ios", "tg_sign", userID, "1", nextTime, DailyRecordOldLogin)
			}
		}
	}
}
