package engine

import "testing"

func TestStartReinitializesEndedGameWithSamePlayers(t *testing.T) {
	g, err := NewGame("room-1", DefaultScript)
	if err != nil {
		t.Fatalf("NewGame failed: %v", err)
	}
	if err := g.AddPlayer("p1", "Alice"); err != nil {
		t.Fatalf("AddPlayer p1 failed: %v", err)
	}
	if err := g.AddPlayer("p2", "Bob"); err != nil {
		t.Fatalf("AddPlayer p2 failed: %v", err)
	}

	if err := g.Start(); err != nil {
		t.Fatalf("first Start failed: %v", err)
	}
	if g.State != Playing {
		t.Fatalf("expected state=%q, got %q", Playing, g.State)
	}

	g.Mu.Lock()
	g.State = Ended
	g.Winner = g.Players[0]
	g.Mu.Unlock()

	if err := g.Start(); err != nil {
		t.Fatalf("restart Start failed: %v", err)
	}
	if g.State != Playing {
		t.Fatalf("expected restarted state=%q, got %q", Playing, g.State)
	}
	if g.Winner != nil {
		t.Fatalf("winner should be reset on restart")
	}
	if len(g.Players) != 2 {
		t.Fatalf("expected players to be preserved, got %d", len(g.Players))
	}
}

func TestStartFailsWithNotEnoughPlayers(t *testing.T) {
	g, err := NewGame("room-2", DefaultScript)
	if err != nil {
		t.Fatalf("NewGame failed: %v", err)
	}
	if err := g.AddPlayer("p1", "Alice"); err != nil {
		t.Fatalf("AddPlayer failed: %v", err)
	}

	if err := g.Start(); err == nil {
		t.Fatalf("expected Start to fail with one player")
	}
}

func TestDrawWhenDeckAndDiscardAreExhaustedDoesNotPanic(t *testing.T) {
	g, err := NewGame("room-3", DefaultScript)
	if err != nil {
		t.Fatalf("NewGame failed: %v", err)
	}
	g.Players = []*Player{{ID: "p1", Name: "Alice"}, {ID: "p2", Name: "Bob"}}
	g.State = Playing
	g.CurrentPlayer = 0
	g.Deck = nil
	g.DiscardPile = []Card{{Suit: Hearts, Value: Seven}}

	defer func() {
		if r := recover(); r != nil {
			t.Fatalf("Draw panicked with exhausted deck/discard: %v", r)
		}
	}()

	err = g.Draw("p1")
	if err == nil {
		t.Fatalf("expected Draw to return an error when no cards are available")
	}
}

func TestPlayCardsRejectsMultipleSelection(t *testing.T) {
	g, err := NewGame("room-5", DefaultScript)
	if err != nil {
		t.Fatalf("NewGame failed: %v", err)
	}
	g.Players = []*Player{{ID: "p1", Name: "Alice"}, {ID: "p2", Name: "Bob"}}
	g.State = Playing
	g.CurrentPlayer = 0
	g.ActiveSuit = Hearts
	g.DiscardPile = []Card{{Suit: Hearts, Value: Nine}}
	g.Players[0].Hand = []Card{{Suit: Hearts, Value: King}, {Suit: Hearts, Value: Ace}}

	if err := g.PlayCards("p1", []int{0, 1}, Hearts); err == nil {
		t.Fatalf("expected multi-card play to be rejected")
	}
}

func TestPendingChoiceBlocksTurnUntilChoiceApplied(t *testing.T) {
	script := "function canPlay(cards, topCard, state){return cards.length===1;}\n" +
		"function onPlayed(cards, state){return {penaltyCards:state.penaltyCards,skipNext:state.skipNext,activeSuit:cards[0].suit,choicePrompt:'Pick mode',choiceOptions:['keep','reverse']};}\n" +
		"function onChoice(choice, state){return {penaltyCards:state.penaltyCards,skipNext:state.skipNext,activeSuit:state.activeSuit,reverseDir:choice==='reverse'};}"

	g, err := NewGame("room-6", script)
	if err != nil {
		t.Fatalf("NewGame failed: %v", err)
	}
	g.Players = []*Player{{ID: "p1", Name: "Alice"}, {ID: "p2", Name: "Bob"}}
	g.State = Playing
	g.CurrentPlayer = 0
	g.ActiveSuit = Hearts
	g.Direction = 1
	g.DiscardPile = []Card{{Suit: Hearts, Value: Seven}}
	g.Players[0].Hand = []Card{{Suit: Hearts, Value: Nine}, {Suit: Clubs, Value: Ten}}
	g.Players[1].Hand = []Card{{Suit: Clubs, Value: Nine}}

	if err := g.PlayCards("p1", []int{0}, Hearts); err != nil {
		t.Fatalf("PlayCards failed: %v", err)
	}
	if g.PendingChoice == nil {
		t.Fatalf("expected pending choice")
	}
	if err := g.Draw("p1"); err == nil {
		t.Fatalf("draw should be blocked while choice is pending")
	}

	if err := g.ApplyChoice("p1", "reverse"); err != nil {
		t.Fatalf("ApplyChoice failed: %v", err)
	}
	if g.PendingChoice != nil {
		t.Fatalf("pending choice should be cleared")
	}
	if g.Direction != -1 {
		t.Fatalf("expected reverse direction after choice")
	}
}

func TestOnChoiceCanDiscardExtraCardByIndex(t *testing.T) {
	script := "function canPlay(cards, topCard, state){return cards.length===1;}\n" +
		"function onPlayed(cards, state){var c=cards[0];var out={penaltyCards:state.penaltyCards,skipNext:state.skipNext,activeSuit:c.suit};if(c.value==='king'){out.choicePrompt='Discard extra?';out.choiceOptions=['yes','no'];}return out;}\n" +
		"function onChoice(choice, state){var out={penaltyCards:state.penaltyCards,skipNext:state.skipNext,activeSuit:state.activeSuit};if(choice==='yes') out.discardIndex=0;return out;}"

	g, err := NewGame("room-7", script)
	if err != nil {
		t.Fatalf("NewGame failed: %v", err)
	}
	g.Players = []*Player{{ID: "p1", Name: "Alice"}, {ID: "p2", Name: "Bob"}}
	g.State = Playing
	g.CurrentPlayer = 0
	g.ActiveSuit = Hearts
	g.Direction = 1
	g.DiscardPile = []Card{{Suit: Hearts, Value: Nine}}
	g.Players[0].Hand = []Card{{Suit: Hearts, Value: King}, {Suit: Clubs, Value: Ten}}
	g.Players[1].Hand = []Card{{Suit: Spades, Value: Seven}}

	if err := g.PlayCards("p1", []int{0}, Hearts); err != nil {
		t.Fatalf("PlayCards failed: %v", err)
	}
	if err := g.ApplyChoice("p1", "yes"); err != nil {
		t.Fatalf("ApplyChoice failed: %v", err)
	}
	if g.State != Ended || g.Winner == nil || g.Winner.ID != "p1" {
		t.Fatalf("expected player to win after discarding last card")
	}
}

func TestOnPlayedCanReplaceHandsAndDiscardPile(t *testing.T) {
	script := "function canPlay(cards, topCard, state){return cards.length===1;}\n" +
		"function onPlayed(cards, state){var me=state.hand.slice();if(me.length>0) me=me.slice(1);return {penaltyCards:0,skipNext:false,activeSuit:'clubs',playerHands:[me,[]],playedPile:[{suit:'clubs',value:'10'}],currentPlayerIndex:1};}"

	g, err := NewGame("room-8", script)
	if err != nil {
		t.Fatalf("NewGame failed: %v", err)
	}
	g.Players = []*Player{{ID: "p1", Name: "Alice"}, {ID: "p2", Name: "Bob"}}
	g.State = Playing
	g.CurrentPlayer = 0
	g.ActiveSuit = Hearts
	g.DiscardPile = []Card{{Suit: Hearts, Value: Nine}}
	g.Deck = []Card{{Suit: Spades, Value: Seven}}
	g.Players[0].Hand = []Card{{Suit: Hearts, Value: Ace}, {Suit: Clubs, Value: King}}
	g.Players[1].Hand = []Card{{Suit: Diamonds, Value: Ten}}

	if err := g.PlayCards("p1", []int{0}, Hearts); err != nil {
		t.Fatalf("PlayCards failed: %v", err)
	}
	if g.ActiveSuit != Clubs {
		t.Fatalf("expected active suit clubs, got %q", g.ActiveSuit)
	}
	if len(g.DiscardPile) != 1 || g.DiscardPile[0].Suit != Clubs || g.DiscardPile[0].Value != Ten {
		t.Fatalf("discard pile was not replaced by script")
	}
	if len(g.Players[1].Hand) != 0 {
		t.Fatalf("expected player 2 hand replaced to empty")
	}
	if g.CurrentPlayer != 1 {
		t.Fatalf("expected current player override to 1, got %d", g.CurrentPlayer)
	}
}

func TestStateExposesPlayerHandsAndCurrentPlayerIndex(t *testing.T) {
	script := "function canPlay(cards, topCard, state){return cards.length===1;}\n" +
		"function onPlayed(cards, state){var next=(state.currentPlayerIndex+(state.direction||1)+state.playerCount)%state.playerCount;var hands=state.playerHands.slice();var tmp=hands[state.currentPlayerIndex];hands[state.currentPlayerIndex]=hands[next];hands[next]=tmp;return {penaltyCards:state.penaltyCards,skipNext:state.skipNext,activeSuit:cards[0].suit,playerHands:hands};}"

	g, err := NewGame("room-9", script)
	if err != nil {
		t.Fatalf("NewGame failed: %v", err)
	}
	g.Players = []*Player{{ID: "p1", Name: "Alice"}, {ID: "p2", Name: "Bob"}}
	g.State = Playing
	g.CurrentPlayer = 0
	g.ActiveSuit = Hearts
	g.DiscardPile = []Card{{Suit: Hearts, Value: Nine}}
	g.Players[0].Hand = []Card{{Suit: Hearts, Value: King}, {Suit: Clubs, Value: Ten}}
	g.Players[1].Hand = []Card{{Suit: Spades, Value: Seven}}

	if err := g.PlayCards("p1", []int{0}, Hearts); err != nil {
		t.Fatalf("PlayCards failed: %v", err)
	}
	if len(g.Players[0].Hand) != 1 || g.Players[0].Hand[0].Suit != Spades || g.Players[0].Hand[0].Value != Seven {
		t.Fatalf("expected player 0 to receive player 1 hand")
	}
}
