package api

import (
	"errors"
	"fmt"
	"github.com/kataras/iris/v12"
	"github.com/sirupsen/logrus"
	"io"
	"main/database"
	"main/model"
	"main/utils"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

var initChessPawns = model.ChessStatus{
	Current: "",
	IsEnd:   false,
	Status:  "0,1,2,3,4,5,6,7,8;-1,-1,-1,-1,-1,-1,-1,-1,-1;-1,9,-1,-1,-1,-1,-1,10,-1;11,-1,12,-1,13,-1,14,-1,15;-1,-1,-1,-1,-1,-1,-1,-1,-1;-1,-1,-1,-1,-1,-1,-1,-1,-1;31,-1,30,-1,29,-1,28,-1,27;-1,26,-1,-1,-1,-1,-1,25,-1;-1,-1,-1,-1,-1,-1,-1,-1,-1;24,23,22,21,20,19,18,17,16",
	Winner:  "",
	ChessId: "",
}

func addAiChessPlayer(ctx iris.Context) {
	username := utils.GetLoginUser(ctx)
	if username.Username == "" {
		return
	}
	id := ctx.Params().Get("id")
	status := getChessRoomStatus(id)
	if status.Players != username.Username {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房主方可添加机器人"))
		return
	}
	status.Players += ",ai--" + id
	status.Status = initChessPawns.Status
	status.Current = username.Username
	status.IsEnd = initChessPawns.IsEnd
	status.Winner = initChessPawns.Winner
	status.ChessId = initChessPawns.ChessId
	db := database.GetInstance().GetMysqlDb()
	if err := db.Save(&status).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
	if utils.ErrHandle(ctx, err) {
		return
	}
}
func updateChessAiStep(ctx iris.Context) {
	username := utils.GetLoginUser(ctx)
	if username.Username == "" {
		return
	}
	id := ctx.Params().Get("id")
	status := getChessRoomStatus(id)
	if players := strings.Split(status.Players, ","); players[0] != username.Username || players[1] != "ai--"+id {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房间人员配置错误"))
		return
	}
	if status.Current != "ai--"+id {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("当前非ai回合"))
		return
	}
	stepList := strings.Split(getAiChessStep(chessPawnStatusToFen(chessPawnStrToSlice(status.Status)), "queryall", "b"), "|")
	var tmpNum int
	if strings.Index(stepList[0], "score:??") != -1 {
		rand.NewSource(time.Now().Unix())
		tmpNum = rand.Intn(len(stepList))
	} else {
		tmpNum = 0
	}
	stepTmp := strings.Split(stepList[tmpNum], ",")[0]
	//fmt.Println(stepTmp, chessPawnStatusToFen(chessPawnStrToSlice(status.Status)))
	if strings.Index(stepTmp, "move:") == -1 {
		ctx.StatusCode(iris.StatusInternalServerError)
		ctx.SetErr(errors.New("机器人无合适步骤，对局结束"))
		return
	}
	step := strings.Split(stepTmp, "move:")[1]
	tmp := "abcdefghi0123456789"
	statusList := chessPawnStrToSlice(status.Status)
	start0 := 8 - strings.IndexByte(tmp, step[0])
	end0 := 8 - strings.IndexByte(tmp, step[2])
	start1 := strings.IndexByte(tmp, step[1]) - 9
	end1 := strings.IndexByte(tmp, step[3]) - 9
	statusList[end1][end0] = statusList[start1][start0]
	statusList[start1][start0] = "-1"
	status.Status = chessPawnSliceToStr(statusList)
	status.ChessId = statusList[end1][end0]
	status.Current = username.Username
	db := database.GetInstance().GetMysqlDb()
	if err := db.Save(&status).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	err := ctx.JSON(utils.FormatRes(200, "", status))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

func testChess(ctx iris.Context) {
	var res [][]string
	db := database.GetInstance().GetMysqlDb()
	var status []model.ChessStatus
	if err := db.Find(&status).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	for _, s := range status {
		stepTmp := getAiChessStep(chessPawnStatusToFen(chessPawnStrToSlice(s.Status)), "queryall", "b")
		//fmt.Println(s.Status, chessPawnStatusToFen(chessPawnStrToSlice(s.Status)))
		step := strings.Split(stepTmp, "move:")[1]
		tmp := "abcdefghi0123456789"
		//statusList := chessPawnStrToSlice(status.Status)
		start0 := strings.IndexByte(tmp, step[0])
		end0 := strings.IndexByte(tmp, step[2])
		start1 := strings.IndexByte(tmp, step[1]) - 9
		end1 := strings.IndexByte(tmp, step[3]) - 9
		statusTmp := chessPawnStrToSlice(s.Status)
		start := statusTmp[start1][start0]
		end := statusTmp[end1][end0]
		res = append(res, []string{start, end})
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

func resetRoom(ctx iris.Context) {
	username := utils.GetLoginUser(ctx)
	if username.Username == "" {
		return
	}
	id := ctx.Params().Get("id")
	status := getChessRoomStatus(id)
	if utils.DataIsNil(status) {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房间号不存在"))
		return
	}
	if strings.Split(status.Players, ",")[0] != username.Username {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房主方可重置房间"))
		return
	}
	db := database.GetInstance().GetMysqlDb()
	status.Status = initChessPawns.Status
	status.Current = username.Username
	status.IsEnd = initChessPawns.IsEnd
	status.Winner = initChessPawns.Winner
	status.ChessId = initChessPawns.ChessId
	if err := db.Save(&status).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

// 新建房间
func newChessRoom(ctx iris.Context) {
	creator := utils.GetLoginUser(ctx)
	if creator.Username == "" {
		return
	}
	db := database.GetInstance().GetMysqlDb()
	status := model.ChessStatus{
		Players: creator.Username,
		Current: creator.Username,
		IsEnd:   initChessPawns.IsEnd,
		Status:  initChessPawns.Status,
		Winner:  initChessPawns.Winner,
		ChessId: initChessPawns.ChessId,
	}
	if err := db.Create(&status).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status.ID))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

// 获取所有房间
func getChessRooms(ctx iris.Context) {
	db := database.GetInstance().GetMysqlDb()
	var rooms []model.ChessStatus
	if err := db.Find(&rooms).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", rooms))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

// 加入房间
func joinChessRoom(ctx iris.Context) {
	username := utils.GetLoginUser(ctx)
	if utils.DataIsNil(username) {
		return
	}
	id := ctx.Params().Get("id")
	status := getChessRoomStatus(id)
	if utils.DataIsNil(status) {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房间号不存在"))
		return
	}
	if len(strings.Split(status.Players, ",")) >= 2 {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房间人数已满"))
		return
	}
	if utils.CheckListItem(strings.Split(status.Players, ","), username.Username) {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("不可重复加入房间"))
		return
	}
	status.Players += fmt.Sprintf(",%s", username.Username)
	db := database.GetInstance().GetMysqlDb()
	if err := db.Save(&status).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	if err := db.Create(&model.ChessStatusLog{
		RoomId:  status.ID,
		Players: status.Players,
		Current: status.Current,
		IsEnd:   status.IsEnd,
		Status:  status.Status,
		Winner:  status.Winner,
		ChessId: status.ChessId,
		Time:    status.CreatedAt,
	}).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

// 离开房间
func leaveChessRoom(ctx iris.Context) {
	username := utils.GetLoginUser(ctx)
	if utils.DataIsNil(username) {
		return
	}
	id := ctx.Params().Get("id")
	status := getChessRoomStatus(id)
	if utils.DataIsNil(status) {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房间号不存在"))
		return
	}
	if !utils.CheckListItem(strings.Split(status.Players, ","), username.Username) {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("未加入该房间"))
		return
	}
	for _, v := range strings.Split(status.Players, ",") {
		if v != username.Username {
			status.Players = v
		}
	}
	db := database.GetInstance().GetMysqlDb()
	if status.Players == username.Username || status.Players == "ai--"+id {
		if err := db.Delete(&status).Error; err != nil {
			logrus.Errorln("sql执行失败：", err)
		}
	} else {
		status.Current = status.Players
		status.ChessId = initChessPawns.ChessId
		status.IsEnd = initChessPawns.IsEnd
		status.Winner = initChessPawns.Winner
		status.Status = initChessPawns.Status
		if err := db.Save(&status).Error; err != nil {
			logrus.Errorln("sql执行失败：", err)
		}
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

// 获取房间状态接口
func getChessRoom(ctx iris.Context) {
	id := ctx.Params().Get("id")
	status := getChessRoomStatus(id)
	if utils.DataIsNil(status) {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房间号不存在"))
		return
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

// 更新棋盘状态
func updateChessStatus(ctx iris.Context) {
	username := utils.GetLoginUser(ctx)
	if utils.DataIsNil(username) {
		return
	}
	id := ctx.Params().Get("id")
	pawn := ctx.URLParam("pawn")
	des := ctx.URLParam("des")
	status := getChessRoomStatus(id)
	if utils.DataIsNil(status) {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房间号不存在"))
		return
	}
	if len(strings.Split(status.Players, ",")) < 2 {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("房间人数不足"))
		return
	}
	pawnNum, _ := strconv.Atoi(pawn)
	if (strings.Split(status.Players, ",")[0] == username.Username && pawnNum > 15) || (strings.Split(status.Players, ",")[1] == username.Username && pawnNum < 16) {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("只能移动自己的棋子"))
		return
	}
	if status.Current != username.Username {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("当前不是你的回合"))
		return
	}
	if checkChessStep(pawn, status.Status, des) {
		tmpStatus := chessPawnStrToSlice(status.Status)
		locTmp := getPawnLoc(status.Status, pawn)
		toTmp0, _ := strconv.Atoi(strings.Split(des, ",")[0])
		toTmp1, _ := strconv.Atoi(strings.Split(des, ",")[1])
		tmpStatus[locTmp[0]][locTmp[1]] = "-1"
		tmpStatus[toTmp0][toTmp1] = pawn
		status.Status = chessPawnSliceToStr(tmpStatus)
		status.ChessId = pawn
		for _, v := range strings.Split(status.Players, ",") {
			if v != username.Username {
				status.Current = v
			}
		}
		if checkWinner(status.Status) == 1 {
			status.Winner = username.Username
			status.IsEnd = true
			status.Current = ""
		}
	} else {
		ctx.StatusCode(iris.StatusBadRequest)
		ctx.SetErr(errors.New("当前无法移到该位置"))
		return
	}
	db := database.GetInstance().GetMysqlDb()
	if checkWinner(status.Status) == 2 {
		for _, v := range strings.Split(status.Players, ",") {
			if v != username.Username {
				status.Winner = v
				status.IsEnd = true
			}
		}
	}
	if err := db.Save(&status).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	if err := db.Create(&model.ChessStatusLog{
		RoomId:  status.ID,
		Current: status.Current,
		Players: status.Players,
		IsEnd:   status.IsEnd,
		Status:  status.Status,
		Winner:  status.Winner,
		ChessId: status.ChessId,
		Time:    status.CreatedAt,
	}).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

// 获取房间状态方法
func getChessRoomStatus(id string) model.ChessStatus {
	db := database.GetInstance().GetMysqlDb()
	var status model.ChessStatus
	if err := db.Where("id = ?", id).First(&status).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	return status
}

func checkWinner(status string) int {
	if utils.DataIsNil(getPawnLoc(status, "4")) || utils.DataIsNil(getPawnLoc(status, "20")) {
		return 1
	}
	if checkNullPath(chessPawnStrToSlice(status), getPawnLoc(status, "4"), getPawnLoc(status, "20")) {
		return 2
	}
	return 0
}

// 获取棋子位置
func getPawnLoc(statusStr string, pawn string) [2]int {
	var res [2]int
	status := chessPawnStrToSlice(statusStr)
	for row, i := range status {
		for col, j := range i {
			if j == pawn {
				res[0] = row
				res[1] = col
			}
		}
	}
	return res
}

// 判断棋子是否可移动
func checkChessStep(pawn string, statusStr string, to string) bool {
	locList := getPawnLoc(statusStr, pawn)
	status := chessPawnStrToSlice(statusStr)
	locTmp0 := locList[0]
	locTmp1 := locList[1]
	toTmp := strings.Split(to, ",")
	toTmp0, _ := strconv.Atoi(toTmp[0])
	toTmp1, _ := strconv.Atoi(toTmp[1])
	//目标位置超出棋盘
	if toTmp0 < 0 || toTmp1 < 0 || toTmp0 > 10 || toTmp1 > 8 {
		return false
	}
	pawnNum, _ := strconv.Atoi(pawn)
	toPawnNum, _ := strconv.Atoi(status[toTmp0][toTmp1])
	if (pawnNum < 16 && toPawnNum < 16 && toPawnNum >= 0) || (pawnNum > 15 && toPawnNum > 15) {
		return false
	}
	switch pawn {
	case "0", "8", "16", "24": //车：同行列，路径为空
		if (toTmp0 == locTmp0 && toTmp1 != locTmp1) || (toTmp1 == locTmp1 && toTmp0 != locTmp0) && checkNullPath(status, [2]int{locTmp0, locTmp1}, [2]int{toTmp0, toTmp1}) {
			return true
		}
	case "1", "7", "17", "23": //马：日字，前后左右无子
		if (utils.IntAbs(toTmp0-locTmp0) == 1 && utils.IntAbs(toTmp1-locTmp1) == 2 && status[locTmp0][(locTmp1+toTmp1)/2] == "-1") || (utils.IntAbs(toTmp0-locTmp0) == 2 && utils.IntAbs(toTmp1-locTmp1) == 1 && status[(locTmp0+toTmp0)/2][locTmp1] == "-1") {
			return true
		}
	case "2", "6", "18", "22": //象：田字，不过河，中间格子为空
		if utils.IntAbs(toTmp0-locTmp0) == 2 && utils.IntAbs(toTmp1-locTmp1) == 2 && ((locTmp0 < 5 && toTmp0 < 5) || (locTmp0 > 4 && toTmp0 > 4)) && status[(locTmp0+toTmp0)/2][(locTmp1+toTmp1)/2] == "-1" {
			return true
		}
	case "3", "5", "19", "21": //士：斜着走，不超出方格
		if toTmp1 < 3 || toTmp1 > 5 || (locTmp0 < 5 && toTmp0 > 2) || (locTmp0 > 4 && toTmp0 < 7) {
			return false
		}
		if utils.IntAbs(toTmp0-locTmp0) == 1 && utils.IntAbs(toTmp1-locTmp1) == 1 {
			return true
		}
	case "4", "20": //将：走一步，不超出方格
		if toTmp1 < 3 || toTmp1 > 5 || (locTmp0 < 5 && toTmp0 > 2) || (locTmp0 > 4 && toTmp0 < 7) {
			return false
		}
		if utils.IntAbs(toTmp0-locTmp0)+utils.IntAbs(toTmp1-locTmp1) == 1 {
			return true
		}
	case "9", "10", "25", "26": //炮：同行列，路径不为空，目标位置不为空
		if ((toTmp0 == locTmp0 && toTmp1 != locTmp1) || (toTmp1 == locTmp1 && toTmp0 != locTmp0)) && ((!checkNullPath(status, [2]int{locTmp0, locTmp1}, [2]int{toTmp0, toTmp1}) && status[toTmp0][toTmp1] != "-1") || (checkNullPath(status, [2]int{locTmp0, locTmp1}, [2]int{toTmp0, toTmp1}) && status[toTmp0][toTmp1] == "-1")) {
			return true
		}
	case "11", "12", "13", "14", "15": //兵：走一步，过河后可横移
		if (toTmp0 < 5 && toTmp1 == locTmp1 && toTmp0-locTmp0 == 1) || (toTmp0 > 4 && utils.IntAbs(toTmp0-locTmp0+toTmp1-locTmp1) == 1) {
			return true
		}
	case "27", "28", "29", "30", "31": //兵：走一步，过河后可横移
		if (toTmp0 > 4 && toTmp1 == locTmp1 && toTmp0-locTmp0 == -1) || (toTmp0 < 5 && utils.IntAbs(toTmp0-locTmp0+toTmp1-locTmp1) == 1) {
			return true
		}
	default:
		return false
	}
	return false
}

// 棋盘状态字符串转数组
func chessPawnStrToSlice(status string) [][]string {
	var res [][]string
	for _, i := range strings.Split(status, ";") {
		var colList []string
		for _, j := range strings.Split(i, ",") {
			colList = append(colList, j)
		}
		res = append(res, colList)
	}
	return res
}

// 棋盘状态数组转字符串
func chessPawnSliceToStr(status [][]string) string {
	var resList []string
	for _, i := range status {
		resList = append(resList, strings.Join(i, ","))
	}
	res := strings.Join(resList, ";")
	return res
}

func chessPawnStatusToFen(status [][]string) string {
	var res []string
	for _, i := range status {
		resTmp := ""
		count := 0
		for col, j := range i {
			if j == "-1" && col == len(i)-1 {
				resTmp += strconv.Itoa(count + 1)
			} else if j == "-1" {
				count += 1
			} else {
				if count != 0 {
					resTmp += strconv.Itoa(count)
					count = 0
				}
				switch j {
				case "0", "8":
					resTmp += "R"
				case "1", "7":
					resTmp += "N"
				case "2", "6":
					resTmp += "B"
				case "3", "5":
					resTmp += "A"
				case "4":
					resTmp += "K"
				case "9", "10":
					resTmp += "C"
				case "11", "12", "13", "14", "15":
					resTmp += "P"
				case "16", "24":
					resTmp += "r"
				case "17", "23":
					resTmp += "n"
				case "18", "22":
					resTmp += "b"
				case "19", "21":
					resTmp += "a"
				case "20":
					resTmp += "k"
				case "25", "26":
					resTmp += "c"
				case "27", "28", "29", "30", "31":
					resTmp += "p"
				}
			}
		}
		res = append(res, resTmp)
	}
	return utils.Reverse(strings.Join(res, "/"))
}

// 判断路径是否为空
func checkNullPath(status [][]string, start [2]int, end [2]int) bool {
	if start[0] == end[0] {
		path := []int{start[1], end[1]}
		sort.Ints(path)
		for i := path[0] + 1; i < path[1]; i++ {
			if status[start[0]][i] != "-1" {
				return false
			}
		}
		return true
	}
	if start[1] == end[1] {
		path := []int{start[0], end[0]}
		sort.Ints(path)
		for i := path[0] + 1; i < path[1]; i++ {
			if status[i][start[1]] != "-1" {
				return false
			}
		}
		return true
	}
	return false
}

func getAiChessStep(fenStatus string, t string, p string) string {
	urlPath, err := url.Parse("http://www.chessdb.cn/chessdb.php")
	params := url.Values{}
	params.Set("action", t)
	params.Set("board", fmt.Sprintf("%s %s", fenStatus, p))
	params.Set("showall", "1")
	urlPath.RawQuery = params.Encode()
	res, err := http.Get(urlPath.String())
	if err != nil {
		return err.Error()
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
		}
	}(res.Body)

	body, _ := io.ReadAll(res.Body)
	return string(body)
}
