package main

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

	tea "github.com/charmbracelet/bubbletea"
)

// TicTacToeModel — TUI поверх TicTacToeGameState.
type TicTacToeModel struct {
	game    *TicTacToeGameState
	bot     TicTacToeBot
	rng     *rand.Rand
	cursor  int
	message string
	isError bool

	quitting   bool
	backToMenu bool
}

// NewTicTacToeModel начинает новую партию. Каким знаком играет
// человек — решается случайно (50/50): если человеку достаются
// нолики, первым ходом автоматически становится ход бота (крестики
// всегда идут первыми).
func NewTicTacToeModel(difficulty TicTacToeDifficulty) TicTacToeModel {
	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
	human := TicTacToeX
	if rng.Intn(2) == 0 {
		human = TicTacToeO
	}
	return TicTacToeModel{
		game:   NewTicTacToeGame(human),
		bot:    TicTacToeBot{Difficulty: difficulty},
		rng:    rng,
		cursor: 4,
	}
}

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

func (m TicTacToeModel) maybeScheduleBot() tea.Cmd {
	if m.game.Phase != TicTacToePhasePlay || m.game.CurrentTurn == m.game.HumanMark {
		return nil
	}
	return tea.Tick(botMoveDelay, func(time.Time) tea.Msg { return botMoveMsg{} })
}

func (m *TicTacToeModel) setInfo(key string) {
	m.message = T(key)
	m.isError = false
}

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

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

func (m TicTacToeModel) handleBotMove() (tea.Model, tea.Cmd) {
	if m.game.Phase != TicTacToePhasePlay || m.game.CurrentTurn == m.game.HumanMark {
		return m, nil
	}
	if err := m.bot.PlayFullTurn(m.game, m.rng); err != nil {
		m.setError(err)
		return m, nil
	}
	m.setInfo("tictactoe.msg.bot_moved")
	return m, m.maybeScheduleBot()
}

func (m TicTacToeModel) 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.Phase == TicTacToePhaseOver {
		if key == "n" {
			next := NewTicTacToeModel(m.bot.Difficulty)
			return next, next.Init()
		}
		return m, nil
	}

	if m.game.CurrentTurn != m.game.HumanMark {
		return m, nil
	}

	switch key {
	case "up", "k":
		if m.cursor >= 3 {
			m.cursor -= 3
		}
	case "down", "j":
		if m.cursor < 6 {
			m.cursor += 3
		}
	case "left", "h":
		if m.cursor%3 != 0 {
			m.cursor--
		}
	case "right", "l":
		if m.cursor%3 != 2 {
			m.cursor++
		}
	case "enter", " ":
		return m.place(m.cursor)
	case "1", "2", "3", "4", "5", "6", "7", "8", "9":
		pos := int(key[0] - '1')
		m.cursor = pos
		return m.place(pos)
	}
	return m, nil
}

func (m TicTacToeModel) place(pos int) (tea.Model, tea.Cmd) {
	if err := m.game.PlaceMark(pos); err != nil {
		m.setError(err)
		return m, nil
	}
	m.message = ""
	m.isError = false
	return m, m.maybeScheduleBot()
}

func ticTacToeMarkGlyph(mark TicTacToeMark) string {
	switch mark {
	case TicTacToeX:
		return "X"
	case TicTacToeO:
		return "O"
	default:
		return " "
	}
}

func (m TicTacToeModel) View() string {
	if m.quitting {
		return T("common.goodbye")
	}
	var b strings.Builder
	fmt.Fprintln(&b, titleStyle.Render(T("tictactoe.title")))
	fmt.Fprintln(&b)
	fmt.Fprintln(&b, Tf("tictactoe.your_mark", ticTacToeMarkGlyph(m.game.HumanMark)))
	fmt.Fprintln(&b)

	winSet := map[int]bool{}
	if m.game.Result != nil && !m.game.Result.Draw {
		for _, idx := range m.game.Result.Line {
			winSet[idx] = true
		}
	}

	for row := 0; row < 3; row++ {
		cells := make([]string, 3)
		for col := 0; col < 3; col++ {
			idx := row*3 + col
			cells[col] = m.renderCell(idx, winSet[idx])
		}
		fmt.Fprintln(&b, " "+strings.Join(cells, " │ ")+" ")
		if row < 2 {
			fmt.Fprintln(&b, "───┼───┼───")
		}
	}
	fmt.Fprintln(&b)

	switch {
	case m.game.Phase == TicTacToePhaseOver && m.game.Result.Draw:
		fmt.Fprintln(&b, infoStyle.Render(T("tictactoe.outcome.draw")))
	case m.game.Phase == TicTacToePhaseOver && m.game.Result.Winner == m.game.HumanMark:
		fmt.Fprintln(&b, winStyle.Render(T("tictactoe.outcome.win")))
	case m.game.Phase == TicTacToePhaseOver:
		fmt.Fprintln(&b, errorStyle.Render(T("tictactoe.outcome.lose")))
	case m.game.CurrentTurn == m.game.HumanMark:
		fmt.Fprintln(&b, T("tictactoe.hint.turn"))
	default:
		fmt.Fprintln(&b, T("common.thinking_generic"))
	}

	if m.message != "" {
		if m.isError {
			fmt.Fprintln(&b, errorStyle.Render("! "+m.message))
		} else {
			fmt.Fprintln(&b, infoStyle.Render(m.message))
		}
	}
	fmt.Fprintln(&b)
	if m.game.Phase == TicTacToePhaseOver {
		fmt.Fprintln(&b, dimStyle.Render(T("tictactoe.hint.over")))
	} else {
		fmt.Fprintln(&b, dimStyle.Render(T("tictactoe.hint.play")))
	}
	return b.String()
}

// renderCell отрисовывает одну клетку доски: цифра-подсказка (1-9),
// если пусто, X/O если занято, с подсветкой курсора (пока пусто и
// сейчас ход человека) или выигрышной линии.
func (m TicTacToeModel) renderCell(idx int, isWinCell bool) string {
	mark := m.game.Board[idx]
	if mark == TicTacToeEmpty {
		text := fmt.Sprintf("%d", idx+1)
		if m.game.Phase == TicTacToePhasePlay && m.game.CurrentTurn == m.game.HumanMark && idx == m.cursor {
			return cursorStyle.Render(text)
		}
		return dimStyle.Render(text)
	}
	text := ticTacToeMarkGlyph(mark)
	if isWinCell {
		return winStyle.Render(text)
	}
	return text
}
