package service

import (
	"context"
	"encoding/json"
	"entrance-grpc/iam"
	"fmt"
	"gadmin/config"
	"gadmin/internal/admin/consts"
	"gadmin/internal/admin/forms"
	"gadmin/internal/admin/gm_rpc/rpc_share"
	"gadmin/internal/gorm/model"
	"gadmin/internal/gorm/query"
	"gadmin/package/gmdata"
	"gadmin/utility"
	"gadmin/utility/character"
	"gadmin/utility/player"
	"gadmin/utility/serializer"
	"gadmin/utility/token"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cast"
	"github.com/xuri/excelize/v2"
	model2 "leafstalk/covenant/model"
	msg2 "leafstalk/covenant/msg"
	"strconv"
	"strings"
	"time"
)

// AdminEmail 邮件
var AdminEmail = new(sAdminEmail)

type sAdminEmail struct{}

type Ret struct {
	model.AdminEmail
	Sender string `json:"sender"`
}

type Ext struct {
	Id    string `json:"id"`
	Count string `json:"count"`
}

func (s *sAdminEmail) List(ctx *gin.Context, req forms.AdminEmailListReq) serializer.Response {

	var (
		q = query.Use(config.DB).AdminEmail
		m = q.WithContext(ctx)
		//u = query.Use(config.DB).AdminUser

		offset int64 = 0
		models forms.UserAccountListRes
		lists  []*Ret
	)
	req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage)
	models.Page = req.Page
	models.PerPage = req.PerPage

	if req.Sender != "" {
		first, err := config.GetIamClient().GetAdminUserByNickName(ctx, &iam.GetAdminUserByNickNameReq{
			NickName: req.Sender,
		})
		if err == nil && first.Code == 0 {
			m = m.Where(q.OperatorID.Eq(first.Data.ID))
		}
	}

	if req.OperatorId > 0 {
		m = m.Where(q.OperatorID.Eq(req.OperatorId))
	}
	if len(req.CreatedAt) == 2 {
		m = m.Where(q.CreatedAt.Between(req.CreatedAt[0], req.CreatedAt[1]))
	}

	m = m.Order(q.ID.Desc())

	count, err := m.Count()
	if err != nil {
		return serializer.Err(consts.CodeParamErr, "查询出错 count", err)
	}

	if count == 0 {
		return serializer.Suc(models)
	}

	if req.IsExport == 1 {
		if err = m.Scan(&lists); err != nil {
			return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
		}
		err = s.Export(ctx, lists)
		if err != nil {
			return serializer.Err(consts.CodeParamErr, "导出错误", err)
		}
		return serializer.Response{}
	}

	if err = m.Limit(int(req.PerPage)).Offset(int(offset)).Scan(&lists); err != nil {
		return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
	}

	uIdMap := make(map[int64]struct{})
	for i, v := range lists {
		if v.OperatorID > 0 {
			uIdMap[v.OperatorID] = struct{}{}
		}
		lists[i].PlayerIds = strings.Replace(v.PlayerIds, "[", "", -1)
		lists[i].PlayerIds = strings.Replace(v.PlayerIds, "]", "", -1)
		lists[i].PlayerIds = strings.Replace(v.PlayerIds, "\"", "", -1)
	}
	uIds := make([]int64, 0, len(lists))
	for uid := range uIdMap {
		uIds = append(uIds, uid)
	}
	res, err := config.GetIamClient().BatchGetAdminUser(context.Background(), &iam.BatchGetAdminUserReq{UIds: uIds})
	if err != nil || res.Code != 0 {
		return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
	}

	uMap := make(map[int64]*iam.AdminUserInfo)
	for _, usr := range res.Data {
		uMap[usr.ID] = usr
	}
	for _, it := range lists {
		if _, ok := uMap[it.OperatorID]; !ok {
			continue
		}
		it.Sender = uMap[it.OperatorID].NickName
	}

	models.List = lists
	models.PageCount = (count + req.PerPage - 1) / req.PerPage
	return serializer.Suc(models)
}

func (s *sAdminEmail) Verify(ctx *gin.Context, req forms.AdminEmailVerifyReq) serializer.Response {
	var q = query.Use(config.DB).AdminEmail
	first, err := q.WithContext(ctx).Where(q.ID.Eq(req.Id)).First()
	if err != nil {
		return serializer.ParamErr(err.Error(), err)
	}

	if first == nil {
		return serializer.ParamErr("邮件不存在", nil)
	}

	if first.Status != consts.EmailStatusVerify {
		return serializer.ParamErr("当前邮件状态非审核状态，操作失败！", nil)
	}

	// 拒绝发送
	if req.VerifyStatus == 2 {
		_, err2 := q.WithContext(ctx).Where(q.ID.Eq(first.ID)).Updates(model.AdminEmail{
			Status: consts.EmailStatusCanceled,
		})
		if err2 != nil {
			return serializer.ParamErr(err.Error(), err)
		}
		return serializer.Suc(nil)
	}

	// 同意发送，进入发送环节
	var status int32

	// 立即发送，进入发送中状态
	if first.SendType == 1 {
		status = consts.EmailStatusIn
	}

	// 延迟发送
	if first.SendType == 2 {
		status = consts.EmailStatusWait
	}

	// 先更新数据
	if _, err = q.WithContext(ctx).Where(q.ID.Eq(first.ID)).Updates(model.AdminEmail{Status: status}); err != nil {
		return serializer.ParamErr(err.Error(), err)
	}

	// 立即发送
	if status == consts.EmailStatusIn {
		q := query.Use(config.DB).AdminEmail
		if err = s.SendEmailToWorld(first); err != nil {
			logrus.Warnf("邮件发送失败-3：%+v", err)
			_, err2 := q.WithContext(ctx).Where(q.ID.Eq(first.ID)).Updates(model.AdminEmail{Status: consts.EmailStatusErr})
			if err2 != nil {
				logrus.Warnf("邮件更新失败：%+v", err2)
			}
			return serializer.ParamErr(err.Error(), err)
		}

		// 发送成功
		_, err2 := q.WithContext(ctx).Where(q.ID.Eq(first.ID)).Updates(model.AdminEmail{
			Status: consts.EmailStatusSent,
			SendAt: time.Now().Unix(),
		})
		if err2 != nil {
			logrus.Warnf("邮件更新失败-2：%+v", err2)
		}
	}
	return serializer.Suc(nil)
}

func (s *sAdminEmail) Add(ctx *gin.Context, req forms.AdminEmailAddReq) serializer.Response {
	ext, err := json.Marshal(req.Ext)
	if err != nil {
		return serializer.ParamErr(err.Error(), err)
	}

	serverIds, err := json.Marshal(req.ServerIds)
	if err != nil {
		return serializer.ParamErr(err.Error(), err)
	}

	playerIds, err := json.Marshal(req.PlayerIds)
	if err != nil {
		return serializer.ParamErr(err.Error(), err)
	}

	var status int32

	// 立即发送，进入发送中状态
	if req.SendType == 1 {
		status = consts.EmailStatusIn
		req.SendAt = time.Now().Unix()
	}

	// 延迟发送
	if req.SendType == 2 {
		status = consts.EmailStatusWait
	}

	// 非超管发送全服或指定服邮件直接进入待审核状态
	if ctx.GetInt64("admin_role_id") != 1 {
		status = consts.EmailStatusVerify
	}

	logrus.Warnf("AdminEmailAdd req:%+v", utility.DumpToJSON(req))
	expired := 7
	if len(req.Ext) > 0 {
		expired = 30
	}
	data := &model.AdminEmail{
		ID:          0,
		Title:       req.Title,
		Content:     req.Content,
		Ext:         string(ext),
		SendType:    req.SendType,
		SendAt:      req.SendAt,
		Expired:     int32(expired),
		ReceiptType: req.ReceiptType,
		ServerIds:   string(serverIds),
		PlayerIds:   string(playerIds),
		OperatorID:  token.GetUID(ctx),
		Status:      status,
		UpdatedAt:   time.Now().Unix(),
		CreatedAt:   time.Now().Unix(),
		Remark:      req.Remark,
	}
	logrus.Warnf("AdminEmailAdd data:%+v", utility.DumpToJSON(data))
	if err = query.Use(config.DB).AdminEmail.WithContext(ctx).Create(data); err != nil {
		return serializer.ParamErr(err.Error(), err)
	}

	logrus.Infof("新增邮件内容：%+v", utility.DumpToJSON(data))

	// 立即发送
	if status == consts.EmailStatusIn {
		q := query.Use(config.DB).AdminEmail
		if err = s.SendEmailToWorld(data); err != nil {
			logrus.Warnf("邮件发送失败：%+v", err)
			_, err2 := q.WithContext(ctx).Where(q.ID.Eq(data.ID)).Updates(model.AdminEmail{Status: consts.EmailStatusErr})
			if err2 != nil {
				logrus.Warnf("邮件更新失败：%+v", err2)
			}
			return serializer.ParamErr(err.Error(), err)
		}

		// 发送成功
		_, err2 := q.WithContext(ctx).Where(q.ID.Eq(data.ID)).Updates(model.AdminEmail{
			Status: consts.EmailStatusSent,
			SendAt: time.Now().Unix(),
		})
		if err2 != nil {
			logrus.Warnf("邮件更新失败-2：%+v", err2)
		}
	}
	return serializer.Suc(nil)
}

// DecodeExtData 解析奖励道具
func (s *sAdminEmail) DecodeExtData(es string) (data []*model2.DropedItem, err error) {
	type Extra struct {
		Id   string `json:"id"`
		Num  string `json:"num"`
		Type int64  `json:"type"`
	}
	var (
		extra []*Extra
	)

	if err = json.Unmarshal([]byte(es), &extra); err != nil {
		return []*model2.DropedItem{}, err
	}
	for _, it := range extra {
		data = append(data, &model2.DropedItem{
			Id:   cast.ToInt64(it.Id),
			Num:  cast.ToInt64(it.Num),
			Type: it.Type,
		})
	}
	return
}

// SendEmailToWorld 发送邮件到游戏服务器
func (s *sAdminEmail) SendEmailToWorld(params *model.AdminEmail) (err error) {
	letter := &model2.MailGlobal{
		MailType:    1,
		MaxPlayerId: 0,
		Title:       params.Title,
		Content:     params.Content,
		ExtraInfo:   nil,
		CreateTime:  time.Unix(params.SendAt, 0),
		ExpireTime:  time.Unix(params.SendAt, 0).AddDate(0, 0, int(params.Expired)),
	}

	letter.Extra, err = s.DecodeExtData(params.Ext)
	if err != nil {
		return err
	}

	switch params.ReceiptType {
	case 1: // 全服
		msg := msg2.GMLetter{
			MsgId:      character.GenerateMsgId(),
			Letter:     letter,
			Players:    []int64{},
			OperatorId: params.OperatorID,
		}

		rpc_share.MsgMap[msg.MsgId] = fmt.Sprintf("%s,GM邮件(编号 %d) 投递成功,全服投递",
			utility.FormatSecond(time.Now()),
			params.ID)

		for _, serverId := range ServerOption.GetServerIds() {
			DB, err := player.GetDBByServerID(serverId)
			if err != nil {
				logrus.Warnf("sendLetterToWorld GetDBByServerID err:%+v", err)
				continue
			}

			var resp *msg2.ResponseGMLetter
			res, err := config.GmNats.GmRequest(DB, "GMLetter", msg)
			if err != nil {
				logrus.Warnf("sendLetterToWorld GmRequest err:%+v", err)
				continue
			}

			if err = json.Unmarshal(res, &resp); err != nil {
				logrus.Warnf("sendLetterToWorld nats Unmarshal err:%+v", err)
				continue
			}

			rpc_share.LogChan <- rpc_share.LogMsg{
				MsgID: msg.MsgId,
				Data:  resp,
			}
		}
	case 2: // 指定服
		var serverIds []int32
		if err = json.Unmarshal([]byte(params.ServerIds), &serverIds); err != nil {
			return err
		}

		for _, serverId := range serverIds {
			sId := cast.ToInt(serverId)
			if sId > 0 {
				DB, err := player.GetDBByServerID(sId)
				if err != nil {
					logrus.Warnf("sendLetterToWorld2 GetDBByServerID2 err:%+v", err)
					continue
				}

				msg := msg2.GMLetter{
					MsgId:      character.GenerateMsgId(),
					Letter:     letter,
					Players:    []int64{},
					OperatorId: params.OperatorID,
				}

				rpc_share.MsgMap[msg.MsgId] = fmt.Sprintf("%s,GM邮件(编号 %d) 投递成功,投递服务器ID:%v",
					utility.FormatSecond(time.Now()), params.ID, sId)

				var resp *msg2.ResponseGMLetter
				res, err := config.GmNats.GmRequest(DB, "GMLetter", msg)
				if err != nil {
					logrus.Warnf("sendLetterToWorld2 GmRequest err:%+v", err)
					continue
				}

				if err = json.Unmarshal(res, &resp); err != nil {
					logrus.Warnf("sendLetterToWorld2 nats Unmarshal err:%+v", err)
					continue
				}

				rpc_share.LogChan <- rpc_share.LogMsg{
					MsgID: msg.MsgId,
					Data:  resp,
				}
			}
		}
	case 3: // 指定玩家
		var playerIds []string
		if err = json.Unmarshal([]byte(params.PlayerIds), &playerIds); err != nil {
			return err
		}
		lineMap := make(map[int][]int64)
		errPlayerIds := make([]int64, 0)
		for _, v := range playerIds {
			playerID := cast.ToInt64(v)
			if playerID <= 0 {
				errPlayerIds = append(errPlayerIds, playerID)
				continue
			}
			db, err := player.GetDBByUserId(playerID)
			if err != nil {
				errPlayerIds = append(errPlayerIds, playerID)
				logrus.Warnf("sendLetterToWorld3 GetDBByUserId err:%+v", err)
				continue
			}
			if _, ok := lineMap[db]; ok {
				lineMap[db] = append(lineMap[db], playerID)
			} else {
				lineMap[db] = []int64{playerID}
			}
		}
		if len(errPlayerIds) > 0 {
			logrus.Warnf("sendLetterToWorld3 errPlayerIds:%v", errPlayerIds)
		}
		for line, ids := range lineMap {
			msg := msg2.GMLetter{
				MsgId:      character.GenerateMsgId(),
				Letter:     letter,
				Players:    ids,
				OperatorId: params.OperatorID,
			}

			rpc_share.MsgMap[msg.MsgId] = fmt.Sprintf("%s,GM邮件(编号 %d) 投递成功,投递玩家ID:%v",
				utility.FormatSecond(time.Now()), params.ID, ids)

			var resp *msg2.ResponseGMLetter
			res, err := config.GmNats.GmRequest(line, "GMLetter", msg)
			if err != nil {
				logrus.Warnf("sendLetterToWorld3 GmRequest err:%+v", err)
				continue
			}

			if err = json.Unmarshal(res, &resp); err != nil {
				logrus.Warnf("sendLetterToWorld3 nats Unmarshal err:%+v", err)
				continue
			}

			rpc_share.LogChan <- rpc_share.LogMsg{
				MsgID: msg.MsgId,
				Data:  resp,
			}
		}
	}
	return
}

func (s *sAdminEmail) Export(ctx *gin.Context, list []*Ret) error {
	f := excelize.NewFile()
	f.SetColWidth("Sheet1", "A", "A", 15)
	f.SetColWidth("Sheet1", "B", "B", 15)
	f.SetColWidth("Sheet1", "C", "C", 15)
	f.SetColWidth("Sheet1", "D", "D", 20)
	f.SetColWidth("Sheet1", "E", "E", 25)
	f.SetColWidth("Sheet1", "F", "F", 30)
	f.SetColWidth("Sheet1", "G", "G", 30)
	f.SetColWidth("Sheet1", "H", "H", 15)
	f.SetColWidth("Sheet1", "I", "I", 15)
	f.SetColWidth("Sheet1", "J", "J", 15)
	f.SetColWidth("Sheet1", "K", "K", 15)

	// 创建一个工作表
	f.SetCellValue("Sheet1", "A1", "序号")
	f.SetCellValue("Sheet1", "B1", "标题")
	f.SetCellValue("Sheet1", "C1", "收件人")
	f.SetCellValue("Sheet1", "D1", "有效期")
	f.SetCellValue("Sheet1", "E1", "邮件内容")
	f.SetCellValue("Sheet1", "F1", "邮件备注")
	f.SetCellValue("Sheet1", "G1", "发送道具")
	f.SetCellValue("Sheet1", "H1", "预计发送时间")
	f.SetCellValue("Sheet1", "I1", "发送状态")
	f.SetCellValue("Sheet1", "J1", "创建人")
	f.SetCellValue("Sheet1", "K1", "创建时间")
	//获取道具和装备信息
	materials := gmdata.MaterialsDict  //道具
	equipments := gmdata.EquipmentDict //装备
	materialMap := make(map[int64]gmdata.Material)
	equipmentMap := make(map[int64]gmdata.Equipment)
	for _, item := range materials {
		materialMap[int64(item.ID)] = item
	}
	for _, item := range equipments {
		equipmentMap[item.LevelId] = item
	}
	data := make([]Ext, 0)
	sendStatus := map[int32]string{1: "已发送", 2: "等待发送", 3: "待审核", 4: "已取消", 5: "发送中", 6: "发送出错"}

	uIds := make([]int64, len(list))
	uMap := make(map[int64]*model.AdminUser)
	for i, v := range list {
		if v.OperatorID > 0 {
			uIds[i] = v.OperatorID
		}
	}
	u := query.Use(config.DB).AdminUser
	usrs, err := u.WithContext(ctx).Where(u.ID.In(uIds...)).Find()
	if err != nil {
		return err
	}
	for _, usr := range usrs {
		uMap[usr.ID] = usr
	}
	type props struct {
		Name  string `json:"name"`
		Count string `json:"count"`
	}

	for i, v := range list {
		f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), v.ID)
		f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), v.Title)

		if v.ReceiptType == 1 {
			f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), "全服")
		} else if v.ReceiptType == 2 {
			f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), fmt.Sprintf("指定服%s", v.ServerIds))
		} else if v.ReceiptType == 3 {
			f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), fmt.Sprintf("指定玩家%s", v.PlayerIds))
		}

		f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), fmt.Sprintf("%d天", v.Expired))
		f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), v.Content)
		f.SetCellValue("Sheet1", fmt.Sprintf("F%d", i+2), v.Remark)
		//装备或者道具
		err := json.Unmarshal([]byte(v.Ext), &data)
		if err != nil {
			return err
		}

		propsArr := make([]props, 0)
		propsStr := ""
		for _, item := range data {
			//装备id大于10000000
			id, _ := strconv.ParseInt(item.Id, 10, 64)
			if id > 1000000 {
				if equipment, ok := equipmentMap[id]; ok {
					propsArr = append(propsArr, props{Name: equipment.Name, Count: item.Count})
				}
			} else if id > 0 && id < 1000000 {
				if material, ok := materialMap[id]; ok {
					propsArr = append(propsArr, props{
						Name:  material.Name,
						Count: item.Count,
					})
				}
			}
			marshal, err := json.Marshal(propsArr)
			if err != nil {
				return err
			}
			propsStr = string(marshal)
		}

		f.SetCellValue("Sheet1", fmt.Sprintf("G%d", i+2), propsStr)
		if v.SendAt < 1 {
			f.SetCellValue("Sheet1", fmt.Sprintf("H%d", i+2), "-")
		} else {
			f.SetCellValue("Sheet1", fmt.Sprintf("H%d", i+2), utility.FormatSecond(time.Unix(v.SendAt, 0)))
		}
		f.SetCellValue("Sheet1", fmt.Sprintf("I%d", i+2), sendStatus[v.Status])
		f.SetCellValue("Sheet1", fmt.Sprintf("J%d", i+2), uMap[v.OperatorID].Nickname)
		f.SetCellValue("Sheet1", fmt.Sprintf("K%d", i+2), utility.FormatSecond(time.Unix(v.CreatedAt, 0)))
	}

	// 设置工作簿的默认工作表
	f.SetActiveSheet(1)
	ctx.Header("Content-Type", "application/vnd.ms-excel")
	ctx.Header("Content-Disposition", fmt.Sprintf("attachment;filename=订单记录导出%s.xlsx", time.Now().Format("20060102150405")))
	f.WriteTo(ctx.Writer)
	return nil
}
