//go:build integration

package inttest

import (
	"context"
	"testing"

	"github.com/google/uuid"

	"scrabble/backend/internal/account"
	"scrabble/backend/internal/engine"
	"scrabble/backend/internal/game"
)

// The seat display-name snapshot freezes the name an opponent sees for the life of a
// game (a later rename no longer rewrites past games) and lets a disguised robot carry a
// fresh per-game name (docs/ARCHITECTURE.md §7).

// seatByAccount returns the seat held by id and whether it was found.
func seatByAccount(g game.Game, id uuid.UUID) (game.Seat, bool) {
	for _, s := range g.Seats {
		if s.AccountID == id {
			return s, true
		}
	}
	return game.Seat{}, false
}

// TestSeatNameFrozenAfterRename checks a human seat captures the player's display name
// when the seat is taken and keeps it after the account is later renamed.
func TestSeatNameFrozenAfterRename(t *testing.T) {
	ctx := context.Background()
	clearOpenGames(t)
	svc := newGameService()
	accounts := account.NewStore(testDB)

	starter := provisionAccount(t)
	if _, err := accounts.UpdateProfile(ctx, starter, account.ProfileUpdate{
		DisplayName: "Original Name", PreferredLanguage: "en", TimeZone: "UTC", VariantPreferences: []string{"erudit_ru"},
	}); err != nil {
		t.Fatalf("set name: %v", err)
	}

	g := openGame(t, svc, starter, evenOpeningSeed(t))
	seat, ok := seatByAccount(g, starter)
	if !ok || seat.DisplayName != "Original Name" {
		t.Fatalf("opened seat snapshot = %q (found %v), want %q", seat.DisplayName, ok, "Original Name")
	}

	// Rename the account; the snapshot on the already-taken seat must not follow.
	if _, err := accounts.UpdateProfile(ctx, starter, account.ProfileUpdate{
		DisplayName: "Renamed Player99", PreferredLanguage: "en", TimeZone: "UTC", VariantPreferences: []string{"erudit_ru"},
	}); err != nil {
		t.Fatalf("rename: %v", err)
	}
	reloaded, err := svc.GameByID(ctx, g.ID)
	if err != nil {
		t.Fatalf("reload game: %v", err)
	}
	got, _ := seatByAccount(reloaded, starter)
	if got.DisplayName != "Original Name" {
		t.Errorf("seat snapshot after rename = %q, want it frozen at %q", got.DisplayName, "Original Name")
	}
}

// TestRobotSeatGetsComposedName checks the disguised auto-match robot's seat stores the
// fresh per-game name composed at attach time (PickNamed -> AttachRobot -> seat), and
// that the name is a valid human display name so the robot stays indistinguishable.
func TestRobotSeatGetsComposedName(t *testing.T) {
	ctx := context.Background()
	clearOpenGames(t)
	svc := newGameService()
	robots := newRobotService(t, svc)
	if err := robots.EnsurePool(ctx); err != nil {
		t.Fatalf("ensure pool: %v", err)
	}

	g := openGame(t, svc, provisionAccount(t), evenOpeningSeed(t))
	robotID, name, err := robots.PickNamed(engine.VariantEnglish)
	if err != nil {
		t.Fatalf("pick named: %v", err)
	}
	if _, err := account.ValidateDisplayName(name); err != nil {
		t.Fatalf("composed robot name %q is not a valid display name: %v", name, err)
	}
	if _, attached, err := svc.AttachRobot(ctx, g.ID, robotID, name); err != nil || !attached {
		t.Fatalf("attach robot = (attached %v, err %v), want attached", attached, err)
	}

	reloaded, err := svc.GameByID(ctx, g.ID)
	if err != nil {
		t.Fatalf("reload game: %v", err)
	}
	seat, ok := seatByAccount(reloaded, robotID)
	if !ok {
		t.Fatal("robot seat not found after attach")
	}
	if seat.DisplayName != name {
		t.Errorf("robot seat snapshot = %q, want the composed per-game name %q", seat.DisplayName, name)
	}
}
