package main

import (
	"fmt"
	"strings"
	"time"

	tea "github.com/charmbracelet/bubbletea"
)

// BlackjackModel — TUI поверх BlackjackGameState. Действий у
// игрока немного и они не требуют выбора карты (в отличие от
// Тонка/Дурака) — только hit/stand/double, поэтому курсор по руке
// не нужен.
type BlackjackModel struct {
	game     *BlackjackGameState
	humanIdx int
	bot      BlackjackBot

	message string
	isError bool

	quitting   bool
	backToMenu bool
}

// NewBlackjackModel создаёт новую партию: человек — игрок 0, дальше
// numPlayers-1 ботов со случайными именами (тот же пул, что и в
// Тонке — это просто имена, без всякого смысла про уровень игры).
func NewBlackjackModel(numPlayers, startingCapital, bet int) BlackjackModel {
	if numPlayers < 1 {
		numPlayers = 1
	}
	if numPlayers > 6 {
		numPlayers = 6
	}
	names := []string{T("common.you")}
	if numPlayers > 1 {
		botNames, _ := newBotRoster(numPlayers - 1)
		names = append(names, botNames...)
	}

	return BlackjackModel{
		game:     NewBlackjackGame(names, 0, startingCapital, bet),
		humanIdx: 0,
		bot:      BlackjackBot{},
	}
}

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

func (m BlackjackModel) maybeScheduleBot() tea.Cmd {
	if m.game.Phase != BJPhasePlayerTurn {
		return nil
	}
	if m.game.CurrentPlayerIdx == m.humanIdx {
		return nil
	}
	return tea.Tick(botMoveDelay, func(time.Time) tea.Msg { return botMoveMsg{} })
}

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

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

func (m BlackjackModel) 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 BlackjackModel) handleBotMove() (tea.Model, tea.Cmd) {
	if m.game.Phase != BJPhasePlayerTurn {
		return m, nil
	}
	idx := m.game.CurrentPlayerIdx
	if idx == m.humanIdx {
		return m, nil
	}
	if err := m.bot.PlayFullTurn(m.game); err != nil {
		m.setError(fmt.Errorf(T("бот %s: %w"), m.game.Players[idx].Name, err))
		return m, nil
	}
	m.setInfo(T("tonk.msg.bot_moved"), m.game.Players[idx].Name)
	return m, m.maybeScheduleBot()
}

func (m BlackjackModel) 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 == BJPhaseRoundOver {
		if key == "n" && !m.humanIsBankrupt() {
			m.game = m.game.NextRound()
			m.message = ""
			m.isError = false
			if m.humanIdx >= 0 && m.humanIdx < len(m.game.RoundBetReduced) && m.game.RoundBetReduced[m.humanIdx] {
				if m.game.Players[m.humanIdx].Bet == 0 {
					m.setInfo(T("blackjack.msg.no_funds"))
				} else {
					m.setInfo(T("blackjack.msg.bet_reduced"), m.game.Players[m.humanIdx].Bet)
				}
			}
			return m, m.maybeScheduleBot()
		}
		return m, nil
	}

	if m.game.CurrentPlayerIdx != m.humanIdx {
		// сейчас ходит бот — игнорируем остальной ввод
		return m, nil
	}

	switch key {
	case "h":
		if err := m.game.Hit(); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("blackjack.msg.hit"))
		return m, m.maybeScheduleBot()
	case "s":
		if err := m.game.Stand(); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("blackjack.msg.stand"))
		return m, m.maybeScheduleBot()
	case "d":
		if err := m.game.DoubleDown(); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("blackjack.msg.double"))
		return m, m.maybeScheduleBot()
	}
	return m, nil
}

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

func renderHandCards(hand []Card) string {
	if len(hand) == 0 {
		return dimStyle.Render(T("common.no_cards"))
	}
	return renderCardBoxHand(hand, nil)
}

func statusLabel(s BlackjackStatus) string {
	switch s {
	case BJStatusStood:
		return T("blackjack.status.stood")
	case BJStatusBusted:
		return T("blackjack.status.busted")
	case BJStatusBlackjack:
		return T("blackjack.status.blackjack")
	default:
		return T("blackjack.status.playing")
	}
}

func (m BlackjackModel) renderPlayers() string {
	var b strings.Builder
	for i, p := range m.game.Players {
		marker := "  "
		isTurn := m.game.Phase == BJPhasePlayerTurn && i == m.game.CurrentPlayerIdx
		if isTurn {
			marker = "> "
		}
		total := handTotal(p.Hand)
		header := fmt.Sprintf(T("blackjack.player_header"), marker, p.Name, p.Bet, total, statusLabel(p.Status))
		if isTurn {
			header = turnStyle.Render(header)
		}
		fmt.Fprintln(&b, header)
		fmt.Fprintln(&b, renderHandCards(p.Hand))
		fmt.Fprintln(&b)
	}
	return strings.TrimRight(b.String(), "\n")
}

func (m BlackjackModel) renderDealer() string {
	if m.game.Phase == BJPhasePlayerTurn {
		// вторая карта дилера скрыта, пока не завершится раунд
		visible := renderCardBox(m.game.Dealer[0], CardNormal)
		hidden := renderFaceDownBox()
		lines := make([]string, 4)
		for i := 0; i < 4; i++ {
			lines[i] = visible[i] + " " + hidden[i]
		}
		return strings.Join(lines, "\n")
	}
	return Tf("blackjack.hand_with_total", renderHandCards(m.game.Dealer), handTotal(m.game.Dealer))
}

func (m BlackjackModel) humanIsBankrupt() bool {
	if m.humanIdx < 0 || m.humanIdx >= len(m.game.Players) {
		return false
	}
	return m.game.Players[m.humanIdx].Balance == 0
}

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

	var b strings.Builder
	fmt.Fprintln(&b, titleStyle.Render(T("blackjack.title")))
	fmt.Fprintln(&b)
	fmt.Fprintf(&b, T("blackjack.dealer_line"), m.renderDealer())
	fmt.Fprintln(&b, m.renderPlayers())
	fmt.Fprintln(&b)

	if m.game.Phase == BJPhaseRoundOver {
		fmt.Fprintln(&b, m.renderResult())
		fmt.Fprintln(&b)
		if m.humanIsBankrupt() {
			fmt.Fprintln(&b, errorStyle.Render(T("blackjack.bankrupt")))
			fmt.Fprintln(&b)
			fmt.Fprintln(&b, dimStyle.Render(T("blackjack.hint.bankrupt")))
		} else {
			fmt.Fprintln(&b, dimStyle.Render(T("blackjack.hint.gameover")))
		}
		return b.String()
	}

	if m.game.CurrentPlayerIdx == m.humanIdx {
		fmt.Fprintln(&b, T("blackjack.hint.turn"))
	} else {
		fmt.Fprintf(&b, T("common.thinking"), m.game.Players[m.game.CurrentPlayerIdx].Name)
	}

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

func (m BlackjackModel) renderResult() string {
	res := m.game.Result
	var b strings.Builder
	dealerLine := Tf("blackjack.hand_with_total", renderHandCards(m.game.Dealer), res.DealerTotal)
	if res.DealerBusted {
		dealerLine += T("blackjack.busted_suffix")
	}
	fmt.Fprintln(&b, dealerLine)
	fmt.Fprintln(&b)

	outcomeText := map[string]string{
		"blackjack": T("blackjack.outcome.blackjack"),
		"win":       T("blackjack.outcome.win"),
		"push":      T("blackjack.outcome.push"),
		"lose":      T("blackjack.outcome.lose"),
		"bust":      T("blackjack.outcome.bust"),
	}
	for i, p := range m.game.Players {
		r := res.PlayerResults[i]
		style := infoStyle
		if r.Delta < 0 {
			style = errorStyle
		} else if r.Delta > 0 {
			style = winStyle
		}
		fmt.Fprintln(&b, style.Render(fmt.Sprintf(T("blackjack.result_line"),
			p.Name, outcomeText[r.Outcome], r.Delta, p.Balance)))
	}
	return strings.TrimRight(b.String(), "\n")
}
