package main

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

	tea "github.com/charmbracelet/bubbletea"
)

// goColumnLetters — буквы столбцов доски Го: традиционно пропускают
// "I", чтобы не путать с "1".
const goColumnLetters = "ABCDEFGHJKLMNOPQRST"

// GoModel — TUI поверх GoGameState. Человек всегда играет чёрными
// (ходят первыми), бот — белыми.
type GoModel struct {
	game       *GoGameState
	bot        GoBot
	rnd        *rand.Rand
	human      GoColor
	cursor     GoPos
	size       int
	difficulty GoDifficulty

	message string
	isError bool

	quitting   bool
	backToMenu bool
}

// NewGoModel создаёт новую партию на доске size x size с ботом
// уровня difficulty. Человек играет чёрными.
func NewGoModel(size int, difficulty GoDifficulty, colorChoice ColorChoice) GoModel {
	human := resolveGoColor(colorChoice)
	return GoModel{
		game:       NewGoGame(size),
		bot:        GoBot{Difficulty: difficulty},
		rnd:        rand.New(rand.NewSource(time.Now().UnixNano())),
		human:      human,
		cursor:     GoPos{Row: size / 2, Col: size / 2},
		size:       size,
		difficulty: difficulty,
	}
}

// resolveGoColor переводит выбор цвета из настроек в конкретный
// GoColor. ColorChoiceA — чёрные (традиционно первый ход в Го,
// соответствует прежнему поведению по умолчанию), ColorChoiceB —
// белые.
func resolveGoColor(choice ColorChoice) GoColor {
	switch choice {
	case ColorChoiceA:
		return GoBlack
	case ColorChoiceB:
		return GoWhite
	default:
		rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
		if rnd.Intn(2) == 0 {
			return GoBlack
		}
		return GoWhite
	}
}

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

func (m GoModel) 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 *GoModel) setInfo(key string) {
	m.message = T(key)
	m.isError = false
}

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

func (m GoModel) 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 GoModel) handleBotMove() (tea.Model, tea.Cmd) {
	if m.game.Result != nil || m.game.Turn == m.human {
		return m, nil
	}
	passed, err := m.bot.PlayFullTurn(m.game, m.rnd)
	if err != nil {
		m.setError(err)
		return m, nil
	}
	if passed {
		m.setInfo("go.msg.bot_passed")
	} else {
		m.setInfo("go.msg.bot_moved")
	}
	return m, m.maybeScheduleBot()
}

func (m GoModel) 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 = NewGoGame(m.size)
			m.cursor = GoPos{Row: m.size / 2, Col: m.size / 2}
			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 < m.game.Size-1 {
			m.cursor.Row++
		}
	case "left", "h":
		if m.cursor.Col > 0 {
			m.cursor.Col--
		}
	case "right", "l":
		if m.cursor.Col < m.game.Size-1 {
			m.cursor.Col++
		}
	case "enter", " ":
		if err := m.game.Play(m.cursor); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo("go.msg.played")
		return m, m.maybeScheduleBot()
	case "p":
		if err := m.game.Pass(); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo("go.msg.passed")
		return m, m.maybeScheduleBot()
	}
	return m, nil
}

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

func goStoneGlyph(c GoColor) string {
	switch c {
	case GoBlack:
		return checkersBlackPiece.Render("●")
	case GoWhite:
		return checkersWhitePiece.Render("○")
	}
	return dimStyle.Render("·")
}

func goStoneSymbolPlain(c GoColor) string {
	switch c {
	case GoBlack:
		return "●"
	case GoWhite:
		return "○"
	}
	return "·"
}

func (m GoModel) renderBoard() string {
	size := m.game.Size
	var b strings.Builder

	fmt.Fprint(&b, "   ")
	for c := 0; c < size; c++ {
		fmt.Fprintf(&b, "%c ", goColumnLetters[c])
	}
	fmt.Fprintln(&b)

	for r := 0; r < size; r++ {
		label := size - r
		fmt.Fprintf(&b, "%2d ", label)
		for c := 0; c < size; c++ {
			pos := GoPos{Row: r, Col: c}
			glyph := goStoneGlyph(m.game.Board[r][c])
			if pos == m.cursor && m.game.Result == nil {
				glyph = cursorStyle.Render(goStoneSymbolPlain(m.game.Board[r][c]))
			}
			fmt.Fprint(&b, glyph+" ")
		}
		fmt.Fprintln(&b)
	}
	return b.String()
}

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

	g := m.game
	var b strings.Builder

	fmt.Fprintln(&b, titleStyle.Render(T("go.title")))
	fmt.Fprintln(&b)
	fmt.Fprintln(&b, Tf("go.header.captures", g.CapturedByBlack, g.CapturedByWhite))
	fmt.Fprintln(&b)
	fmt.Fprintln(&b, m.renderBoard())
	fmt.Fprintln(&b)

	if g.Result != nil {
		res := g.Result
		winnerKey := "go.color.black"
		if res.Winner == GoWhite {
			winnerKey = "go.color.white"
		}
		fmt.Fprintln(&b, winStyle.Render(Tf("go.result.line", T(winnerKey), res.BlackScore, res.WhiteScore)))
		fmt.Fprintln(&b)
		fmt.Fprintln(&b, dimStyle.Render(T("go.hint.gameover")))
		return b.String()
	}
	turnLabel := T("go.turn.you")
	if g.Turn != m.human {
		turnLabel = T("go.turn.bot")
	}
	fmt.Fprintln(&b, turnLabel)
	fmt.Fprintln(&b)

	if g.Turn == m.human {
		fmt.Fprintln(&b, T("go.hint.play"))
	}

	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()
}
