package game

import "testing"

func TestParseCardRoundTrip(t *testing.T) {
	cases := []string{"AS", "10H", "2C", "KD", "JS", "QH"}
	for _, code := range cases {
		c, err := ParseCard(code)
		if err != nil {
			t.Fatalf("parse %q: %v", code, err)
		}
		if c.String() != code {
			t.Errorf("roundtrip %q -> %q", code, c.String())
		}
	}
}

func TestBeats(t *testing.T) {
	trump := Hearts
	lead := Spades
	// آتو بر غیرآتو می‌برد
	if !beats(Card{Hearts, 2}, Card{Spades, Ace}, trump, lead) {
		t.Error("trump 2 should beat non-trump Ace")
	}
	// بالاترین خال زمینه بر کارت خال دیگر (غیرآتو) می‌برد
	if !beats(Card{Spades, 5}, Card{Diamonds, Ace}, trump, lead) {
		t.Error("lead-suit card should beat off-suit card")
	}
	// کارت خارج از خال زمینه و غیرآتو نمی‌برد
	if beats(Card{Diamonds, Ace}, Card{Spades, 2}, trump, lead) {
		t.Error("off-suit non-trump should not beat lead suit")
	}
}

func TestMustFollowSuit(t *testing.T) {
	g := NewGame(7, 0)
	if err := g.ChooseTrump(0, Hearts); err != nil {
		t.Fatal(err)
	}
	// حاکم (seat 0) شروع می‌کند. یک کارت از دستش بازی می‌کنیم.
	lead := g.hands[0][0]
	if err := g.PlayCard(0, lead); err != nil {
		t.Fatal(err)
	}
	// بازیکن بعدی اگر کارت هم‌خال دارد باید همان را بزند.
	next := g.Turn
	if hasSuit(g.hands[next], lead.Suit) {
		// یک کارت با خال متفاوت پیدا کن
		for _, c := range g.hands[next] {
			if c.Suit != lead.Suit {
				if err := g.PlayCard(next, c); err != ErrMustFollow {
					t.Fatalf("expected ErrMustFollow, got %v", err)
				}
				return
			}
		}
	}
}

// playFullGame یک بازی کامل را با یک ربات ساده تا پایان پیش می‌برد و
// تمام نامتغیرها (invariants) را بررسی می‌کند.
func TestFullGamePlaysOut(t *testing.T) {
	g := NewGame(7, FirstHakem())

	for hand := 0; g.Phase != PhaseGameOver; hand++ {
		if hand > 100 {
			t.Fatal("too many hands; engine likely stuck")
		}
		// انتخاب حکم توسط حاکم
		if g.Phase != PhaseChooseTrump {
			t.Fatalf("expected choose_trump, got %s", g.Phase)
		}
		if err := g.ChooseTrump(g.Hakem, g.hands[g.Hakem][0].Suit); err != nil {
			t.Fatal(err)
		}
		// بررسی: هر بازیکن دقیقاً ۱۳ کارت دارد
		for s := 0; s < 4; s++ {
			if g.HandCount(s) != 13 {
				t.Fatalf("seat %d has %d cards after deal", s, g.HandCount(s))
			}
		}

		// بازی دست‌ها تا پایان هَند
		for g.Phase == PhasePlaying {
			if g.TrickDone {
				if err := g.CollectTrick(); err != nil {
					t.Fatalf("collect trick: %v", err)
				}
				continue
			}
			seat := g.Turn
			card := legalMove(g, seat)
			if err := g.PlayCard(seat, card); err != nil {
				t.Fatalf("play %s by seat %d: %v", card, seat, err)
			}
		}

		// بعد از هَند: یا تمام شده یا hand_over
		if g.Phase == PhaseHandOver {
			r := g.LastResult
			if r.Points < 1 || r.Points > 2 {
				t.Fatalf("unexpected points %d", r.Points)
			}
			if err := g.NextHand(); err != nil {
				t.Fatal(err)
			}
		}
	}

	// در پایان یک تیم باید به امتیاز هدف رسیده باشد
	if g.Scores[0] < g.TargetScore && g.Scores[1] < g.TargetScore {
		t.Fatalf("game over but no team reached target: %v", g.Scores)
	}
	t.Logf("final scores: %v", g.Scores)
}

// TestTargetScoreRespected بررسی می‌کند بازی دقیقاً وقتی یک تیم به TargetScore
// رسید تمام می‌شود (نه زودتر، نه دیرتر) — برای میزهایی مثل مبتدی (۳ دست).
func TestTargetScoreRespected(t *testing.T) {
	for _, target := range []int{1, 3, 5, 7} {
		for trial := 0; trial < 20; trial++ {
			g := NewGame(target, FirstHakem())
			for g.Phase != PhaseGameOver {
				switch {
				case g.TrickDone:
					if err := g.CollectTrick(); err != nil {
						t.Fatal(err)
					}
				case g.Phase == PhaseChooseTrump:
					if err := g.ChooseTrump(g.Hakem, g.hands[g.Hakem][0].Suit); err != nil {
						t.Fatal(err)
					}
				case g.Phase == PhaseHandOver:
					if err := g.NextHand(); err != nil {
						t.Fatal(err)
					}
				default: // playing
					if err := g.PlayCard(g.Turn, legalMove(g, g.Turn)); err != nil {
						t.Fatalf("play: %v", err)
					}
				}
			}
			w := g.Scores[0]
			if g.Scores[1] > w {
				w = g.Scores[1]
			}
			if w < target {
				t.Fatalf("target %d: game over but max score %d < target (scores %v)",
					target, w, g.Scores)
			}
			// بازنده نباید به هدف رسیده باشد (بازی باید به‌محضِ رسیدنِ یک تیم تمام شود).
			loser := g.Scores[0] + g.Scores[1] - w
			if loser >= target {
				t.Fatalf("target %d: both teams reached target %v", target, g.Scores)
			}
		}
	}
}

// legalMove یک حرکت مجاز برای ربات انتخاب می‌کند (follow suit در صورت امکان).
func legalMove(g *Game, seat int) Card {
	hand := g.hands[seat]
	if g.leadSet {
		for _, c := range hand {
			if c.Suit == g.LeadSuit {
				return c
			}
		}
	}
	return hand[0]
}
