package main

import (
	"fmt"
	"strings"
	"time"

	tea "github.com/charmbracelet/bubbletea"
)

// DurakModel — TUI поверх DurakGameState. Действующее лицо всегда
// одно: атакующий (обязательный первый ход раунда), защищающийся
// (отбить/забрать) либо очередной подкидывающий (подкинуть/пасовать
// в фазе DurakPhaseThrowIn) — определяется через currentActor().
type DurakModel struct {
	game     *DurakGameState
	humanIdx int
	bot      DurakBot

	cursor  int
	message string
	isError bool

	quitting   bool
	backToMenu bool
}

// NewDurakModel создаёт новую партию Дурака на numPlayers игроков
// (2-6, включая человека) со случайными именами ботов.
func NewDurakModel(numPlayers int) DurakModel {
	if numPlayers < 2 {
		numPlayers = 2
	}
	if numPlayers > 6 {
		numPlayers = 6
	}
	botNames := newDurakBotNames(numPlayers - 1)
	names := append([]string{T("common.you")}, botNames...)

	return DurakModel{
		game:     NewDurakGame(names, 0),
		humanIdx: 0,
		bot:      DurakBot{},
	}
}

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

// currentActor возвращает индекс игрока, который должен действовать
// прямо сейчас: атакующий в фазе первого хода, защищающийся в фазе
// защиты, очередной подкидывающий в фазе подкидывания.
func (m DurakModel) currentActor() int {
	switch m.game.Phase {
	case DurakPhaseAttack:
		return m.game.AttackerIdx
	case DurakPhaseDefend:
		return m.game.DefenderIdx
	case DurakPhaseThrowIn:
		return m.game.CurrentThrower()
	}
	return -1
}

func (m DurakModel) maybeScheduleBot() tea.Cmd {
	// В фазе подкидывания человеку, у которого нет ни одной карты
	// подходящего ранга (в том числе если рука вообще пуста), нечего
	// решать — пасуем за него автоматически, точно так же, как это
	// уже делают боты через DecideThrowOrPass. Цикл сам завершится:
	// либо очередь дойдёт до кого-то другого, либо резолвится отбой.
	for m.game.Phase == DurakPhaseThrowIn &&
		m.currentActor() == m.humanIdx &&
		!m.game.hasThrowableCard(m.humanIdx) {
		if err := m.game.Pass(); err != nil {
			break
		}
	}

	if m.game.Phase == DurakPhaseGameOver {
		return nil
	}
	if m.currentActor() == m.humanIdx {
		return nil
	}
	return tea.Tick(botMoveDelay, func(time.Time) tea.Msg { return botMoveMsg{} })
}

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

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

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

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

func (m DurakModel) 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 DurakModel) handleBotMove() (tea.Model, tea.Cmd) {
	if m.game.Phase == DurakPhaseGameOver {
		return m, nil
	}
	actor := m.currentActor()
	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 != DurakPhaseGameOver && m.currentActor() == m.humanIdx {
		m.resetSelection()
	}
	return m, m.maybeScheduleBot()
}

func (m DurakModel) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
	key := msg.String()

	if key == "ctrl+c" {
		m.quitting = true
		return m, tea.Quit
	}

	if m.game.Phase == DurakPhaseGameOver {
		switch key {
		case "n":
			numPlayers := len(m.game.Players)
			m = NewDurakModel(numPlayers)
			return m, m.maybeScheduleBot()
		case "q":
			m.backToMenu = true
			return m, nil
		}
		return m, nil
	}

	if key == "q" {
		m.backToMenu = true
		return m, nil
	}

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

	switch m.game.Phase {
	case DurakPhaseAttack:
		return m.handleAttackKey(key)
	case DurakPhaseDefend:
		return m.handleDefendKey(key)
	case DurakPhaseThrowIn:
		return m.handleThrowInKey(key)
	}
	return m, nil
}

func (m DurakModel) handleAttackKey(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.ThrowIn(card); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("durak.msg.threw_in"), card)
		if m.game.Phase != DurakPhaseGameOver {
			m.resetSelection()
		}
		return m, m.maybeScheduleBot()
	}
	return m, nil
}

func (m DurakModel) handleThrowInKey(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.ThrowIn(card); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("durak.msg.threw_in"), card)
		if m.game.Phase != DurakPhaseGameOver {
			m.resetSelection()
		}
		return m, m.maybeScheduleBot()
	case "p":
		if err := m.game.Pass(); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("durak.msg.passed"))
		if m.game.Phase != DurakPhaseGameOver {
			m.resetSelection()
		}
		return m, m.maybeScheduleBot()
	}
	return m, nil
}

func (m DurakModel) handleDefendKey(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.Defend(card); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("durak.msg.defended"), card)
		if m.game.Phase != DurakPhaseGameOver {
			m.resetSelection()
		}
		return m, m.maybeScheduleBot()
	case "t":
		if err := m.game.Take(); err != nil {
			m.setError(err)
			return m, nil
		}
		m.setInfo(T("durak.msg.took"))
		if m.game.Phase != DurakPhaseGameOver {
			m.resetSelection()
		}
		return m, m.maybeScheduleBot()
	}
	return m, nil
}

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

func (m DurakModel) 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 DurakModel) renderTable() string {
	if len(m.game.Table) == 0 {
		return dimStyle.Render(T("tonk.table_empty"))
	}
	parts := make([]string, len(m.game.Table))
	for i, pair := range m.game.Table {
		if pair.Defend != nil {
			parts[i] = fmt.Sprintf("%s/%s", renderCard(pair.Attack), renderCard(*pair.Defend))
		} else {
			parts[i] = fmt.Sprintf("%s/__", renderCard(pair.Attack))
		}
	}
	return strings.Join(parts, "  ")
}

func (m DurakModel) renderPlayers() string {
	var b strings.Builder
	for i, p := range m.game.Players {
		marker := "  "
		switch {
		case m.game.Phase == DurakPhaseThrowIn && i == m.game.CurrentThrower():
			marker = "T "
		case i == m.game.AttackerIdx && m.game.Phase != DurakPhaseGameOver:
			marker = "A "
		case i == m.game.DefenderIdx && m.game.Phase != DurakPhaseGameOver:
			marker = "D "
		}
		name := p.Name
		status := ""
		if p.Out {
			status = T("durak.status_out")
		}
		line := fmt.Sprintf(T("durak.player_line"), marker, name, len(p.Hand), status)
		if i == m.game.AttackerIdx || i == m.game.DefenderIdx || (m.game.Phase == DurakPhaseThrowIn && i == m.game.CurrentThrower()) {
			line = turnStyle.Render(line)
		}
		fmt.Fprintln(&b, line)
	}
	return strings.TrimRight(b.String(), "\n")
}

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

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

	trumpStr := renderCard(m.game.TrumpCard)
	fmt.Fprintf(&b, T("durak.status_line"), trumpStr, len(m.game.Talon))

	fmt.Fprintln(&b, T("durak.table_label"))
	fmt.Fprintln(&b, m.renderTable())
	fmt.Fprintln(&b)

	if m.game.Phase == DurakPhaseGameOver {
		fmt.Fprintln(&b, m.renderResult())
		fmt.Fprintln(&b)
		fmt.Fprintln(&b, dimStyle.Render(T("durak.hint.gameover")))
		return b.String()
	}

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

		switch m.game.Phase {
		case DurakPhaseAttack:
			fmt.Fprintln(&b, T("durak.hint.attack"))
		case DurakPhaseDefend:
			fmt.Fprintln(&b, T("durak.hint.defend"))
		case DurakPhaseThrowIn:
			fmt.Fprintln(&b, T("durak.hint.throwin"))
		}
	} else {
		fmt.Fprintf(&b, T("common.thinking"), m.game.Players[m.currentActor()].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 DurakModel) renderResult() string {
	res := m.game.Result
	if res.Stalemate {
		return errorStyle.Render(T("durak.result.stalemate"))
	}
	if res.NoLoser {
		return winStyle.Render(T("durak.result.noloser"))
	}
	loserName := m.game.Players[res.Loser].Name
	if res.Loser == m.humanIdx {
		return errorStyle.Render(Tf("durak.result.loser", loserName))
	}
	return winStyle.Render(Tf("durak.result.loser", loserName))
}
