package main

import (
	"fmt"
	"math/rand"
	"strings"
	"time"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
)

// ChessModel — TUI поверх ChessGameState. Человек всегда играет
// белыми (ходят первыми), бот — чёрными. Превращение пешки
// автоматическое (всегда в ферзя, см. примечание в chess_game.go),
// поэтому отдельного шага выбора фигуры превращения не требуется.
type ChessModel struct {
	game     *ChessGameState
	bot      ChessBot
	human    ChessColor
	cursor   ChessPos
	selected *ChessPos

	message string
	isError bool

	quitting   bool
	backToMenu bool
}

// NewChessModel создаёт новую партию: человек играет белыми. depth —
// глубина поиска бота в полуходах (0 -> значение по умолчанию).
func NewChessModel(depth int, colorChoice ColorChoice) ChessModel {
	human := resolveChessColor(colorChoice)
	return ChessModel{
		game:   NewChessGame(),
		bot:    ChessBot{Depth: depth},
		human:  human,
		cursor: ChessPos{Row: 7, Col: 4},
	}
}

// resolveChessColor переводит выбор цвета из настроек в конкретный
// ChessColor — при случайном выборе бросает монету одноразовым
// генератором (партия сама по себе детерминирована, отдельный rng в
// модели ради этого не нужен).
func resolveChessColor(choice ColorChoice) ChessColor {
	switch choice {
	case ColorChoiceA:
		return ChessWhite
	case ColorChoiceB:
		return ChessBlack
	default:
		rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
		if rnd.Intn(2) == 0 {
			return ChessWhite
		}
		return ChessBlack
	}
}

func (m ChessModel) Init() tea.Cmd {
	return m.maybeScheduleBot()
}

func (m ChessModel) maybeScheduleBot() tea.Cmd {
	if m.game.Result != nil {
		return nil
	}
	if m.game.Turn == m.human {
		return nil
	}
	return tea.Tick(botMoveDelay, func(time.Time) tea.Msg { return botMoveMsg{} })
}

func (m *ChessModel) setInfo(format string, args ...interface{}) {
	m.message = fmt.Sprintf(format, args...)
	m.isError = false
}

func (m *ChessModel) setError(err error) {
	m.message = T(err.Error())
	m.isError = true
}

func (m ChessModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		return m.handleKey(msg)
	case botMoveMsg:
		return m.handleBotMove()
	}
	return m, nil
}

func (m ChessModel) handleBotMove() (tea.Model, tea.Cmd) {
	if m.game.Result != nil || m.game.Turn == m.human {
		return m, nil
	}
	if err := m.bot.PlayFullTurn(m.game); err != nil {
		m.setError(err)
		return m, nil
	}
	m.setInfo(T("chess.msg.bot_moved"))
	return m, m.maybeScheduleBot()
}

func (m ChessModel) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
	key := msg.String()

	if key == "ctrl+c" {
		m.quitting = true
		return m, tea.Quit
	}
	if key == "q" {
		m.backToMenu = true
		return m, nil
	}

	if m.game.Result != nil {
		if key == "n" {
			m.game = NewChessGame()
			m.selected = nil
			m.message = ""
			m.isError = false
			return m, m.maybeScheduleBot()
		}
		return m, nil
	}

	if m.game.Turn != m.human {
		return m, nil
	}

	switch key {
	case "up", "k":
		if m.cursor.Row > 0 {
			m.cursor.Row--
		}
	case "down", "j":
		if m.cursor.Row < 7 {
			m.cursor.Row++
		}
	case "left", "h":
		if m.cursor.Col > 0 {
			m.cursor.Col--
		}
	case "right", "l":
		if m.cursor.Col < 7 {
			m.cursor.Col++
		}
	case "esc":
		m.selected = nil
		m.message = ""
	case "enter", " ":
		return m.handleSelectOrMove()
	}
	return m, nil
}

func (m ChessModel) handleSelectOrMove() (tea.Model, tea.Cmd) {
	if m.selected == nil {
		piece := m.game.Board[m.cursor.Row][m.cursor.Col]
		if piece == nil || piece.Color != m.human {
			m.setError(ErrChessNoPiece)
			return m, nil
		}
		if len(m.game.LegalMovesFrom(m.cursor)) == 0 {
			m.setError(ErrChessInvalidMove)
			return m, nil
		}
		pos := m.cursor
		m.selected = &pos
		m.message = ""
		return m, nil
	}

	if *m.selected == m.cursor {
		m.selected = nil
		return m, nil
	}

	from := *m.selected
	err := m.game.Move(from, m.cursor)
	if err != nil {
		m.setError(err)
		return m, nil
	}
	m.setInfo(T("chess.msg.moved"))
	m.selected = nil
	return m, m.maybeScheduleBot()
}

// --- Отрисовка -----------------------------------------------------

// chessGlyphs — буквы фигур по стандартной шахматной нотации:
// заглавные для белых, строчные для чёрных (цвет дополнительно
// подчёркивается стилем текста). Сознательно НЕ используются
// символы Unicode ♔♕♖♗♘♙ — у них неоднозначная ширина отображения
// в разных шрифтах/рендерерах (где-то один символ на ширину клетки,
// где-то два), из-за чего клетки с фигурами визуально "сжимались"
// относительно пустых клеток. Буквы ASCII гарантированно имеют
// одинаковую ширину везде.
var chessGlyphs = map[ChessPieceType][2]string{
	ChessKing:   {"K", "k"},
	ChessQueen:  {"Q", "q"},
	ChessRook:   {"R", "r"},
	ChessBishop: {"B", "b"},
	ChessKnight: {"N", "n"},
	ChessPawn:   {"P", "p"},
}

// chessGlyphChar возвращает "сырую" (без цвета) букву фигуры —
// используется вместе с chessPieceForegroundColor, чтобы построить
// один комбинированный стиль (фон клетки + цвет фигуры) вместо
// вложенных вызовов Render(), которые ломаются в некоторых
// терминалах.
func chessGlyphChar(p *ChessPiece) string {
	if p == nil {
		return " "
	}
	pair := chessGlyphs[p.Type]
	if p.Color == ChessBlack {
		return pair[1]
	}
	return pair[0]
}

// chessPieceForegroundColor возвращает цвет буквы фигуры (второе
// значение — false, если клетка пуста).
func chessPieceForegroundColor(p *ChessPiece) (lipgloss.Color, bool) {
	if p == nil {
		return "", false
	}
	if p.Color == ChessWhite {
		return checkersWhitePieceColor, true
	}
	return checkersBlackPieceColor, true
}

func (m ChessModel) legalDestinationSet() map[ChessPos]bool {
	set := map[ChessPos]bool{}
	if m.selected == nil {
		return set
	}
	for _, opt := range m.game.LegalMovesFrom(*m.selected) {
		set[opt.To] = true
	}
	return set
}

func (m ChessModel) renderBoard() string {
	dests := m.legalDestinationSet()
	var b strings.Builder
	fmt.Fprintln(&b, "   a  b  c  d  e  f  g  h")
	for r := 0; r < 8; r++ {
		fmt.Fprintf(&b, "%d  ", 8-r)
		for c := 0; c < 8; c++ {
			pos := ChessPos{Row: r, Col: c}
			cellStyle := checkersLightSquare
			if (r+c)%2 == 1 {
				cellStyle = checkersDarkSquare
			}

			switch {
			case m.cursor == pos:
				cellStyle = checkersCursorStyle
			case m.selected != nil && *m.selected == pos:
				cellStyle = checkersSelStyle
			case dests[pos]:
				cellStyle = checkersSelStyle
			}

			piece := m.game.Board[r][c]
			text := chessGlyphChar(piece)
			combined := cellStyle
			if fg, ok := chessPieceForegroundColor(piece); ok {
				combined = cellStyle.Foreground(fg).Bold(true)
			}
			fmt.Fprint(&b, combined.Render(" "+text+" "))
		}
		fmt.Fprintln(&b)
	}
	return b.String()
}

func (m ChessModel) View() string {
	if m.quitting {
		return T("common.goodbye")
	}

	var b strings.Builder
	fmt.Fprintln(&b, titleStyle.Render(T("chess.title")))
	fmt.Fprintln(&b)
	fmt.Fprintln(&b, m.renderBoard())
	fmt.Fprintln(&b, dimStyle.Render(T("chess.legend")))
	fmt.Fprintln(&b)

	if m.game.Result != nil {
		res := m.game.Result
		switch {
		case res.Checkmate && res.Winner == m.human:
			fmt.Fprintln(&b, winStyle.Render(T("chess.result.win_checkmate")))
		case res.Checkmate:
			fmt.Fprintln(&b, errorStyle.Render(T("chess.result.lose_checkmate")))
		case res.Draw:
			fmt.Fprintln(&b, errorStyle.Render(Tf("chess.result.draw", T("chess.drawreason."+res.DrawReason))))
		}
		fmt.Fprintln(&b)
		fmt.Fprintln(&b, dimStyle.Render(T("chess.hint.gameover")))
		return b.String()
	}

	if m.game.inCheck(m.game.Turn) {
		fmt.Fprintln(&b, errorStyle.Render(T("chess.check")))
	}

	turnLabel := T("chess.turn.you")
	if m.game.Turn != m.human {
		turnLabel = T("chess.turn.bot")
	}
	fmt.Fprintln(&b, turnLabel)
	fmt.Fprintln(&b)

	if m.game.Turn == m.human {
		if m.selected == nil {
			fmt.Fprintln(&b, T("chess.hint.noselect"))
		} else {
			fmt.Fprintln(&b, T("chess.hint.selected"))
		}
	}

	fmt.Fprintln(&b)
	if m.message != "" {
		if m.isError {
			fmt.Fprintln(&b, errorStyle.Render("! "+m.message))
		} else {
			fmt.Fprintln(&b, infoStyle.Render(m.message))
		}
	}

	return b.String()
}
