package main

import (
	"fmt"
	"strings"
	"time"

	tea "github.com/charmbracelet/bubbletea"
)

// ThousandModel — TUI поверх ThousandGameState.
type ThousandModel struct {
	game     *ThousandGameState
	humanIdx int
	bot      ThousandBot

	cursor    int
	bidAmount int
	message   string
	isError   bool

	quitting   bool
	backToMenu bool
}

// NewThousandModel создаёт новую партию 1000 на троих (человек +
// 2 бота со случайными именами).
func NewThousandModel() ThousandModel {
	botNames := newThousandBotNames(2)
	names := append([]string{T("common.you")}, botNames...)
	return ThousandModel{
		game:     NewThousandGame(names, 0),
		humanIdx: 0,
		bot:      ThousandBot{},
	}
}

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

func (m ThousandModel) actorForPhase() int {
	switch m.game.Phase {
	case ThousandPhaseBidding:
		return m.game.CurrentBidderIdx
	case ThousandPhaseDiscard:
		return m.game.DeclarerIdx
	case ThousandPhaseTrick:
		return m.game.currentTrickActor()
	}
	return -1
}

func (m ThousandModel) maybeScheduleBot() tea.Cmd {
	if m.game.Phase == ThousandPhaseHandOver {
		return nil
	}
	if m.actorForPhase() == m.humanIdx {
		return nil
	}
	return tea.Tick(botMoveDelay, func(time.Time) tea.Msg { return botMoveMsg{} })
}

func (m *ThousandModel) resetSelection() {
	hand := m.currentHand()
	if len(hand) == 0 {
		m.cursor = 0
	} else if m.cursor >= len(hand) {
		m.cursor = len(hand) - 1
	}
}

func (m ThousandModel) currentHand() []Card {
	idx := m.actorForPhase()
	if idx < 0 {
		return nil
	}
	return m.game.Players[idx].Hand
}

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

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

func (m ThousandModel) 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 ThousandModel) handleBotMove() (tea.Model, tea.Cmd) {
	if m.game.Phase == ThousandPhaseHandOver {
		return m, nil
	}
	actor := m.actorForPhase()
	if actor == m.humanIdx {
		return m, nil
	}
	if err := m.bot.PlayFullTurn(m.game); err != nil {
		m.setError(fmt.Errorf(T("бот %s: %w"), m.game.Players[actor].Name, err))
		return m, nil
	}
	m.setInfo(T("tonk.msg.bot_moved"), m.game.Players[actor].Name)
	if m.game.Phase != ThousandPhaseHandOver && m.actorForPhase() == m.humanIdx {
		m.resetSelection()
		m.bidAmount = m.game.HighBid + 5
		if m.bidAmount < 100 {
			m.bidAmount = 100
		}
	}
	return m, m.maybeScheduleBot()
}

func (m ThousandModel) 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 == ThousandPhaseHandOver {
		if key == "n" && !m.game.Result.MatchOver {
			m.game.NextHand()
			m.message = ""
			m.isError = false
			m.resetSelection()
			return m, m.maybeScheduleBot()
		}
		return m, nil
	}

	if m.actorForPhase() != m.humanIdx {
		return m, nil
	}

	switch m.game.Phase {
	case ThousandPhaseBidding:
		return m.handleBiddingKey(key)
	case ThousandPhaseDiscard:
		return m.handleDiscardKey(key)
	case ThousandPhaseTrick:
		return m.handleTrickKey(key)
	}
	return m, nil
}

func (m ThousandModel) handleBiddingKey(key string) (tea.Model, tea.Cmd) {
	if m.bidAmount < 100 {
		m.bidAmount = 100
	}
	switch key {
	case "up", "right", "l", "k":
		m.bidAmount += 5
	case "down", "left", "h", "j":
		if m.bidAmount > 100 {
			m.bidAmount -= 5
		}
	case "enter":
		if err := m.game.Bid(m.bidAmount); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("thousand.msg.bid"), m.bidAmount)
		if m.game.Phase != ThousandPhaseHandOver {
			m.bidAmount = m.game.HighBid + 5
		}
		return m, m.maybeScheduleBot()
	case "p":
		if err := m.game.Pass(); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("thousand.msg.passed"))
		return m, m.maybeScheduleBot()
	case "v":
		if err := m.game.DeclareBlind(); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("thousand.msg.blind_declared"))
		return m, m.maybeScheduleBot()
	}
	return m, nil
}

func (m ThousandModel) handleDiscardKey(key string) (tea.Model, tea.Cmd) {
	hand := m.currentHand()
	switch key {
	case "left", "h":
		if m.cursor > 0 {
			m.cursor--
		}
	case "right", "l":
		if m.cursor < len(hand)-1 {
			m.cursor++
		}
	case "enter", " ":
		if len(hand) == 0 {
			return m, nil
		}
		card := hand[m.cursor]
		if err := m.game.DiscardCard(card); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("thousand.msg.gave_card"), card)
		m.resetSelection()
		if m.game.Phase != ThousandPhaseDiscard {
			return m, m.maybeScheduleBot()
		}
	}
	return m, nil
}

func (m ThousandModel) handleTrickKey(key string) (tea.Model, tea.Cmd) {
	hand := m.currentHand()
	switch key {
	case "left", "h":
		if m.cursor > 0 {
			m.cursor--
		}
	case "right", "l":
		if m.cursor < len(hand)-1 {
			m.cursor++
		}
	case "enter", " ":
		if len(hand) == 0 {
			return m, nil
		}
		card := hand[m.cursor]
		if err := m.game.PlayCard(card); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("thousand.msg.played"), card)
		if m.game.Phase != ThousandPhaseHandOver {
			m.resetSelection()
		}
		return m, m.maybeScheduleBot()
	}
	return m, nil
}

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

func (m ThousandModel) renderHand() string {
	hand := m.currentHand()
	if len(hand) == 0 {
		return dimStyle.Render(T("common.no_cards"))
	}
	return renderCardBoxHand(hand, cursorHighlights(len(hand), m.cursor))
}

func (m ThousandModel) renderPlayers() string {
	var b strings.Builder
	for i, p := range m.game.Players {
		marker := "  "
		if m.game.Phase != ThousandPhaseHandOver && i == m.actorForPhase() {
			marker = "> "
		}
		name := p.Name
		role := ""
		if m.game.Phase != ThousandPhaseBidding && i == m.game.DeclarerIdx {
			role = T("thousand.declarer_tag")
		}
		if p.OnBarrel {
			role += " " + Tf("thousand.barrel_tag", p.BarrelAttempts, ThousandBarrelMaxAttempts)
		}
		line := fmt.Sprintf(T("thousand.player_line"),
			marker, name, len(p.Hand), p.HandPoints, p.Score, role)
		if m.game.Phase != ThousandPhaseHandOver && i == m.actorForPhase() {
			line = turnStyle.Render(line)
		}
		fmt.Fprintln(&b, line)
	}
	return strings.TrimRight(b.String(), "\n")
}

func (m ThousandModel) renderTrick() string {
	if len(m.game.CurrentTrick) == 0 {
		return dimStyle.Render(T("thousand.trick_not_started"))
	}
	parts := make([]string, len(m.game.CurrentTrick))
	for i, play := range m.game.CurrentTrick {
		parts[i] = fmt.Sprintf("%s:%s", m.game.Players[play.PlayerIdx].Name, renderCard(play.Card))
	}
	return strings.Join(parts, "  ")
}

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

	var b strings.Builder
	fmt.Fprintln(&b, titleStyle.Render(T("thousand.title")))
	fmt.Fprintln(&b)
	if m.game.HandNumber <= ThousandGoldenHands {
		fmt.Fprintln(&b, dimStyle.Render(T("thousand.golden_round_tag")))
	}
	if m.game.PlayedBlind {
		fmt.Fprintln(&b, dimStyle.Render(T("thousand.blind_round_tag")))
	}
	fmt.Fprintln(&b, m.renderPlayers())
	fmt.Fprintln(&b)

	if m.game.Phase == ThousandPhaseHandOver {
		fmt.Fprintln(&b, m.renderResult())
		fmt.Fprintln(&b)
		if m.game.Result.MatchOver {
			fmt.Fprintln(&b, dimStyle.Render(T("thousand.hint.gameover_match")))
		} else {
			fmt.Fprintln(&b, dimStyle.Render(T("thousand.hint.gameover")))
		}
		return b.String()
	}

	switch m.game.Phase {
	case ThousandPhaseBidding:
		fmt.Fprintf(&b, T("thousand.bidding_status"), m.game.HighBid, m.bidderName())
	case ThousandPhaseDiscard, ThousandPhaseTrick:
		trumpStr := T("thousand.trump_unset")
		if m.game.TrumpSet {
			trumpStr = suitName(m.game.TrumpSuit)
		}
		fmt.Fprintf(&b, T("thousand.play_status"), m.game.FinalBid, m.game.Players[m.game.DeclarerIdx].Name, trumpStr)
		if m.game.Phase == ThousandPhaseTrick {
			fmt.Fprintln(&b, T("thousand.trick_label"), m.renderTrick())
		}
		fmt.Fprintln(&b)
	}

	if m.actorForPhase() == m.humanIdx {
		fmt.Fprintln(&b, T("thousand.your_hand"))
		fmt.Fprintln(&b, m.renderHand())
		fmt.Fprintln(&b)

		switch m.game.Phase {
		case ThousandPhaseBidding:
			fmt.Fprintf(&b, T("thousand.hint.bid"), m.bidAmount)
		case ThousandPhaseDiscard:
			fmt.Fprintln(&b, T("thousand.hint.discard"))
		case ThousandPhaseTrick:
			fmt.Fprintln(&b, T("thousand.hint.trick"))
		}
		fmt.Fprintln(&b, dimStyle.Render(T("thousand.hint.menu")))
	} else {
		fmt.Fprintf(&b, T("common.thinking"), m.game.Players[m.actorForPhase()].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 ThousandModel) bidderName() string {
	if m.game.HighBidderIdx < 0 {
		return T("thousand.no_bidder")
	}
	return m.game.Players[m.game.HighBidderIdx].Name
}

func suitName(s Suit) string {
	switch s {
	case Clubs:
		return T("thousand.suit.clubs")
	case Diamonds:
		return T("thousand.suit.diamonds")
	case Hearts:
		return T("thousand.suit.hearts")
	case Spades:
		return T("thousand.suit.spades")
	}
	return "?"
}

func (m ThousandModel) renderResult() string {
	res := m.game.Result
	var b strings.Builder
	declarerName := m.game.Players[res.DeclarerIdx].Name
	if res.Made {
		fmt.Fprintln(&b, winStyle.Render(Tf("thousand.result.made", declarerName, res.FinalBid)))
	} else {
		fmt.Fprintln(&b, errorStyle.Render(Tf("thousand.result.failed", declarerName, res.FinalBid)))
	}
	fmt.Fprintln(&b)
	for i, p := range m.game.Players {
		fmt.Fprintln(&b, fmt.Sprintf(T("thousand.result.delta_line"), p.Name, res.ScoreDeltas[i], p.Score))
	}
	if res.MatchOver {
		fmt.Fprintln(&b)
		fmt.Fprintln(&b, winStyle.Render(Tf("thousand.result.match_over", m.game.Players[res.MatchWinner].Name)))
	}
	return strings.TrimRight(b.String(), "\n")
}
