package service

import (
	"encoding/json"
	"fmt"
	"gadmin/config"
	"gadmin/internal/admin/consts"
	"gadmin/internal/admin/forms"
	"gadmin/internal/gorm/model"
	"gadmin/internal/gorm/query"
	"gadmin/package/gmdata"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/now"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cast"
	"sort"
	"strings"
)

var Treasure = new(sTreasure)

type sTreasure struct{}

type TreasureOutput map[string]interface{}

type TreasureReportInfo struct {
	Days    []string `json:"days"`
	Players []int64  `json:"players"`
	TreasureOutput
}

type TreasureLogItem struct {
	Day     string `json:"days"`
	Players int64  `json:"players"`
	TreasureOutput
}

type TreasureInfo struct {
	Info TreasureReportInfo `json:"info"`
	Rows []TreasureLogItem  `json:"rows"`
}

type ReportDayTreasure struct {
	model.ReportDayTreasure
	TreasureOutput
	ActiveCount int64 `json:"active_count"`
}

// QueryTreasureLog 统计宝物碎片信息
func (s *sTreasure) QueryTreasureLog(params forms.TreasureReportReq) (result []*ReportDayTreasure, err error) {
	rdb := query.Use(config.DB).ReportDayTreasure

	type ItemCount struct {
		ItemID     int   `json:"item_id"`
		TotalCount int64 `json:"total_count"`
	}

	m := rdb.Where(rdb.Date.Gte(params.Day), rdb.Date.Lt(params.EndDay)).Order(rdb.Date.Desc())

	switch params.ChannelId {
	case consts.ChannelIdNone:
		// 不选择渠道
	case consts.ChannelIdAllAdv, consts.ChannelIdAllWx, consts.ChannelIdAllTT:
		// 所有的广告渠道
		m = m.Where(rdb.ChannelID.In(Channel.GetIdsByType(params.ChannelId)...))
	default:
		// 指定渠道
		m = m.Where(rdb.ChannelID.Eq(params.ChannelId))
	}

	if params.ServerId > 0 {
		m = m.Where(rdb.ServerID.Eq(int32(params.ServerId)))
	}

	handleOutput := func(v *ReportDayTreasure, row *model.ReportDayTreasure) {
		var items []*ItemCount
		if row.Output != "" {
			if err = json.Unmarshal([]byte(row.Output), &items); err != nil {
				logrus.Warnf("Gearitems Unmarshal err :%+v", err)
				return
			}
		}

		for _, item := range items {
			ck := fmt.Sprintf("d:%v", item.ItemID)
			o, ok := v.TreasureOutput[ck]
			if ok {
				v.TreasureOutput[ck] = cast.ToInt64(o) + item.TotalCount
			} else {
				v.TreasureOutput[ck] = item.TotalCount
			}
		}
	}

	add := func(row *model.ReportDayTreasure) {
		var exist bool
		for _, v := range result {
			if v.Date == row.Date {
				exist = true
				v.PlayerCount += row.PlayerCount
				handleOutput(v, row)
			}
		}

		if exist == false {
			v := new(ReportDayTreasure)
			v.Date = row.Date
			v.ActiveCount = DayBasic.GetPlayerCount(params.ServerId, row.Date, params.ChannelId, -1, "active_count")
			v.ChannelID = row.ChannelID
			v.PlayerCount += row.PlayerCount
			v.TreasureOutput = make(TreasureOutput)

			handleOutput(v, row)
			result = append(result, v)
		}
	}

	distributes, err := m.Find()
	if err != nil {
		return
	}

	for _, distribute := range distributes {
		add(distribute)
	}

	for _, v := range result {
		v.Date = strings.ReplaceAll(v.Date, "T00:00:00+08:00", "")
	}
	return
}

type LevelTreasure struct {
	Level          int   `json:"level"`
	TotalTreasures int64 `json:"totalTreasures"`
}

func (s *sTreasure) PlayerLevel(params forms.TreasurePlayerLevelReq) (results []LevelTreasure, err error) {
	key := config.UKey("PlayerLevel", params)
	if err = config.GetCacheScan(key, &results); err != nil {
		return
	}

	if len(results) != 0 {
		return
	}

	var (
		m = config.DB.Model(&model.PlayerCache{}).
			Select("`level`, SUM(JSON_LENGTH(treasures)) AS total_treasures").
			Where("`level` BETWEEN ? AND ? AND JSON_LENGTH(treasures) > 0", params.StartLevel, params.EndLevel)
	)

	if params.ServerId > 0 {
		m = m.Where("server_id = ?", params.ServerId)
	}

	m.Group("level").Scan(&results)

	results = s.completeMissingLevels(params, results)

	if len(results) > 0 {
		config.SetCache(key, results, now.BeginningOfDay().Unix()+86400)
	}
	return
}

func (s *sTreasure) completeMissingLevels(params forms.TreasurePlayerLevelReq, results []LevelTreasure) []LevelTreasure {
	var completedResults []LevelTreasure
	levels := make(map[int]bool)

	for _, result := range results {
		levels[result.Level] = true
		completedResults = append(completedResults, result)
	}

	for i := params.StartLevel; i <= params.EndLevel; i++ {
		if !levels[int(i)] {
			completedResults = append(completedResults, LevelTreasure{Level: int(i), TotalTreasures: 0})
		}
	}

	sort.Slice(completedResults, func(i, j int) bool {
		return completedResults[i].Level < completedResults[j].Level // 根据等级从小到大排序
	})
	return completedResults
}

type TreasureRoleWear struct {
	Role      int64            `json:"role"`
	RoleName  string           `json:"roleName"`
	Treasures map[string]int64 `json:"treasures"`
}

func (s *sTreasure) RoleWear(ctx *gin.Context) (result []*TreasureRoleWear, err error) {
	key := "TreasureRoleWear"
	if err = config.GetCacheScan(key, &result); err != nil {
		return
	}

	if len(result) != 0 {
		return
	}

	var roles = make(map[int64]map[string]int64)

	handle := func(player *model.PlayerCache) (err error) {
		var treasures []*gmdata.DropedTreasure
		err = json.Unmarshal([]byte(player.Treasures), &treasures)
		if err != nil {
			return
		}

		for _, v := range treasures {
			if v.RoleID < 1 { // 如果宝物未佩戴，就没有角色
				continue
			}

			if _, ok := roles[v.RoleID]; !ok {
				roles[v.RoleID] = make(map[string]int64)
			}

			tName := gmdata.GetTreasureByTID(v.TID)
			if tName == "" {
				logrus.Warnf("无效TID:%+v", v)
				continue
			}

			if _, ok2 := roles[v.RoleID][tName]; !ok2 {
				roles[v.RoleID][tName] = 1
			} else {
				roles[v.RoleID][tName] += 1
			}

		}
		return
	}

	lastID := int64(0)

	var forErr error
	for {
		var players []*model.PlayerCache
		// Perform the query with pagination and sorting by id greater than lastID
		dao := config.DB.Model(&model.PlayerCache{}).
			Select("`id`, `treasures`").
			Where("`level` > ?  AND JSON_LENGTH(treasures) > 0", 10). // 10级以下基本不可能获得宝物，减少数据查询量做个范围过滤
			Where("id > ?", lastID)

		list := dao.Limit(1000).Order("id ASC").Scan(&players)
		if list.Error != nil {
			logrus.Errorf("RoleWear result.Error:%+v", list.Error)
			break
		}

		// If no more rows were fetched, break the loop
		if len(players) == 0 {
			break
		}

		// Process the retrieved players here as needed
		for _, player := range players {
			forErr = handle(player)
			if forErr != nil {
				return nil, forErr
			}
		}

		// Update the lastID for the next batch
		lastID = players[len(players)-1].ID
	}

	if forErr != nil {
		return nil, forErr
	}

	for role, treasures := range roles {
		roleName := gmdata.GetRoleNameByType(role)
		if roleName == "" {
			continue
		}
		result = append(result, &TreasureRoleWear{
			Role:      role,
			RoleName:  roleName,
			Treasures: treasures,
		})
	}

	sort.Slice(result, func(i, j int) bool {
		return result[i].Role < result[j].Role // 从小到大排序
	})

	if len(result) > 0 {
		config.SetCache(key, result, now.BeginningOfDay().Unix()+86400)
	}
	return
}

type LevelTreasure2 struct {
	Level     int64            `json:"level"`
	Treasures map[string]int64 `json:"treasures"`
}

func (s *sTreasure) Level(params forms.TreasureLevelReq) (results []*LevelTreasure2, err error) {
	key := fmt.Sprintf("TreasureLevel:ServerId:%v", params.ServerId)
	var allResult []*LevelTreasure2
	if err = config.GetCacheScan(key, &allResult); err != nil {
		return
	}

	if len(allResult) != 0 {
		results = s.filterLevel(params, allResult)
		return
	}

	var levels = make(map[int64]map[string]int64)

	handle := func(player *model.PlayerCache) (err error) {
		var treasures []*gmdata.DropedTreasure
		err = json.Unmarshal([]byte(player.Treasures), &treasures)
		if err != nil {
			return
		}

		for _, v := range treasures {
			tName := gmdata.GetTreasureByTID(v.TID)
			if tName == "" {
				logrus.Warnf("Level 无效TID:%+v", v)
				continue
			}

			level := gmdata.GetTreasureLevel(v.TID)
			if level < 1 {
				logrus.Warnf("Level 无效TID2:%+v", v)
				continue
			}

			_, ok := levels[level]
			if !ok {
				levels[level] = make(map[string]int64)
			}

			if _, ok2 := levels[level][tName]; !ok2 {
				levels[level][tName] = 1
			} else {
				levels[level][tName] += 1
			}

		}
		return
	}

	lastID := int64(0)

	var forErr error
	for {
		var players []*model.PlayerCache
		// Perform the query with pagination and sorting by id greater than lastID
		dao := config.DB.Model(&model.PlayerCache{}).
			Select("`id`, `treasures`").
			Where("`level` > ?  AND JSON_LENGTH(treasures) > 0", 10). // 10级以下基本不可能获得宝物，减少数据查询量做个范围过滤
			Where("id > ?", lastID)

		if params.ServerId > 0 {
			dao = dao.Where("server_id = ?", params.ServerId)
		}

		list := dao.Limit(1000).Order("id ASC").Scan(&players)
		if list.Error != nil {
			logrus.Errorf("RoleWear result.Error:%+v", list.Error)
			break
		}

		// If no more rows were fetched, break the loop
		if len(players) == 0 {
			break
		}

		// Process the retrieved players here as needed
		for _, player := range players {
			forErr = handle(player)
			if forErr != nil {
				return nil, forErr
			}
		}

		// Update the lastID for the next batch
		lastID = players[len(players)-1].ID
	}

	if forErr != nil {
		return nil, forErr
	}

	for i := 1; i <= 200; i++ {
		ni := int64(i)
		_, ok := levels[ni]

		// 等级不存在就进行补全
		if !ok {
			levels[ni] = make(map[string]int64)
			for _, t := range gmdata.Treasures {
				levels[ni][t.Name] = 0
			}
			continue
		}

		// 补全宝物
		for _, t := range gmdata.Treasures {
			_, ok2 := levels[ni][t.Name]
			if !ok2 {
				levels[ni][t.Name] = 0
			}
		}
	}

	for level, treasures := range levels {
		allResult = append(allResult, &LevelTreasure2{
			Level:     level,
			Treasures: treasures,
		})
	}

	sort.Slice(allResult, func(i, j int) bool {
		return allResult[i].Level < allResult[j].Level // 从小到大排序
	})

	if len(allResult) > 0 {
		config.SetCache(key, allResult, now.BeginningOfDay().Unix()+86400)
	}

	results = s.filterLevel(params, allResult)
	return
}

// filterLevel 过滤等级
func (s *sTreasure) filterLevel(params forms.TreasureLevelReq, allResult []*LevelTreasure2) (result []*LevelTreasure2) {
	for _, v := range allResult {
		if v.Level >= params.StartLevel && v.Level <= params.EndLevel {
			result = append(result, v)
		}
	}

	sort.Slice(result, func(i, j int) bool {
		return result[i].Level < result[j].Level // 从小到大排序
	})
	return
}

type StarTreasure struct {
	Star      int64            `json:"star"`
	Treasures map[string]int64 `json:"treasures"`
}

func (s *sTreasure) Star(params forms.TreasureStarReq) (results []*StarTreasure, err error) {
	key := fmt.Sprintf("TreasureStar:ServerId:%v", params.ServerId)
	if err = config.GetCacheScan(key, &results); err != nil {
		return
	}

	if len(results) != 0 {
		return
	}

	var stars = make(map[int64]map[string]int64)

	handle := func(player *model.PlayerCache) (err error) {
		var treasures []*gmdata.DropedTreasure
		err = json.Unmarshal([]byte(player.Treasures), &treasures)
		if err != nil {
			return
		}

		for _, v := range treasures {
			tName := gmdata.GetTreasureByTID(v.TID)
			if tName == "" {
				logrus.Warnf("Star 无效TID:%+v", v)
				continue
			}

			star := gmdata.GetTreasureStar(v.TID)
			if star < 0 {
				logrus.Warnf("Star 无效TID2:%+v", v)
				continue
			}

			_, ok := stars[star]
			if !ok {
				stars[star] = make(map[string]int64)
			}

			if _, ok2 := stars[star][tName]; !ok2 {
				stars[star][tName] = 1
			} else {
				stars[star][tName] += 1
			}

		}
		return
	}

	lastID := int64(0)

	var forErr error
	for {
		var players []*model.PlayerCache
		// Perform the query with pagination and sorting by id greater than lastID
		dao := config.DB.Model(&model.PlayerCache{}).
			Select("`id`, `treasures`").
			Where("`level` > ?  AND JSON_LENGTH(treasures) > 0", 10). // 10级以下基本不可能获得宝物，减少数据查询量做个范围过滤
			Where("id > ?", lastID)

		if params.ServerId > 0 {
			dao = dao.Where("server_id = ?", params.ServerId)
		}

		list := dao.Limit(1000).Order("id ASC").Scan(&players)
		if list.Error != nil {
			logrus.Errorf("RoleWear result.Error:%+v", list.Error)
			break
		}

		// If no more rows were fetched, break the loop
		if len(players) == 0 {
			break
		}

		// Process the retrieved players here as needed
		for _, player := range players {
			forErr = handle(player)
			if forErr != nil {
				return nil, forErr
			}
		}

		// Update the lastID for the next batch
		lastID = players[len(players)-1].ID
	}

	if forErr != nil {
		return nil, forErr
	}

	for i := 1; i <= 20; i++ {
		ni := int64(i)
		_, ok := stars[ni]

		// 等级不存在就进行补全
		if !ok {
			stars[ni] = make(map[string]int64)
			for _, t := range gmdata.Treasures {
				stars[ni][t.Name] = 0
			}
			continue
		}

		// 补全宝物
		for _, t := range gmdata.Treasures {
			_, ok2 := stars[ni][t.Name]
			if !ok2 {
				stars[ni][t.Name] = 0
			}
		}
	}

	for star, treasures := range stars {
		results = append(results, &StarTreasure{
			Star:      star,
			Treasures: treasures,
		})
	}

	sort.Slice(results, func(i, j int) bool {
		return results[i].Star < results[j].Star // 从小到大排序
	})

	if len(results) > 0 {
		config.SetCache(key, results, now.BeginningOfDay().Unix()+86400)
	}
	return
}
