package qznn

import (
	"errors"
	"fmt"
	"math/rand"
	"runtime/debug"
	"time"

	"github.com/shopspring/decimal"
	"github.com/sirupsen/logrus"
	"gogs.daxia.dev/huanan/pkg.daxia.dev/db"
	"gorm.io/gorm"
	"nn.daxia.dev/gameproto"
	"nn.daxia.dev/model"
	"nn.daxia.dev/nxd"
)

func (p *Game) WaitReady(room Room) error {
	select {
	case <-room.ReadyCh:
		//
	case <-time.After(5 * time.Second):
		//
	}

	return nil
}

func (p *Game) WaitChooseMaster(room Room) error {
	select {
	case <-room.ChooseMasterCh:
		//
	case <-time.After(7 * time.Second):
		//
	}

	p.locker.Lock()
	defer p.locker.Unlock()

	roomUserIDList, err := p.getRoomUserIDList(room.ID)
	if err != nil {
		logrus.Error(err)
		return err
	}

	//设置状态
	userIDList, err := p.getRoomUserIDListWithStatus(room.ID, PlayerStatusReady)
	if err != nil {
		logrus.Error(err)
		return err
	}

	for _, userID := range userIDList {
		userItem := p.PlayerMap[userID]

		userItem.Status = PlayerStatusChooseMaster
		userItem.MasterMul = 0
		p.PlayerMap[userID] = userItem

		p.EventInfo(userID, "选庄", "通知默认不抢")

		nxd.SendMsgToUserList(roomUserIDList, gameproto.NotifyTypeEnum_NotifyTypeChooseMaster, &gameproto.ChooseMaster{
			UserID: uint32(userID),
			Mul:    uint32(0),
			RoomID: uint32(room.ID),
		})
	}

	//获取修正后的状态用户
	userIDList, err = p.getRoomUserIDListWithStatus(room.ID, PlayerStatusChooseMaster)
	if err != nil {
		logrus.Error(err)
		return err
	}

	maxMasterMul := 0
	for _, userID := range userIDList {
		userItem := p.PlayerMap[userID]
		if userItem.MasterMul > maxMasterMul {
			maxMasterMul = userItem.MasterMul
		}
	}

	masterIDList := make([]int32, 0)
	for _, userID := range userIDList {
		userItem := p.PlayerMap[userID]
		if userItem.MasterMul != maxMasterMul {
			continue
		}

		masterIDList = append(masterIDList, userID)
	}

	//更新用户，选取庄家
	index := rand.Intn(len(masterIDList))
	masterUserID := masterIDList[index]

	p.EventInfo(masterUserID, "选庄", "选为庄家")

	userItem := p.PlayerMap[masterUserID]
	userItem.IsMaster = true
	if userItem.MasterMul == 0 {
		userItem.MasterMul = 1
	}

	p.PlayerMap[masterUserID] = userItem

	//更新
	err = db.GetDB().Model(model.GameOrder{}).Where("user_id = ? and issue = ?", masterUserID, room.Issue).Update("is_master", 1).Error
	if err != nil {
		logrus.Error(err)
		return err
	}

	//通知
	nxd.SendMsgToUserList(roomUserIDList, gameproto.NotifyTypeEnum_NotifyTypeChooseMasterFinish, &gameproto.ChooseMasterFinish{
		MasterUserID: uint32(masterUserID),
		MulList:      []uint32{1, 4, 8, 11, 15},
		RoomID:       uint32(room.ID),
	})

	return nil
}

func (p *Game) WaitChooseMul(room Room) error {
	p.locker.Lock()
	{
		room.Status = RoomStatusChooseMul
		room.StatusStartTime = time.Now().Unix()
		p.RoomMap[room.ID] = room
	}

	//master直接发送，跳过开牌流程
	userIDList, err := p.getRoomUserIDListWithStatus(room.ID, PlayerStatusChooseMaster)
	if err != nil {
		logrus.Error(err)

		//FOCUS:UnLock
		p.locker.Unlock()
		return err
	}

	roomUserIDList, err := p.getRoomUserIDList(room.ID)
	if err != nil {
		logrus.Error(err)

		//FOCUS:UnLock
		p.locker.Unlock()
		return err
	}

	//master提前发送
	for _, userID := range userIDList {
		userItem, exists := p.PlayerMap[userID]
		if !exists {
			logrus.Error("not exist:", userID)
			continue
		}

		if !userItem.IsMaster {
			continue
		}

		userItem.Status = PlayerStatusChooseMul
		userItem.Mul = 1
		p.PlayerMap[userID] = userItem

		p.EventInfo(userID, "选倍数", "庄家默认倍数1")

		nxd.SendMsgToUserK(uint32(userID), gameproto.NotifyTypeEnum_NotifyTypeChooseMul, &gameproto.ChooseMul{
			UserID:   uint32(userID),
			Mul:      uint32(1),
			RoomID:   uint32(room.ID),
			CardList: GetNNCardList(userItem.CardList),
		})

		break
	}

	p.locker.Unlock()

	logrus.Infof("等待选择倍数")

	select {
	case <-room.ChooseMulCh:
		//
	case <-time.After(7 * time.Second):
		//
	}

	p.locker.Lock()
	defer p.locker.Unlock()

	logrus.Infof("未选取的用户，开始自动选取")
	for _, userID := range userIDList {
		userItem, exists := p.PlayerMap[userID]
		if !exists {
			logrus.Error("not exist:", userID)
			continue
		}

		if userItem.Status != PlayerStatusChooseMaster {
			continue
		}

		userItem.Status = PlayerStatusChooseMul
		userItem.Mul = 1
		p.PlayerMap[userID] = userItem

		p.EventInfo(userID, "选倍数", "默认倍数1")

		//通知
		for _, sendToUserID := range roomUserIDList {
			var cardList []uint32
			if userID == sendToUserID {
				cardList = GetNNCardList(userItem.CardList)
			}

			nxd.SendMsgToUserK(uint32(sendToUserID), gameproto.NotifyTypeEnum_NotifyTypeChooseMul, &gameproto.ChooseMul{
				UserID:   uint32(userID),
				Mul:      uint32(1),
				RoomID:   uint32(room.ID),
				CardList: cardList,
			})
		}
	}

	logrus.Infof("通知选择倍数完成, 用户数:%d", len(roomUserIDList))

	//通知
	nxd.SendMsgToUserList(roomUserIDList, gameproto.NotifyTypeEnum_NotifyTypeChooseMulFinish, &gameproto.ChooseMulFinish{
		RoomID: uint32(room.ID),
	})

	return nil
}

func (p *Game) WaitOpen(room Room) error {
	p.locker.Lock()
	{
		room.Status = RoomStatusOpen
		room.StatusStartTime = time.Now().Unix()
		p.RoomMap[room.ID] = room
	}
	p.locker.Unlock()

	select {
	case <-room.OpenCh:
		//
	case <-time.After(15 * time.Second):
		//
	}

	p.locker.Lock()
	defer p.locker.Unlock()

	roomUserIDList, err := p.getRoomUserIDList(room.ID)
	if err != nil {
		logrus.Error(err)
		return err
	}

	userIDList, err := p.getRoomUserIDListWithStatus(room.ID, PlayerStatusChooseMul)
	if err != nil {
		logrus.Error(err)
		return err
	}

	for _, userID := range userIDList {
		userItem, exists := p.PlayerMap[userID]
		if !exists {
			logrus.Error("not exist:", userID)
			continue
		}

		userItem.Status = PlayerStatusOpen
		p.PlayerMap[userID] = userItem

		p.EventInfo(userID, "开牌", "默认开牌")

		//通知
		nxd.SendMsgToUserList(roomUserIDList, gameproto.NotifyTypeEnum_NotifyTypeOpen, &gameproto.Open{
			RoomID:   uint32(room.ID),
			UserID:   uint32(userID),
			CardList: GetNNCardList(userItem.CardList),
		})
	}

	userIDList, err = p.getRoomUserIDListWithStatus(room.ID, PlayerStatusOpen)
	if err != nil {
		logrus.Error(err)
		return err
	}

	//获取庄家的牌
	var masterPlayerItem Player
	for _, userID := range userIDList {
		playerItem := p.PlayerMap[userID]
		if !playerItem.IsMaster {
			continue
		}

		masterPlayerItem = playerItem
		break
	}

	masterCardList := GetNNCardList(masterPlayerItem.CardList)
	masterTotalWinAmount := decimal.NewFromInt(0)
	timeNow := time.Now()
	msg := &gameproto.OpenFinish{
		RoomID:         uint32(room.ID),
		PayoutInfoList: []*gameproto.PayoutInfo{},
	}

	err = db.GetDB().Transaction(func(tx *gorm.DB) error {
		defer func() {
			if err := recover(); err != nil {
				logrus.Error(err)
				fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
			}
		}()

		for _, userID := range userIDList {
			if userID == masterPlayerItem.ID {
				continue
			}

			userItem, exists := p.PlayerMap[userID]
			if !exists {
				logrus.Error("not exist:", userID)
				continue
			}

			var isWin bool
			var winAmount decimal.Decimal
			userCardList := GetNNCardList(userItem.CardList)
			masterWin, isEqual, cardLevel := WinLevel(masterCardList, userCardList)
			if cardLevel == 0 {
				cardLevel = 1
			}

			if !masterWin {
				isWin = true

				//赢庄家
				winAmount = room.BaseAmount.Mul(decimal.NewFromInt(int64(masterPlayerItem.MasterMul))).Mul(decimal.NewFromInt(int64(userItem.Mul))).Mul(decimal.NewFromInt(int64(cardLevel)))
			} else {
				isWin = false

				//输给庄家
				winAmount = room.BaseAmount.Mul(decimal.NewFromInt(int64(masterPlayerItem.MasterMul))).Mul(decimal.NewFromInt(int64(userItem.Mul))).Mul(decimal.NewFromInt(-1)).Mul(decimal.NewFromInt(int64(cardLevel)))
			}

			if isEqual {
				isWin = false
				winAmount = decimal.NewFromInt(0)
			}

			err = tx.Model(model.User{}).Where("id = ? and balance + ? > 0", userID, winAmount).
				Update("balance", gorm.Expr("balance + ?", winAmount)).Error
			if err != nil {
				return err
			}

			//设置注单
			err = tx.Model(model.GameOrder{}).Where("issue = ? and user_id = ?", room.Issue, userID).Updates(map[string]interface{}{
				"is_draw":    1,
				"draw_time":  &timeNow,
				"is_win":     isWin,
				"win_amount": winAmount,
			}).Error
			if err != nil {
				return err
			}

			currUserModel := model.User{}
			err = tx.Model(model.User{}).Where("id = ?", userID).First(&currUserModel).Error
			if err != nil {
				return err
			}

			userItem.Balance = currUserModel.Balance
			p.PlayerMap[userID] = userItem

			//设置结算信息
			userBalance, _ := userItem.Balance.Round(2).Float64()
			msg.PayoutInfoList = append(msg.PayoutInfoList, &gameproto.PayoutInfo{
				UserID:    uint32(userID),
				WinAmount: float32(winAmount.Mul(decimal.NewFromInt(100)).IntPart()) / 100,
				IsMaster:  false,
				Balance:   userBalance,
			})

			masterTotalWinAmount = masterTotalWinAmount.Add(winAmount.Mul(decimal.NewFromInt(-1)))
		}

		err = tx.Model(model.User{}).Where("id = ? and balance + ? > 0", masterPlayerItem.ID, masterTotalWinAmount).
			Update("balance", gorm.Expr("balance + ?", masterTotalWinAmount)).Error
		if err != nil {
			logrus.Error(err)
			return err
		}

		err = tx.Model(model.GameOrder{}).Where("issue = ? and user_id = ?", room.Issue, masterPlayerItem.ID).Updates(map[string]interface{}{
			"is_draw":    1,
			"draw_time":  &timeNow,
			"is_win":     masterTotalWinAmount.GreaterThan(decimal.Zero),
			"win_amount": masterTotalWinAmount,
		}).Error

		//设置master结算信息,更新余额
		currUserModel := model.User{}
		err = tx.Model(model.User{}).Where("id = ?", masterPlayerItem.ID).First(&currUserModel).Error
		if err != nil {
			logrus.Error(err)
			return err
		}

		masterPlayerItem.Balance = currUserModel.Balance
		p.PlayerMap[masterPlayerItem.ID] = masterPlayerItem

		masterBalance, _ := masterPlayerItem.Balance.Round(2).Float64()
		msg.PayoutInfoList = append(msg.PayoutInfoList, &gameproto.PayoutInfo{
			UserID:    uint32(masterPlayerItem.ID),
			WinAmount: float32(masterTotalWinAmount.Mul(decimal.NewFromInt(100)).IntPart()) / 100,
			IsMaster:  true,
			Balance:   masterBalance,
		})

		return nil
	})

	if err != nil {
		logrus.Error(err, "===============")
		return err
	}

	//更新一下用户信息
	userModelList := make([]model.User, 0)
	err = db.GetDB().Model(model.User{}).Where("id in ?", roomUserIDList).Scan(&userModelList).Error
	if err != nil {
		logrus.Error(err)
		return err
	}

	for _, userModel := range userModelList {
		userID := int32(userModel.ID)
		userItem := p.PlayerMap[userID]

		userItem.Balance = userModel.Balance
		userItem.HeadImg = userModel.HeadImg
		userItem.Name = userModel.Name
		userItem.IsMaster = false
		userItem.Status = PlayerStatusWaitReady

		p.EventInfo(userID, "开牌", "更新用户状态为等待开牌")

		p.PlayerMap[userID] = userItem
	}

	room.Status = RoomStatusWaitReady
	room.StatusStartTime = time.Now().Unix()
	p.RoomMap[room.ID] = room

	//通知
	roomUserIDList, err = p.getRoomUserIDList(room.ID)
	if err != nil {
		logrus.Error(err)
		return err
	}

	nxd.SendMsgToUserList(roomUserIDList, gameproto.NotifyTypeEnum_NotifyTypeOpenFinish, msg)
	return nil
}

func (p *Game) getRoomUserIDListWithStatus(roomID int32, status PlayerStatus) ([]int32, error) {
	roomItem, exists := p.RoomMap[roomID]
	if !exists {
		return nil, errors.New("room not exists")
	}

	userIDList := make([]int32, 0)
	for _, chairItem := range roomItem.ChairList {
		if chairItem.PlayerID == 0 {
			continue
		}

		playerItem, exists := p.PlayerMap[chairItem.PlayerID]
		if !exists {
			continue
		}

		if playerItem.Status != status {
			continue
		}

		userIDList = append(userIDList, chairItem.PlayerID)
	}

	return userIDList, nil
}

func (p *Game) getRoomUserIDList(roomID int32) ([]int32, error) {
	roomItem, exists := p.RoomMap[roomID]
	if !exists {
		return nil, errors.New("room not exists")
	}

	userIDList := make([]int32, 0)
	for _, chairItem := range roomItem.ChairList {
		if chairItem.PlayerID == 0 {
			continue
		}

		userIDList = append(userIDList, chairItem.PlayerID)
	}

	return userIDList, nil
}
