package main

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

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

var (
	checkersDarkSquare      = lipgloss.NewStyle().Background(lipgloss.Color("236"))
	checkersLightSquare     = lipgloss.NewStyle().Background(lipgloss.Color("180"))
	checkersWhitePieceColor = lipgloss.Color("255")
	checkersBlackPieceColor = lipgloss.Color("124")
	checkersWhitePiece      = lipgloss.NewStyle().Foreground(checkersWhitePieceColor).Bold(true)
	checkersBlackPiece      = lipgloss.NewStyle().Foreground(checkersBlackPieceColor).Bold(true)
	checkersCursorStyle     = lipgloss.NewStyle().Background(lipgloss.Color("82")).Foreground(lipgloss.Color("16")).Bold(true)
	checkersSelStyle        = lipgloss.NewStyle().Background(lipgloss.Color("39")).Foreground(lipgloss.Color("16")).Bold(true)
)

// CheckersModel — TUI поверх CheckersGameState. Человек всегда
// играет белыми (ходят первыми), бот — чёрными.
type CheckersModel struct {
	game     *CheckersGameState
	bot      CheckersBot
	human    CheckersColor
	cursor   CheckersPos
	selected *CheckersPos

	message string
	isError bool

	quitting   bool
	backToMenu bool
}

// NewCheckersModel создаёт новую партию: человек играет белыми.
// depth — глубина поиска бота в полуходах (0 -> используется
// значение по умолчанию из CheckersBot).
func NewCheckersModel(depth int, colorChoice ColorChoice) CheckersModel {
	human := resolveCheckersColor(colorChoice)
	return CheckersModel{
		game:   NewCheckersGame(),
		bot:    CheckersBot{Depth: depth},
		human:  human,
		cursor: CheckersPos{Row: 5, Col: 0},
	}
}

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

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

func (m CheckersModel) 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 *CheckersModel) setInfo(format string, args ...interface{}) {
	m.message = fmt.Sprintf(format, args...)
	m.isError = false
}

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

func (m CheckersModel) 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 CheckersModel) 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("checkers.msg.bot_moved"))
	return m, m.maybeScheduleBot()
}

func (m CheckersModel) 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 = NewCheckersGame()
			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 CheckersModel) 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(ErrCheckersNoPiece)
			return m, nil
		}
		if len(m.game.LegalMovesFrom(m.cursor)) == 0 {
			m.setError(ErrCheckersInvalidMove)
			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("checkers.msg.moved"))
	if m.game.ChainPos != nil {
		pos := *m.game.ChainPos
		m.selected = &pos
		m.cursor = pos
	} else {
		m.selected = nil
	}
	return m, m.maybeScheduleBot()
}

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

// pieceGlyphChar возвращает "сырой" (без цвета) символ фигуры —
// используется вместе с pieceForegroundColor, чтобы построить один
// комбинированный стиль (фон клетки + цвет фигуры) вместо вложенных
// вызовов Render(), которые ломаются в некоторых терминалах (см.
// аналогичный баг, исправленный ранее в отрисовке карт Тонка).
func pieceGlyphChar(p *CheckersPiece) string {
	if p == nil {
		return " "
	}
	if p.King {
		return "O"
	}
	return "o"
}

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

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

func (m CheckersModel) 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 := CheckersPos{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 := pieceGlyphChar(piece)
			combined := cellStyle
			if fg, ok := pieceForegroundColor(piece); ok {
				combined = cellStyle.Foreground(fg).Bold(true)
			}
			fmt.Fprint(&b, combined.Render(" "+text+" "))
		}
		fmt.Fprintln(&b)
	}
	return b.String()
}

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

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

	if m.game.Result != nil {
		res := m.game.Result
		switch {
		case res.Draw:
			fmt.Fprintln(&b, errorStyle.Render(T("checkers.result.draw")))
		case res.Winner == m.human:
			fmt.Fprintln(&b, winStyle.Render(T("checkers.result.win")))
		default:
			fmt.Fprintln(&b, errorStyle.Render(T("checkers.result.lose")))
		}
		fmt.Fprintln(&b)
		fmt.Fprintln(&b, dimStyle.Render(T("checkers.hint.gameover")))
		return b.String()
	}

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

	if m.game.Turn == m.human {
		if m.selected == nil {
			fmt.Fprintln(&b, T("checkers.hint.noselect"))
		} else {
			fmt.Fprintln(&b, T("checkers.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()
}
