package integration_test

import (
	"context"
	"encoding/json"
	"net/http"
	"testing"
	"time"

	"galaxy/integration/testenv"
	lobbymodel "galaxy/model/lobby"
	"galaxy/transcoder"
)

// TestLobbyMyGamesList drives `lobby.my.games.list` through the
// authenticated edge surface (Connect / gRPC / gRPC-Web). `my.games.list` returns games
// where the caller has an active membership, so the test creates a
// private game with one user, opens enrollment, invites a second
// user, the second user redeems the invite (becomes a member), and
// the second user's listing must include the game.
func TestLobbyMyGamesList(t *testing.T) {
	plat := testenv.Bootstrap(t, testenv.BootstrapOptions{})
	ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
	defer cancel()

	admin := testenv.NewBackendAdminClient(plat.Backend.HTTPURL, plat.Backend.AdminUser, plat.Backend.AdminPassword)
	if _, resp, err := admin.Do(ctx, http.MethodPost, "/api/v1/admin/engine-versions", map[string]any{
		"version": "v1.0.0", "image_ref": "galaxy/game:integration", "enabled": true,
	}); err != nil || resp.StatusCode/100 != 2 {
		t.Fatalf("seed engine_version: err=%v resp=%v", err, resp)
	}

	owner := testenv.RegisterSession(t, plat, "owner+mygames@example.com")
	pilot := testenv.RegisterSession(t, plat, "pilot+mygames@example.com")
	ownerID, err := owner.LookupUserID(ctx, plat)
	if err != nil {
		t.Fatalf("resolve owner: %v", err)
	}
	pilotID, err := pilot.LookupUserID(ctx, plat)
	if err != nil {
		t.Fatalf("resolve pilot: %v", err)
	}
	ownerHTTP := testenv.NewBackendUserClient(plat.Backend.HTTPURL, ownerID)
	pilotHTTP := testenv.NewBackendUserClient(plat.Backend.HTTPURL, pilotID)

	gameBody := map[string]any{
		"game_name":             "MyGames Lobby",
		"visibility":            "private",
		"min_players":           2,
		"max_players":           4,
		"start_gap_hours":       1,
		"start_gap_players":     2,
		"enrollment_ends_at":    time.Now().Add(24 * time.Hour).UTC().Format(time.RFC3339),
		"turn_schedule":         "0 * * * *",
		"target_engine_version": "v1.0.0",
	}
	raw, resp, err := ownerHTTP.Do(ctx, http.MethodPost, "/api/v1/user/lobby/games", gameBody)
	if err != nil || resp.StatusCode != http.StatusCreated {
		t.Fatalf("create private game: err=%v status=%d body=%s", err, resp.StatusCode, string(raw))
	}
	var created struct {
		GameID string `json:"game_id"`
	}
	if err := json.Unmarshal(raw, &created); err != nil {
		t.Fatalf("decode: %v", err)
	}

	if _, resp, err := ownerHTTP.Do(ctx, http.MethodPost, "/api/v1/user/lobby/games/"+created.GameID+"/open-enrollment", nil); err != nil || resp.StatusCode != http.StatusOK {
		t.Fatalf("open enrollment: err=%v status=%d", err, resp.StatusCode)
	}
	raw, resp, err = ownerHTTP.Do(ctx, http.MethodPost, "/api/v1/user/lobby/games/"+created.GameID+"/invites", map[string]any{
		"invited_user_id": pilotID,
		"race_name":       "PilotMG",
	})
	if err != nil || resp.StatusCode != http.StatusCreated {
		t.Fatalf("issue invite: err=%v status=%d body=%s", err, resp.StatusCode, string(raw))
	}
	var invite struct{ InviteID string `json:"invite_id"` }
	_ = json.Unmarshal(raw, &invite)
	if _, resp, err := pilotHTTP.Do(ctx, http.MethodPost, "/api/v1/user/lobby/games/"+created.GameID+"/invites/"+invite.InviteID+"/redeem", nil); err != nil || resp.StatusCode/100 != 2 {
		t.Fatalf("redeem: err=%v status=%d", err, resp.StatusCode)
	}

	gw, err := pilot.DialAuthenticated(ctx, plat)
	if err != nil {
		t.Fatalf("dial: %v", err)
	}
	defer gw.Close()

	payload, err := transcoder.MyGamesListRequestToPayload(&lobbymodel.MyGamesListRequest{})
	if err != nil {
		t.Fatalf("encode payload: %v", err)
	}
	res, err := gw.Execute(ctx, lobbymodel.MessageTypeMyGamesList, payload, testenv.ExecuteOptions{})
	if err != nil {
		t.Fatalf("execute my.games.list: %v", err)
	}
	if res.ResultCode != "ok" {
		t.Fatalf("result_code = %q, want ok", res.ResultCode)
	}
	list, err := transcoder.PayloadToMyGamesListResponse(res.PayloadBytes)
	if err != nil {
		t.Fatalf("decode list response: %v", err)
	}
	found := false
	for _, g := range list.Items {
		if g.GameID == created.GameID {
			found = true
			break
		}
	}
	if !found {
		t.Fatalf("created game %q absent from my-games list: %+v", created.GameID, list.Items)
	}
}
