package game

import "errors"

// Phase مرحله بازی.
type Phase int

const (
	PhaseChooseTrump Phase = iota // حاکم باید حکم بزند
	PhasePlaying                  // در حال بازی دست‌ها (tricks)
	PhaseHandOver                 // یک هَند تمام شد
	PhaseGameOver                 // بازی تمام شد
)

func (p Phase) String() string {
	switch p {
	case PhaseChooseTrump:
		return "choose_trump"
	case PhasePlaying:
		return "playing"
	case PhaseHandOver:
		return "hand_over"
	case PhaseGameOver:
		return "game_over"
	}
	return "unknown"
}

// خطاهای موتور.
var (
	ErrWrongPhase   = errors.New("wrong phase")
	ErrNotYourTurn  = errors.New("not your turn")
	ErrNotHakem     = errors.New("only hakem can choose trump")
	ErrCardNotHeld  = errors.New("card not in hand")
	ErrMustFollow   = errors.New("must follow lead suit")
	ErrInvalidSeat  = errors.New("invalid seat")
)

// TrickCard یک کارت انداخته‌شده روی زمین به‌همراه جایگاه بازیکن.
type TrickCard struct {
	Seat int  `json:"seat"`
	Card Card `json:"card"`
}

// HandResult نتیجه یک هَند.
type HandResult struct {
	WinnerTeam int  `json:"winner_team"`
	Kot        bool `json:"kot"`       // کُت: تیم بازنده هیچ دستی نبرد
	HakemKot   bool `json:"hakem_kot"` // حاکم‌کُت: تیمِ حاکم کُت شد (۳ امتیاز)
	Points     int  `json:"points"`    // امتیاز این هَند (۱ معمولی، ۲ کُت، ۳ حاکم‌کُت)
	Hakem      int  `json:"hakem"`     // حاکمِ همین هَند (پیش از چرخش)
	NextHakem  int  `json:"next_hakem"`
}

// Team تیم یک جایگاه را برمی‌گرداند (0&2 تیم۰، 1&3 تیم۱).
func Team(seat int) int { return seat % 2 }

// Game وضعیت کامل یک میز حکم؛ فقط در حافظه نگه‌داری می‌شود.
type Game struct {
	Phase       Phase
	Hakem       int      // جایگاه حاکم
	Trump       Suit     // خال حکم
	TrumpChosen bool     // آیا حکم انتخاب شده

	hands [4][]Card // کارت هر بازیکن (خصوصی)
	deck  []Card    // باقیمانده دسته برای پخش پس از انتخاب حکم

	Turn          int         // جایگاه نوبت فعلی
	LeadSuit      Suit        // خال شروع‌کننده دست جاری
	leadSet       bool        // آیا خال زمینه تعیین شده
	Trick         []TrickCard // کارت‌های دست جاری
	TricksWon     [2]int      // تعداد دست‌های برده هر تیم در هَند جاری
	TrickDone     bool        // دستِ کامل (۴ کارت) منتظر جمع‌آوری
	pendingWinner int         // برنده‌ی دستِ کاملِ منتظر

	Scores      [2]int // امتیاز کلی (تعداد هَند برده)
	TargetScore int    // امتیاز لازم برای برد بازی

	LastResult *HandResult // نتیجه آخرین هَند (در PhaseHandOver/GameOver)
}

// NewGame یک میز با حاکم اولیه مشخص می‌سازد و اولین هَند را شروع می‌کند.
// hakem را می‌توان با FirstHakem از روی یک دسته تعیین کرد.
func NewGame(targetScore, hakem int) *Game {
	g := &Game{
		Hakem:       hakem,
		TargetScore: targetScore,
	}
	g.startHand()
	return g
}

// FirstHakem با پخش کارت دور میز تا اولین آس، جایگاه حاکم اول را تعیین می‌کند.
func FirstHakem() int {
	d := NewDeck()
	Shuffle(d)
	for i, c := range d {
		if c.Rank == Ace {
			return i % 4
		}
	}
	return 0
}

// startHand دسته را بُر زده، ۵ کارت اول را به حاکم می‌دهد و منتظر انتخاب حکم می‌ماند.
func (g *Game) startHand() {
	g.deck = NewDeck()
	Shuffle(g.deck)

	for i := range g.hands {
		g.hands[i] = nil
	}
	// حاکم ۵ کارت اول را می‌گیرد و بر اساس آن حکم را انتخاب می‌کند.
	g.hands[g.Hakem] = append(g.hands[g.Hakem], g.deck[:5]...)

	g.Phase = PhaseChooseTrump
	g.Trump = 0
	g.TrumpChosen = false
	g.Turn = g.Hakem
	g.LeadSuit = 0
	g.leadSet = false
	g.Trick = nil
	g.TricksWon = [2]int{}
	g.TrickDone = false
}

// Reshuffle دستِ جاری را پیش از انتخابِ حکم دوباره بُر می‌زند و ۵ کارتِ تازه به
// همان حاکم می‌دهد (مصرفِ یک بلیت در لایه‌ی بالاتر انجام می‌شود).
func (g *Game) Reshuffle(seat int) error {
	if g.Phase != PhaseChooseTrump {
		return ErrWrongPhase
	}
	if seat != g.Hakem {
		return ErrNotHakem
	}
	g.startHand() // همان حاکم، دستِ نو، باز هم منتظرِ انتخابِ حکم
	return nil
}

// ChooseTrump حکم را توسط حاکم تعیین کرده و بقیه کارت‌ها را پخش می‌کند.
func (g *Game) ChooseTrump(seat int, s Suit) error {
	if g.Phase != PhaseChooseTrump {
		return ErrWrongPhase
	}
	if seat != g.Hakem {
		return ErrNotHakem
	}
	g.Trump = s
	g.TrumpChosen = true
	g.dealRemaining()

	g.Phase = PhasePlaying
	g.Turn = g.Hakem // حاکم دست اول را شروع می‌کند
	return nil
}

// dealRemaining باقی کارت‌ها را پخش می‌کند تا هر بازیکن ۱۳ کارت داشته باشد.
func (g *Game) dealRemaining() {
	idx := 5 // ۵ کارت اول قبلاً به حاکم داده شد
	deal := func(seat int) {
		for len(g.hands[seat]) < 13 {
			g.hands[seat] = append(g.hands[seat], g.deck[idx])
			idx++
		}
	}
	deal(g.Hakem)
	for s := 0; s < 4; s++ {
		if s != g.Hakem {
			deal(s)
		}
	}
	g.deck = nil
}

// PlayCard یک کارت را برای بازیکن seat بازی می‌کند.
func (g *Game) PlayCard(seat int, c Card) error {
	if g.Phase != PhasePlaying {
		return ErrWrongPhase
	}
	if g.TrickDone {
		return ErrWrongPhase // دستِ کامل هنوز جمع نشده (CollectTrick)
	}
	if seat < 0 || seat > 3 {
		return ErrInvalidSeat
	}
	if seat != g.Turn {
		return ErrNotYourTurn
	}
	idx := indexOf(g.hands[seat], c)
	if idx < 0 {
		return ErrCardNotHeld
	}
	// قانون follow: اگر خال زمینه تعیین شده و بازیکن آن خال را دارد، باید همان را بازی کند.
	if g.leadSet && c.Suit != g.LeadSuit && hasSuit(g.hands[seat], g.LeadSuit) {
		return ErrMustFollow
	}

	// حذف کارت از دست
	g.hands[seat] = append(g.hands[seat][:idx], g.hands[seat][idx+1:]...)

	if !g.leadSet {
		g.LeadSuit = c.Suit
		g.leadSet = true
	}
	g.Trick = append(g.Trick, TrickCard{Seat: seat, Card: c})

	if len(g.Trick) < 4 {
		g.Turn = (g.Turn + 1) % 4
		return nil
	}

	// دست کامل شد ⇒ برنده تعیین می‌شود ولی جمع‌آوری به CollectTrick موکول می‌شود
	// تا کلاینت‌ها فرصت دیدن هر ۴ کارت روی زمین را داشته باشند.
	g.pendingWinner = g.trickWinner()
	g.TrickDone = true
	return nil
}

// CollectTrick دستِ کامل را جمع کرده، برنده را ثبت و نوبت بعد را تنظیم می‌کند.
// پس از PhasePlaying و وقتی TrickDone باشد فراخوانی می‌شود.
func (g *Game) CollectTrick() error {
	if !g.TrickDone {
		return ErrWrongPhase
	}
	winner := g.pendingWinner
	g.TricksWon[Team(winner)]++
	g.Trick = nil
	g.leadSet = false
	g.Turn = winner
	g.TrickDone = false
	g.checkHandOver()
	return nil
}

// trickWinner برنده دست جاری را تعیین می‌کند.
func (g *Game) trickWinner() int {
	best := g.Trick[0]
	for _, tc := range g.Trick[1:] {
		if beats(tc.Card, best.Card, g.Trump, g.LeadSuit) {
			best = tc
		}
	}
	return best.Seat
}

// Beats نسخه‌ی عمومیِ beats برای استفاده‌ی هوشِ باتِ لایه‌ی ws.
func Beats(a, b Card, trump, lead Suit) bool { return beats(a, b, trump, lead) }

// beats مشخص می‌کند آیا a کارت b را می‌برد (با توجه به حکم و خال زمینه).
func beats(a, b Card, trump, lead Suit) bool {
	aTrump := a.Suit == trump
	bTrump := b.Suit == trump
	switch {
	case aTrump && !bTrump:
		return true
	case !aTrump && bTrump:
		return false
	case aTrump && bTrump:
		return a.Rank > b.Rank
	default:
		// هیچ‌کدام آتو نیستند: فقط کارت هم‌خالِ زمینه می‌تواند ببرد
		if a.Suit != lead {
			return false
		}
		if b.Suit != lead {
			return true
		}
		return a.Rank > b.Rank
	}
}

// checkHandOver بررسی می‌کند آیا تیمی به ۷ دست رسیده و هَند را تسویه می‌کند.
func (g *Game) checkHandOver() {
	const tricksToWin = 7
	var winnerTeam = -1
	if g.TricksWon[0] >= tricksToWin {
		winnerTeam = 0
	} else if g.TricksWon[1] >= tricksToWin {
		winnerTeam = 1
	}
	if winnerTeam == -1 {
		return
	}

	loserTeam := 1 - winnerTeam
	kot := g.TricksWon[loserTeam] == 0
	points := 1
	if kot {
		points = 2
		// حاکم‌کُت: اگر تیمِ بازنده همان تیمِ حاکم باشد (حاکم صفر دست برده)،
		// کُت سنگین‌تر است ⇒ ۳ امتیاز.
		if Team(g.Hakem) == loserTeam {
			points = 3
		}
	}
	g.Scores[winnerTeam] += points

	// چرخش حاکمی: اگر تیم حاکم برد، حاکم می‌ماند؛ وگرنه به نفر بعد می‌رسد.
	nextHakem := g.Hakem
	if Team(g.Hakem) != winnerTeam {
		nextHakem = (g.Hakem + 1) % 4
	}

	g.LastResult = &HandResult{
		WinnerTeam: winnerTeam,
		Kot:        kot,
		HakemKot:   points == 3,
		Points:     points,
		Hakem:      g.Hakem, // حاکمِ این هَند (هنوز چرخش نکرده)
		NextHakem:  nextHakem,
	}

	if g.Scores[winnerTeam] >= g.TargetScore {
		g.Phase = PhaseGameOver
	} else {
		g.Phase = PhaseHandOver
		g.Hakem = nextHakem
	}
}

// NextHand هَند بعدی را شروع می‌کند (پس از PhaseHandOver).
func (g *Game) NextHand() error {
	if g.Phase != PhaseHandOver {
		return ErrWrongPhase
	}
	g.startHand()
	return nil
}

// Hand کپی کارت‌های یک بازیکن را برمی‌گرداند.
func (g *Game) Hand(seat int) []Card {
	out := make([]Card, len(g.hands[seat]))
	copy(out, g.hands[seat])
	return out
}

// HandCount تعداد کارت‌های هر بازیکن.
func (g *Game) HandCount(seat int) int { return len(g.hands[seat]) }

// --- کمکی‌ها ---

func indexOf(hand []Card, c Card) int {
	for i, x := range hand {
		if x == c {
			return i
		}
	}
	return -1
}

func hasSuit(hand []Card, s Suit) bool {
	for _, c := range hand {
		if c.Suit == s {
			return true
		}
	}
	return false
}
