//go:build integration

package inttest

import (
	"context"
	"testing"

	"github.com/google/uuid"

	"scrabble/backend/internal/game"
)

// TestForfeitAllResignsActiveGame checks the game-side effect of a block on a two-player game:
// ForfeitAllForAccount resigns the blocked player, finishing the game with the opponent winning.
func TestForfeitAllResignsActiveGame(t *testing.T) {
	ctx := context.Background()
	svc := newGameService()
	gid, seats := newGameWithSeats(t, 2) // seats[0] = blocked, seats[1] = opponent

	n, err := svc.ForfeitAllForAccount(ctx, seats[0])
	if err != nil {
		t.Fatalf("forfeit all: %v", err)
	}
	if n < 1 {
		t.Fatalf("forfeit count = %d, want at least 1", n)
	}

	g, err := svc.GameByID(ctx, gid)
	if err != nil {
		t.Fatalf("get game: %v", err)
	}
	if g.Status != game.StatusFinished {
		t.Errorf("status = %q, want finished", g.Status)
	}
	if w := seatWinner(g, seats[0]); w {
		t.Error("the blocked player must not be the winner")
	}
	if w := seatWinner(g, seats[1]); !w {
		t.Error("the opponent must win when the blocked player forfeits")
	}
}

// TestForfeitAllCancelsOpenGame checks that a block deletes the blocked player's open auto-match
// game (no opponent to resign against), clearing it from the matchmaking pool.
func TestForfeitAllCancelsOpenGame(t *testing.T) {
	ctx := context.Background()
	clearOpenGames(t)
	svc := newGameService()
	starter := provisionAccount(t)
	g := openGame(t, svc, starter, evenOpeningSeed(t))

	n, err := svc.ForfeitAllForAccount(ctx, starter)
	if err != nil {
		t.Fatalf("forfeit all: %v", err)
	}
	if n < 1 {
		t.Fatalf("forfeit count = %d, want at least 1", n)
	}
	if c := gameRowCount(t, g.ID); c != 0 {
		t.Errorf("open game rows after forfeit = %d, want 0 (deleted)", c)
	}
}

// TestForfeitAllResignsSeatInMultiplayerGame checks that in a 3-player game the blocked player's
// seat is resigned while the game continues for the remaining players.
func TestForfeitAllResignsSeatInMultiplayerGame(t *testing.T) {
	ctx := context.Background()
	svc := newGameService()
	gid, seats := newGameWithSeats(t, 3)

	n, err := svc.ForfeitAllForAccount(ctx, seats[0])
	if err != nil {
		t.Fatalf("forfeit all: %v", err)
	}
	if n < 1 {
		t.Fatalf("forfeit count = %d, want at least 1", n)
	}

	g, err := svc.GameByID(ctx, gid)
	if err != nil {
		t.Fatalf("get game: %v", err)
	}
	if g.Status != game.StatusActive {
		t.Errorf("status = %q, want still active (two seats remain)", g.Status)
	}
	if c := resignMoveCount(t, gid, 0); c != 1 {
		t.Errorf("resign moves for seat 0 = %d, want 1", c)
	}
}

// seatWinner reports the is_winner flag for the given account's seat in g.
func seatWinner(g game.Game, accountID uuid.UUID) bool {
	for _, s := range g.Seats {
		if s.AccountID == accountID {
			return s.IsWinner
		}
	}
	return false
}

// gameRowCount counts the rows in the games table for gameID (0 once it is deleted).
func gameRowCount(t *testing.T, gameID uuid.UUID) int {
	t.Helper()
	var n int
	if err := testDB.QueryRowContext(context.Background(),
		`SELECT count(*) FROM backend.games WHERE game_id = $1`, gameID).Scan(&n); err != nil {
		t.Fatalf("count game rows: %v", err)
	}
	return n
}

// resignMoveCount counts the resign moves journalled for a seat in a game.
func resignMoveCount(t *testing.T, gameID uuid.UUID, seat int) int {
	t.Helper()
	var n int
	if err := testDB.QueryRowContext(context.Background(),
		`SELECT count(*) FROM backend.game_moves WHERE game_id = $1 AND action = 'resign' AND seat = $2`,
		gameID, seat).Scan(&n); err != nil {
		t.Fatalf("count resign moves: %v", err)
	}
	return n
}
