package service

import (
	"encoding/json"
	"fmt"
	"gadmin/config"
	"gadmin/internal/admin/consts"
	"gadmin/internal/admin/data"
	"gadmin/internal/admin/forms"
	"gadmin/internal/elastic/eapi"
	"gadmin/internal/gorm/model"
	"gadmin/internal/gorm/query"
	"gadmin/utility"
	"gadmin/utility/serializer"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"os"
	"sort"
)

var ChangedLogs = new(sChangedLogs)

type sChangedLogs struct{}

func (s *sChangedLogs) ItemRanking(ctx *gin.Context, req forms.ItemRankingReq) serializer.Response {
	type mods struct {
		ID       int64  `json:"id"`
		UserId   int64  `json:"user_id"`
		T        int64  `json:"total"`
		NickName string `json:"nickName"`
		Source   int64  `json:"source"`
	}

	var (
		where eapi.M
		index = fmt.Sprintf("%s%s", os.Getenv("ELASTIC_TOPIC"), "changed_logs_*")
		lists []*mods
	)

	must := []eapi.M{
		{
			"term": eapi.M{
				"source": req.Source,
			},
		},
		{
			"range": eapi.M{
				"times": eapi.M{
					"gt": 0,
				},
			},
		},
	}

	if req.ServerId > 0 {
		must = append(must, eapi.M{
			"term": eapi.M{
				"server_id": req.ServerId,
			},
		})
	}

	if len(req.Createtime) == 2 {
		must = append(must, eapi.M{
			"range": eapi.M{
				"time": eapi.M{
					"gte": req.Createtime[0],
					"lte": req.Createtime[1],
				},
			}})
	}

	where = eapi.M{
		"query": eapi.M{
			"bool": eapi.M{
				"must": must,
			},
		},
	}

	logrus.Warnf("where:%+v", utility.DumpToJSON(where))
	topList, err := eapi.TopSumScore(ctx, index, where, "user_id", "times", 100)
	if err != nil {
		return serializer.Err(consts.CodeParamErr, "查询出错 TopList", err)
	}

	var (
		models forms.ListRes
	)

	for i := 0; i < len(topList); i++ {
		v := new(mods)
		v.ID = int64(i + 1)
		v.UserId = topList[i].Key
		v.T = int64(topList[i].SumScore.Value)
		v.Source = req.Source
		user := data.GetUser(v.UserId)
		if user != nil {
			v.NickName = user.NickName
		}

		lists = append(lists, v)
	}

	models.List = lists
	models.Page = req.Page
	models.PerPage = req.PerPage
	models.PageCount = 0
	return serializer.Suc(models)
}

func (s *sChangedLogs) DiamondRanking(ctx *gin.Context, req forms.DiamondRankingReq) serializer.Response {
	type Result struct {
		ID             int64   `json:"id"`
		UserId         int64   `json:"user_id"`
		T              int64   `json:"total"`
		NickName       string  `json:"nickName"`
		RechargeAmount float64 `json:"rechargeAmount"`
	}
	type Counter struct {
		Total float64 `json:"total"`
	}
	var (
		models forms.ListRes
		field        = "expend_diamond"
		offset int64 = 0
		result []Result
		count  int64 = 0
	)

	if req.Type == 2 {
		field = "expend_coin"
	}

	req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage)

	countSql := fmt.Sprintf("SELECT sum(`%v`) as t FROM `changed_player` WHERE `%v` < 0", field, field)

	if req.ServerId > 0 {
		countSql = fmt.Sprintf(countSql+" and server_id = %v", req.ServerId)
	}
	countSql = countSql + "  GROUP BY `playerid` ORDER BY t ASC"
	config.DB.Raw(countSql).Count(&count)

	if count > 0 {
		listSql := fmt.Sprintf("SELECT `playerid` as user_id,sum(`%v`) as t FROM `changed_player` WHERE `%v` < 0", field, field)

		if req.ServerId > 0 {
			listSql = fmt.Sprintf(listSql+" and server_id = %v", req.ServerId)
		}
		listSql = fmt.Sprintf("%v GROUP BY `playerid` ORDER BY t ASC LIMIT %d OFFSET %d", listSql, req.PerPage, offset)
		config.DB.Raw(listSql).Find(&result)
		for i := 0; i < len(result); i++ {
			result[i].ID = offset + int64(i) + 1
			result[i].T = result[i].T * -1
			user := data.GetUser(result[i].UserId)
			if user != nil {
				result[i].NickName = user.NickName
			}

			var (
				orderQuery            = query.Use(config.DB).Order
				rechargeAmountCounter Counter
			)

			err := orderQuery.WithContext(ctx).Select(orderQuery.Money.Sum().As("total")).
				Where(orderQuery.PlayerID.Eq(result[i].UserId), orderQuery.Status.Eq(consts.OrderStateSuccess)).
				Scan(&rechargeAmountCounter)
			if err != nil {
				return serializer.Err(
					consts.CodeParamErr,
					"查询出错 DiamondRanking RechargeAmount count",
					err,
				)
			}
			result[i].RechargeAmount = rechargeAmountCounter.Total
		}
	}

	models.List = result
	models.Page = req.Page
	models.PerPage = req.PerPage
	models.PageCount = (count + req.PerPage - 1) / req.PerPage

	return serializer.Suc(models)

}

func (s *sChangedLogs) unionModel(ctx *gin.Context, where string) (unionALL string, err error) {
	type Table struct {
		Value string
	}

	dbName := config.GetDBName(os.Getenv("MYSQL_DSN"))
	var sql = "SELECT TABLE_NAME as `value` FROM information_schema.`TABLES` WHERE TABLE_SCHEMA ='" + dbName + "' AND TABLE_NAME LIKE 'changed_logs%'"
	tx := config.DB.Raw(sql)
	var tables []string
	if err = tx.Scan(&tables).Error; err != nil {
		return
	}

	logrus.Warnf("tables:%+v", tables)
	for k, table := range tables {
		if k == 0 {
			unionALL = fmt.Sprintf("(SELECT * FROM %s %s)", table, where)
			continue
		}
		unionALL += fmt.Sprintf(" UNION ALL (SELECT * FROM %s %s)", table, where)
	}

	return unionALL, nil
}

func (s *sChangedLogs) List(ctx *gin.Context, req forms.ChangedLogsListReq) serializer.Response {
	type ChangedLog struct {
		model.ChangedLog
		NickName string `json:"nickName"`
	}
	var (
		//c                 = query.Use(config.DB).PlayerChannel
		//q                 = query.Use(config.DB).ChangedLog
		//m                 = q.WithContext(ctx)
		offset      int64 = 0
		models      forms.UserAccountListRes
		lists       []ChangedLog
		hasWhere    bool
		where       string
		count       int64
		masterWhere string
	)

	if req.ServerId > 0 {
		if hasWhere {
			where += fmt.Sprintf(" and server_id = %v", req.ServerId)
		} else {
			hasWhere = true
			where += fmt.Sprintf(" where server_id = %v", req.ServerId)
		}
	}

	if len(req.Createtime) == 2 {
		if hasWhere {
			where += fmt.Sprintf(" and `time` BETWEEN %v AND %v", int32(req.Createtime[0]), int32(req.Createtime[1]))
		} else {
			hasWhere = true
			where += fmt.Sprintf(" where `time` BETWEEN %v AND %v", int32(req.Createtime[0]), int32(req.Createtime[1]))
		}
	}

	if req.Source > 0 {
		if hasWhere {
			where += fmt.Sprintf(" and source = %v", req.Source)
		} else {
			hasWhere = true
			where += fmt.Sprintf(" where source = %v", req.Source)
		}
	}

	if req.UserId > 0 {
		if hasWhere {
			where += fmt.Sprintf(" and user_id = %v", req.UserId)
		} else {
			hasWhere = true
			where += fmt.Sprintf(" where user_id = %v", req.UserId)
		}
	}

	switch req.ChannelId {
	case consts.ChannelIdNone:
		// 不选择渠道
	case consts.ChannelIdAllAdv, consts.ChannelIdAllWx, consts.ChannelIdAllTT:
		var cls = Channel.GetIdsByTypeToString(req.ChannelId)
		if hasWhere {
			where += fmt.Sprintf(" and channel_id in (%v)", cls)
		} else {
			hasWhere = true
			where += fmt.Sprintf(" where channel_id in (%v)", cls)
		}
	default:
		// 指定渠道
		if hasWhere {
			where += fmt.Sprintf(" and channel_id = %v", req.ChannelId)
		} else {
			hasWhere = true
			where += fmt.Sprintf(" where channel_id = %v", req.ChannelId)
		}
	}

	unionALL, err := s.unionModel(ctx, where)
	if err != nil {
		return serializer.Err(consts.CodeParamErr, "查询出错 unionModel", err)
	}

	countSql := fmt.Sprintf("SELECT count(1) FROM(%s) as a", unionALL)
	getSql := fmt.Sprintf("SELECT * FROM(%s) as a", unionALL)

	// 排序
	masterWhere += " ORDER BY time DESC"

	// 分页
	req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage)

	config.DB.Raw(countSql + masterWhere).Scan(&count)
	if count > 0 {
		masterWhere += fmt.Sprintf(" LIMIT %v", req.PerPage)
		if offset > 0 {
			masterWhere += fmt.Sprintf(" OFFSET %v", offset)
		}
		config.DB.Raw(getSql + masterWhere).Scan(&lists)
	}

	for k, v := range lists {
		user := data.GetUser(v.UserID)
		if user != nil {
			lists[k].NickName = user.NickName
		}
	}

	models.List = lists
	models.Page = req.Page
	models.PerPage = req.PerPage
	models.PageCount = (count + req.PerPage - 1) / req.PerPage

	return serializer.Suc(models)
}

type StatisticsResult struct {
	Type      int32   `json:"type"`
	Source    int32   `json:"source"`
	UserCount int     `json:"user_count"`
	Counts    int64   `json:"counts"`
	Amount    int64   `json:"amount"`
	Players   []int64 `json:"-"`
}

type StatisticsResults []*StatisticsResult

func (s StatisticsResults) Len() int {
	return len(s)
}
func (s StatisticsResults) Less(i, j int) bool {
	return s[i].Amount < s[j].Amount
}
func (s StatisticsResults) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Statistics 消费统计
func (s *sChangedLogs) Statistics(ctx *gin.Context, req forms.ConsumptionStatisticsReq) serializer.Response {
	var (
		q       = query.Use(config.DB).ChangedStatistic
		m       = q.WithContext(ctx)
		models  forms.UserAccountListRes
		lists   []*model.ChangedStatistic
		results StatisticsResults
	)

	if req.Type == 1 {
		// 钻石
		m = m.Where(q.Type.Eq(1))
	} else {
		// 金币
		m = m.Where(q.Type.Eq(2))
	}

	if req.ServerId > 0 {
		m = m.Where(q.ServerID.Eq(req.ServerId))
	}

	if req.Source > 0 {
		m = m.Where(q.Source.Eq(req.Source))
	}

	if len(req.Date) == 2 {
		m = m.Where(q.Date.Between(req.Date[0], req.Date[1]))
	}

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

	if err := m.Scan(&lists); err != nil {
		return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
	}

	for _, v := range lists {
		var (
			exist   bool
			players []int64
		)

		for _, vv := range results {
			if vv.Source == v.Source {
				exist = true
				vv.Amount = vv.Amount + v.Amount
				vv.Counts = vv.Counts + v.Counts
				if err := json.Unmarshal([]byte(v.Players), &players); err != nil {
					logrus.Warnf("json.Unmarshal err:%+v, v:%+v", err, v)
					return serializer.Err(consts.CodeParamErr, "json.Unmarshal err", err)
				}
				vv.Players = append(vv.Players, players...)
			}
		}

		if exist == false {
			if err := json.Unmarshal([]byte(v.Players), &players); err != nil {
				logrus.Warnf("json.Unmarshal2 err:%+v, v:%+v", err, v)
				return serializer.Err(consts.CodeParamErr, "json.Unmarshal2 err", err)
			}
			results = append(results, &StatisticsResult{
				Type:    v.Type,
				Source:  v.Source,
				Amount:  v.Amount,
				Counts:  v.Counts,
				Players: players,
			})
		}

	}

	for _, v := range results {
		v.UserCount = len(utility.UniqueInt64s(v.Players))
	}

	sort.Sort(results)
	models.List = results
	return serializer.Suc(models)
}
