package api

import (
	"errors"
	"github.com/kataras/iris/v12"
	"github.com/sirupsen/logrus"
	"main/database"
	"main/model"
	"main/utils"
	"strings"
)

func getSudokuList(ctx iris.Context) {
	var resSudokuList resSudokuListType
	var startingSudokuList []sudokuListType
	var newSudokuList []model.Sudoku
	db := database.GetInstance().GetMysqlDb()
	if err := db.Where("id not in (?)", db.Table("sudoku_statuses").Distinct("sudoku_id").Where("username = ? and deleted_at is null", "admin")).Find(&newSudokuList).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	for _, v := range newSudokuList {
		resSudokuList.New = append(resSudokuList.New, sudokuListType{Sudoku: v.Sudoku, SudokuId: v.ID, Complete: false})
	}
	queryId := db.Table("sudoku_statuses").Select("max(id)").Where("username = ? and deleted_at is null", "admin").Group("sudoku_id")
	if queryId.Error != nil {
		logrus.Errorln("sql执行失败：", queryId.Error)
	}
	queryStatus := db.Table("sudoku_statuses").Where("id in (?)", queryId)
	if queryStatus.Error != nil {
		logrus.Errorln("sql执行失败：", queryStatus.Error)
	}
	if err := db.Model(&model.Sudoku{}).Select("sudokus.sudoku, q.*").Joins("left join (?) q on sudokus.id = q.sudoku_id", queryStatus).Scan(&startingSudokuList).Error; err != nil {
		logrus.Errorln("sql执行失败：", err)
	}
	for _, v := range startingSudokuList {
		if v.Complete {
			resSudokuList.Complete = append(resSudokuList.Complete, v)
		} else {
			resSudokuList.Starting = append(resSudokuList.Starting, v)
		}
	}
	err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", resSudokuList))
	if utils.ErrHandle(ctx, err) {
		return
	}
}

func addSudokuGame(ctx iris.Context) {
	var sudokuParam addSudokuParam
	var dbCheck model.Sudoku
	var sudokuResult [9][9]string
	err := ctx.ReadJSON(&sudokuParam)
	if utils.ErrHandle(ctx, err) {
		return
	}
	if sudokuParam.Result != "" {
		sudokuResult, err = sudokuStringToList(sudokuParam.Result)
		if utils.ErrHandle(ctx, err) {
			return
		}
	}
	db := database.GetInstance().GetMysqlDb()
	if err1 := db.Where("sudoku = ?", sudokuParam.Sudoku).First(&dbCheck).Error; err1 != nil {
		logrus.Errorln("sql执行失败：", err1)
	}
	if !utils.DataIsNil(dbCheck) {
		utils.ErrHandle(ctx, errors.New("棋盘已存在"))
		return
	}
	err, r := utils.CheckSudoku(sudokuParam.Sudoku)
	if utils.ErrHandle(ctx, err) {
		return
	}
	if r.Data.Check || checkSudokuCompleted(sudokuResult) {
		if r.Data.Check {
			if err1 := db.Create(&model.Sudoku{Sudoku: sudokuParam.Sudoku, Username: "admin", Result: r.Data.Result}).Error; err1 != nil {
				logrus.Errorln("sql执行失败：", err1)
			}
		} else {
			if err1 := db.Create(&model.Sudoku{Sudoku: sudokuParam.Sudoku, Username: "admin", Result: sudokuParam.Result}).Error; err1 != nil {
				logrus.Errorln("sql执行失败：", err1)
			}
		}
		err = ctx.JSON(utils.FormatRes(iris.StatusOK, "", "success"))
		if utils.ErrHandle(ctx, err) {
			return
		}
	} else {
		utils.ErrHandle(ctx, errors.New("检测题目无法完成，请上传解题结果"))
		return
	}
}

func sudokuStringToList(sudoku string) ([9][9]string, error) {
	var res [9][9]string
	sudokuRows := strings.Split(sudoku, ";")
	if len(sudokuRows) != 9 {
		return res, errors.New("棋盘格式错误")
	}
	for r, row := range sudokuRows {
		sudokuCols := strings.Split(row, ",")
		if len(sudokuCols) != 9 {
			return res, errors.New("棋盘格式错误")
		}
		for c, col := range sudokuCols {
			if len(col) > 1 || strings.Index("123456789", col) == -1 {
				return res, errors.New("棋盘格式错误")
			}
			res[r][c] = col
		}
	}
	return res, nil
}

func sudokuListToString(sudoku [9][9]string) string {
	var res string
	for r, _ := range sudoku {
		var tmp string
		for c, _ := range sudoku[r] {
			if tmp == "" {
				tmp = sudoku[r][c]
			} else {
				tmp += "," + sudoku[r][c]
			}
		}
		if res == "" {
			res = tmp
		} else {
			res += ";" + tmp
		}
	}
	return res
}

// 检查题目是否已完成
func checkSudokuCompleted(sudoku [9][9]string) bool {
	for r, _ := range sudoku {
		for c, _ := range sudoku[r] {
			if sudoku[r][c] == "" || !checkNum(r, c, sudoku) {
				return false
			}
		}
	}
	return true
}

// 检查题目当前位置是否符合要求
func checkNum(r int, c int, sudoku [9][9]string) bool {
	if sudoku[r][c] == "" {
		return true
	}
	for i, n := range sudoku[r] {
		if n == sudoku[r][c] && i != c {
			return false
		}
	}
	for i, _ := range sudoku {
		if sudoku[i][c] == sudoku[r][c] && i != r {
			return false
		}
	}
	for row := (r % 3) * 3; row < (r%3)*3+2; row++ {
		for col := (c % 3) * 3; col < (c%3)*3+2; col++ {
			if sudoku[row][col] == sudoku[r][c] && row != r && col != c {
				return false
			}
		}
	}
	return true
}
