package main

import (
	"fmt"
	"sort"
)

const aiDepth = 3

var pieceValues = map[int]int{
	Empty:  0, Pawn: 100, Knight: 320, Bishop: 330,
	Rook: 500, Queen: 900, King: 20000,
}

var pawnTable = [8][8]int{
	{0, 0, 0, 0, 0, 0, 0, 0},
	{50, 50, 50, 50, 50, 50, 50, 50},
	{10, 10, 20, 30, 30, 20, 10, 10},
	{5, 5, 10, 25, 25, 10, 5, 5},
	{0, 0, 0, 20, 20, 0, 0, 0},
	{5, -5, -10, 0, 0, -10, -5, 5},
	{5, 10, 10, -20, -20, 10, 10, 5},
	{0, 0, 0, 0, 0, 0, 0, 0},
}

var knightTable = [8][8]int{
	{-50, -40, -30, -30, -30, -30, -40, -50},
	{-40, -20, 0, 0, 0, 0, -20, -40},
	{-30, 0, 10, 15, 15, 10, 0, -30},
	{-30, 5, 15, 20, 20, 15, 5, -30},
	{-30, 0, 15, 20, 20, 15, 0, -30},
	{-30, 5, 10, 15, 15, 10, 5, -30},
	{-40, -20, 0, 5, 5, 0, -20, -40},
	{-50, -40, -30, -30, -30, -30, -40, -50},
}

var bishopTable = [8][8]int{
	{-20, -10, -10, -10, -10, -10, -10, -20},
	{-10, 0, 0, 0, 0, 0, 0, -10},
	{-10, 0, 5, 10, 10, 5, 0, -10},
	{-10, 5, 5, 10, 10, 5, 5, -10},
	{-10, 0, 10, 10, 10, 10, 0, -10},
	{-10, 10, 10, 10, 10, 10, 10, -10},
	{-10, 5, 0, 0, 0, 0, 5, -10},
	{-20, -10, -10, -10, -10, -10, -10, -20},
}

var rookTable = [8][8]int{
	{0, 0, 0, 0, 0, 0, 0, 0},
	{5, 10, 10, 10, 10, 10, 10, 5},
	{-5, 0, 0, 0, 0, 0, 0, -5},
	{-5, 0, 0, 0, 0, 0, 0, -5},
	{-5, 0, 0, 0, 0, 0, 0, -5},
	{-5, 0, 0, 0, 0, 0, 0, -5},
	{-5, 0, 0, 0, 0, 0, 0, -5},
	{0, 0, 0, 5, 5, 0, 0, 0},
}

var queenTable = [8][8]int{
	{-20, -10, -10, -5, -5, -10, -10, -20},
	{-10, 0, 0, 0, 0, 0, 0, -10},
	{-10, 0, 5, 5, 5, 5, 0, -10},
	{-5, 0, 5, 5, 5, 5, 0, -5},
	{0, 0, 5, 5, 5, 5, 0, -5},
	{-10, 5, 5, 5, 5, 5, 0, -10},
	{-10, 0, 5, 0, 0, 0, 0, -10},
	{-20, -10, -10, -5, -5, -10, -10, -20},
}

var kingTable = [8][8]int{
	{-30, -40, -40, -50, -50, -40, -40, -30},
	{-30, -40, -40, -50, -50, -40, -40, -30},
	{-30, -40, -40, -50, -50, -40, -40, -30},
	{-30, -40, -40, -50, -50, -40, -40, -30},
	{-20, -30, -30, -40, -40, -30, -30, -20},
	{-10, -20, -20, -20, -20, -20, -20, -10},
	{20, 20, 0, 0, 0, 0, 20, 20},
	{20, 30, 10, 0, 0, 10, 30, 20},
}

func evaluate(b *Board) int {
	score := 0
	for r := 0; r < 8; r++ {
		for c := 0; c < 8; c++ {
			s := b.Grid[r][c]
			if s.Piece == Empty {
				continue
			}
			val := pieceValues[s.Piece]
			var bonus int
			switch s.Piece {
			case Pawn:
				if s.Color == White {
					bonus = pawnTable[r][c]
				} else {
					bonus = pawnTable[7-r][c]
				}
			case Knight:
				if s.Color == White {
					bonus = knightTable[r][c]
				} else {
					bonus = knightTable[7-r][c]
				}
			case Bishop:
				if s.Color == White {
					bonus = bishopTable[r][c]
				} else {
					bonus = bishopTable[7-r][c]
				}
			case Rook:
				if s.Color == White {
					bonus = rookTable[r][c]
				} else {
					bonus = rookTable[7-r][c]
				}
			case Queen:
				if s.Color == White {
					bonus = queenTable[r][c]
				} else {
					bonus = queenTable[7-r][c]
				}
			case King:
				if s.Color == White {
					bonus = kingTable[r][c]
				} else {
					bonus = kingTable[7-r][c]
				}
			}
			if s.Color == White {
				score += val + bonus
			} else {
				score -= val + bonus
			}
		}
	}
	return score
}

func minimax(b *Board, depth int, alpha, beta int, color int) int {
	if depth == 0 {
		return evaluate(b)
	}
	moves := b.generateLegalMoves(color)
	if len(moves) == 0 {
		if b.isInCheck(color) {
			if color == White {
				return -100000 - depth
			}
			return 100000 + depth
		}
		return 0
	}
	oppColor := Black
	if color == White {
		oppColor = Black
	} else {
		oppColor = White
	}
	if color == White {
		maxEval := -999999
		for _, m := range moves {
			clone := b.Clone()
			clone.applyMove(m)
			eval := minimax(clone, depth-1, alpha, beta, oppColor)
			if eval > maxEval {
				maxEval = eval
			}
			if maxEval > alpha {
				alpha = maxEval
			}
			if alpha >= beta {
				break
			}
		}
		return maxEval
	} else {
		minEval := 999999
		for _, m := range moves {
			clone := b.Clone()
			clone.applyMove(m)
			eval := minimax(clone, depth-1, alpha, beta, oppColor)
			if eval < minEval {
				minEval = eval
			}
			if minEval < beta {
				beta = minEval
			}
			if alpha >= beta {
				break
			}
		}
		return minEval
	}
}

func GetBestMove(b *Board, color int) (string, error) {
	moves := b.generateLegalMoves(color)
	if len(moves) == 0 {
		return "", fmt.Errorf("no legal moves")
	}
	oppColor := Black
	if color == White {
		oppColor = Black
	} else {
		oppColor = White
	}
	var bestMove *Move
	var bestScore int
	first := true
	alpha := -999999
	beta := 999999
	sort.Slice(moves, func(i, j int) bool {
		vi := 0
		vj := 0
		if b.Grid[moves[i].To.Row][moves[i].To.Col].Piece != Empty {
			vi = pieceValues[b.Grid[moves[i].To.Row][moves[i].To.Col].Piece]
		}
		if b.Grid[moves[j].To.Row][moves[j].To.Col].Piece != Empty {
			vj = pieceValues[b.Grid[moves[j].To.Row][moves[j].To.Col].Piece]
		}
		return vi > vj
	})
	for _, m := range moves {
		clone := b.Clone()
		clone.applyMove(m)
		score := minimax(clone, aiDepth-1, alpha, beta, oppColor)
		if first {
			bestMove = &m
			bestScore = score
			first = false
		} else if color == White && score > bestScore {
			bestScore = score
			bestMove = &m
			alpha = score
		} else if color == Black && score < bestScore {
			bestScore = score
			bestMove = &m
			beta = score
		}
	}
	if bestMove == nil {
		return "", fmt.Errorf("no move found")
	}
	return MoveToAlgebraic(*bestMove), nil
}

func PlayVsComputer() {
	fmt.Println("=== CHESS VS COMPUTER ===")
	fmt.Println("You play WHITE. Enter moves like: e2e4, g1f3, e7e8q")
	fmt.Println("Type 'quit' to exit.")
	fmt.Println()
	g := NewGame()
	fmt.Print(g.Render())
	for !g.Over {
		fmt.Print("White> ")
		var input string
		fmt.Scanln(&input)
		if input == "quit" || input == "q" {
			fmt.Println("Goodbye!")
			return
		}
		_, err := g.MakeMove(input)
		if err != nil {
			fmt.Printf("  Error: %v\n\n", err)
			continue
		}
		fmt.Print(g.Render())
		if g.Over {
			break
		}
		fmt.Print("  Black thinking... ")
		aiMove, err := GetBestMove(g.Board, Black)
		if err != nil {
			fmt.Printf("AI error: %v\n", err)
			break
		}
		fmt.Printf("Black plays %s\n", aiMove)
		_, err = g.MakeMove(aiMove)
		if err != nil {
			fmt.Printf("  Error: %v\n", err)
			continue
		}
		fmt.Print(g.Render())
	}
	switch g.Result {
	case WhiteWins:
		fmt.Println("  *** YOU WIN! ***")
	case BlackWins:
		fmt.Println("  *** COMPUTER WINS! ***")
	case Draw:
		fmt.Println("  *** DRAW! ***")
	}
}

