package main

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	_ "embed"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math"
	"math/big"
	"net"
	"net/http"
	"time"
)

//go:embed mock_gamestatus.bin
var mockGameStatusBytes []byte

//go:embed mock_gamedata.b64
var mockGameDataBase64 string

const (
	MockUserID   = 8381763
	MockUserName = "CustomPlayer"
)

// User Details Response Structures
type UserDetailsResponse struct {
	Error  bool            `json:"error"`
	Status int             `json:"status"`
	Data   UserDetailsData `json:"data"`
}

type UserDetailsData struct {
	User UserDetails `json:"user"`
}

type PartnerData struct {
	PartnerID     int    `json:"partner_id"`
	PartnerUserID string `json:"partner_user_id"`
	CreatedAt     string `json:"created_at"`
}

type UserDetails struct {
	UserID         int           `json:"user_id"`
	LoginName      string        `json:"login_name"`
	Email          string        `json:"email"`
	Name           string        `json:"name"`
	EmailValid     bool          `json:"email_valid"`
	Validated      bool          `json:"validated"`
	Country        string        `json:"country"`
	Language       string        `json:"language"`
	TimeZone       string        `json:"time_zone"`
	PostedMsgCount int           `json:"posted_msg_count"`
	Features       []string      `json:"features"`
	Partners       []PartnerData `json:"partners"`
	BoardGames     []interface{} `json:"boardgames"`
	OnlineGames    []interface{} `json:"onlinegames"`
	Avatar         string        `json:"avatar"`
}

// User Search Response Structures
type UserSearchResponse struct {
	Error  bool           `json:"error"`
	Status int            `json:"status"`
	Data   UserSearchData `json:"data"`
}

type UserSearchData struct {
	Total int                    `json:"total"`
	Links map[string]interface{} `json:"_links"`
	Users []SearchUser           `json:"users"`
}

type SearchUser struct {
	UserID      int           `json:"user_id"`
	LoginName   string        `json:"login_name"`
	Avatar      string        `json:"avatar"`
	Features    []string      `json:"features"`
	BoardGames  []interface{} `json:"boardgames"`
	OnlineGames []interface{} `json:"onlinegames"`
}

var (
	// Global state to track if we are in a game (simplistic single-user view for now)
	hasActiveGame bool  = false
	activeGameID  int64 = 0
)

func main() {
	mux := http.NewServeMux()

	mux.HandleFunc("/main/v2/oauth/token", handleToken)
	mux.HandleFunc("/main/v1/user/me", handleUserMe)
	mux.HandleFunc("/main/v1/user/me/link", handleLink)
	mux.HandleFunc("/main/v1/users", handleUserSearch)
	mux.HandleFunc("/main/v3/showcase/games/steam/es", handleShowcase)
	mux.HandleFunc("/main/v1/user/me/buddies", handleBuddies)
	mux.HandleFunc("/main/v1/user/me/lastopponents/GOTDBG", handleLastOpponents)

	// Catch-all to log other requests and return empty JSON instead of text 404
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("[HTTP] [UNKNOWN] Request to %s (%s) from %s - Full URI: %s\n", r.URL.Path, r.Method, r.RemoteAddr, r.RequestURI)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(`{"error":true,"status":404,"message":"Not found"}`))
	})

	// 1. HTTP Server for REST API (Port 8080)
	go func() {
		fmt.Println("[HTTP] REST API: http://localhost:8080")
		if err := http.ListenAndServe(":8080", mux); err != nil {
			log.Fatalf("HTTP server failed: %v", err)
		}
	}()

	// 2. TCP Server for Scalable Server (Port 3000) with SSL
	tlsConfig, err := generateSelfSignedCert()
	if err != nil {
		log.Fatalf("Failed to generate self-signed cert: %v", err)
	}

	listener, err := tls.Listen("tcp", ":3000", tlsConfig)
	if err != nil {
		log.Fatalf("TCP TLS listener failed: %v", err)
	}
	fmt.Println("[TCP] Scalable Server (TLS): localhost:3000")

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Printf("Accept error: %v\n", err)
			continue
		}
		go handleTCPConnection(conn)
	}
}

func generateSelfSignedCert() (*tls.Config, error) {
	priv, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, err
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			Organization: []string{"Custom Server Mod"},
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour * 24 * 365),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IPAddresses:           []net.IP{net.ParseIP("127.0.0.1")},
		DNSNames:              []string{"localhost"},
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return nil, err
	}

	cert := tls.Certificate{
		Certificate: [][]byte{derBytes},
		PrivateKey:  priv,
	}

	return &tls.Config{Certificates: []tls.Certificate{cert}}, nil
}

func handleToken(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("[HTTP] Request to %s from %s\n", r.URL.Path, r.RemoteAddr)
	response := map[string]interface{}{
		"access_token":  "mock_access_token_12345",
		"token_type":    "bearer",
		"expires_in":    3600,
		"refresh_token": "mock_refresh_token_67890",
		"scope":         "public private boardgames onlinegames partners features",
	}
	w.Header().Set("Content-Type", "application/json")
	data, _ := json.Marshal(response)
	w.Write(data)
}

func handleUserMe(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("[HTTP] Request to %s from %s\n", r.URL.Path, r.RemoteAddr)

	response := UserDetailsResponse{
		Error:  false,
		Status: 200,
		Data: UserDetailsData{
			User: UserDetails{
				UserID:         MockUserID,
				LoginName:      MockUserName,
				Email:          "player@customserver.local",
				Name:           MockUserName,
				EmailValid:     true,
				Validated:      true,
				Country:        "US",
				Language:       "en",
				TimeZone:       "UTC",
				PostedMsgCount: 42,
				Features:       []string{"online_play", "all_expansions", "community", "profile", "userpages"},
				Partners: []PartnerData{
					{
						PartnerID:     12, // Steam
						PartnerUserID: "76561198084728812",
						CreatedAt:     "2026-01-01T00:00:00Z",
					},
				},
				BoardGames:  []interface{}{},
				OnlineGames: []interface{}{},
				Avatar:      "https://uploads.asmodee.net/builtin/avatar-neutral.jpg",
			},
		},
	}

	w.Header().Set("Content-Type", "application/json")
	data, _ := json.Marshal(response)
	w.Write(data)
}

func handleLink(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("[HTTP] Request to %s (%s) from %s\n", r.URL.Path, r.Method, r.RemoteAddr)
	response := map[string]interface{}{
		"error":  false,
		"status": 200,
		"data":   map[string]interface{}{},
	}
	w.Header().Set("Content-Type", "application/json")
	data, _ := json.Marshal(response)
	w.Write(data)
}

func handleUserSearch(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("[HTTP] Request to %s from %s\n", r.URL.Path, r.RemoteAddr)

	response := UserSearchResponse{
		Error:  false,
		Status: 200,
		Data: UserSearchData{
			Total: 1,
			Links: map[string]interface{}{
				"first": nil,
				"last":  nil,
				"next":  nil,
				"prev":  nil,
			},
			Users: []SearchUser{
				{
					UserID:      MockUserID,
					LoginName:   MockUserName,
					Avatar:      "https://uploads.asmodee.net/builtin/avatar-neutral.jpg",
					Features:    []string{"online_play", "all_expansions"},
					BoardGames:  []interface{}{},
					OnlineGames: []interface{}{},
				},
			},
		},
	}

	w.Header().Set("Content-Type", "application/json")
	data, _ := json.Marshal(response)
	w.Write(data)
}

func handleShowcase(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("[HTTP] Request to %s (%s) from %s\n", r.URL.Path, r.Method, r.RemoteAddr)
	response := map[string]interface{}{
		"error":  false,
		"status": 200,
		"data":   []interface{}{},
	}
	w.Header().Set("Content-Type", "application/json")
	data, _ := json.Marshal(response)
	w.Write(data)
}

func handleBuddies(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("[HTTP] Request to %s from %s\n", r.URL.Path, r.RemoteAddr)
	response := map[string]interface{}{
		"error":  false,
		"status": 200,
		"data": map[string]interface{}{
			"total":   0,
			"buddies": []interface{}{},
			"_links": map[string]interface{}{
				"next":  nil,
				"prev":  nil,
				"first": nil,
				"last":  nil,
			},
		},
	}
	w.Header().Set("Content-Type", "application/json")
	data, _ := json.Marshal(response)
	w.Write(data)
}

func handleLastOpponents(w http.ResponseWriter, r *http.Request) {
	fmt.Printf("[HTTP] Request to %s from %s\n", r.URL.Path, r.RemoteAddr)
	// Needs to match UserOnlineGameGetRecentOpponentsDataJson structure
	// { "data": { "opponents": [] } }
	response := map[string]interface{}{
		"data": map[string]interface{}{
			"opponents": []interface{}{},
		},
	}
	w.Header().Set("Content-Type", "application/json")
	data, _ := json.Marshal(response)
	w.Write(data)
}

func getMockPlayerBytes() []byte {
	player := make([]byte, 0)

	// field 1 (Name), string
	player = append(player, 0x0a)
	player = append(player, encodeVarint(uint64(len(MockUserName)))...)
	player = append(player, MockUserName...)

	// field 2 (Id), varint
	player = append(player, 0x10)
	player = append(player, encodeVarint(uint64(MockUserID))...)

	// field 5 (Karma), varint
	player = append(player, 0x28)
	player = append(player, encodeVarint(52)...)

	// field 7 (RankScore), fixed64 (WireType 1) - double
	player = append(player, 0x39) // (7 << 3) | 1 = 57 = 0x39
	player = append(player, encodeDouble(1500.0)...)

	// field 9 (WWWId), varint
	player = append(player, 0x48)
	player = append(player, encodeVarint(uint64(MockUserID))...)

	// field 11 (nbGames), varint
	player = append(player, 0x58)
	player = append(player, encodeVarint(2)...)

	// field 12 (Banned), varint
	player = append(player, 0x60)
	player = append(player, encodeVarint(0)...) // False

	// field 14 (Avatar), message
	avatarMsg := make([]byte, 0)
	avatarUrl := "https://uploads.asmodee.net/builtin/avatar-neutral.jpg"
	// Avatar.Image (Field 3), String
	avatarMsg = append(avatarMsg, 0x1a)
	avatarMsg = append(avatarMsg, encodeVarint(uint64(len(avatarUrl)))...)
	avatarMsg = append(avatarMsg, avatarUrl...)

	player = append(player, 0x72) // Field 14, WireType 2
	player = append(player, encodeVarint(uint64(len(avatarMsg)))...)
	player = append(player, avatarMsg...)

	// field 15 (Language), varint
	player = append(player, 0x78)
	player = append(player, encodeVarint(1)...)

	// field 16 (Tz), string
	player = append(player, 0x82, 0x01) // Field 16 (128 + 2), WireType 2
	player = append(player, encodeVarint(3)...)
	player = append(player, "UTC"...)

	return player
}

func getMockIronHumanPlayerBytes() []byte {
	// IronHumanPlayer
	// Field 1: PlayerId (int32)
	// Field 2: House (Enum) -> Stark (0)
	data := make([]byte, 0)
	// PlayerId
	data = append(data, 0x08)
	data = append(data, encodeVarint(uint64(MockUserID))...)
	// House (0 is default, we skip it)
	return data
}

func getMockIronGameConfigurationBytes() []byte {
	// IronGameConfiguration
	// Field 1: TotalPlayers (int32) -> 6
	// Field 7: HumanPlayers (repeated IronHumanPlayer)
	// Field 8: CreatedBy (int64)
	data := make([]byte, 0)

	// TotalPlayers = 6
	data = append(data, 0x08)
	data = append(data, encodeVarint(6)...)

	// HumanPlayers (repeated)
	humanPlayer := getMockIronHumanPlayerBytes()
	data = append(data, 0x3a) // Field 7, WireType 2
	data = append(data, encodeVarint(uint64(len(humanPlayer)))...)
	data = append(data, humanPlayer...)

	// CreatedBy
	data = append(data, 0x40) // Field 8, WireType 0
	data = append(data, encodeVarint(uint64(MockUserID))...)

	return data
}

func getMockIronGameStateBytes() []byte {
	// IronGameState
	// Field 1: GameId (int64)
	// Field 7: Configuration (IronGameConfiguration)
	data := make([]byte, 0)

	// GameId
	data = append(data, 0x08)
	data = append(data, encodeVarint(12345)...)

	// Configuration
	config := getMockIronGameConfigurationBytes()
	data = append(data, 0x3a) // Field 7, WireType 2
	data = append(data, encodeVarint(uint64(len(config)))...)
	data = append(data, config...)

	return data
}

func handleTCPConnection(conn net.Conn) {
	defer conn.Close()
	fmt.Printf("[TCP] New TLS connection from %s\n", conn.RemoteAddr())

	for {
		// Read length (4 bytes, Big Endian)
		var length uint32
		err := binary.Read(conn, binary.BigEndian, &length)
		if err != nil {
			if err != io.EOF {
				fmt.Printf("[TCP] Read length error: %v\n", err)
			}
			return
		}

		// Read packet
		data := make([]byte, length)
		_, err = io.ReadFull(conn, data)
		if err != nil {
			fmt.Printf("[TCP] Read data error: %v\n", err)
			return
		}

		fmt.Printf("[TCP] Received packet of %d bytes\n", length)

		packetID := int64(0)
		requestNumber := int32(0)
		var wrapperRequestNumber uint64
		var payloadBytes []byte

		reader := bytes.NewReader(data)
		for {
			tag, err := readVarint(reader)
			if err != nil {
				break
			}
			fieldNum := tag >> 3
			wireType := tag & 0x7

			if fieldNum == 1 && wireType == 0 { // Packet.id
				packetID, _ = readVarintInt64(reader)
			} else if fieldNum == 3 && wireType == 2 { // Packet.payload (Message)
				payloadLen, _ := readVarint(reader)
				payloadBytes = make([]byte, payloadLen)
				reader.Read(payloadBytes)

				payloadReader := bytes.NewReader(payloadBytes)
				for {
					pTag, err := readVarint(payloadReader)
					if err != nil {
						break
					}
					pFieldNum := pTag >> 3
					pWireType := pTag & 0x7
					if pFieldNum == 1 && pWireType == 0 { // Message.request_number
						reqNum64, _ := readVarintInt64(payloadReader)
						requestNumber = int32(reqNum64)
					} else {
						skipField(payloadReader, pWireType)
					}
				}
			} else {
				skipField(reader, wireType)
			}
		}

		fmt.Printf("[TCP] Got Request ID: %d, Number: %d\n", packetID, requestNumber)

		var responsePayload []byte
		var responseFieldNum int

		switch requestNumber {
		case 400: // AsyncAuthRequest
			fmt.Println("[TCP] Handling AsyncAuthRequest")
			// AsyncConnectedRequest (406)
			// Matches official: "4016461897007108096" (string)
			// In official log: session: { id: "..." }
			// This means Session is a message with id (field 1 probably) as String.

			// Session Message
			sessionData := make([]byte, 0)
			// field 1 (Id), Varint (WireType 0)
			sessionData = append(sessionData, 0x08)
			sessionData = append(sessionData, encodeVarint(uint64(time.Now().UnixNano()))...)

			// Player Message
			player := getMockPlayerBytes()

			// AsyncConnectedRequest
			asyncConnected := make([]byte, 0)
			// field 1 (Session), message
			asyncConnected = append(asyncConnected, 0x0a)
			asyncConnected = append(asyncConnected, encodeVarint(uint64(len(sessionData)))...)
			asyncConnected = append(asyncConnected, sessionData...)

			// field 2 (Player), message
			asyncConnected = append(asyncConnected, 0x12)
			asyncConnected = append(asyncConnected, encodeVarint(uint64(len(player)))...)
			asyncConnected = append(asyncConnected, player...)

			responsePayload = asyncConnected
			responseFieldNum = 406

		case 408: // AskServerStatisticsRequest
			fmt.Println("[TCP] Handling AskServerStatisticsRequest")
			// Return ServerStatisticsRequest (409)
			// Fields: 1:HostedGames, 2:Players, 3:ConnectedPlayers (all int32)
			stats := make([]byte, 0)

			// Field 1: HostedGames (Varint)
			stats = append(stats, 0x08)
			stats = append(stats, encodeVarint(1)...)

			// Field 2: Players (Varint)
			stats = append(stats, 0x10)
			stats = append(stats, encodeVarint(1)...)

			// Field 3: ConnectedPlayers (Varint)
			stats = append(stats, 0x18)
			stats = append(stats, encodeVarint(1)...)

			responsePayload = stats
			responseFieldNum = 409

		case 515: // AsyncBuddyListRequest
			fmt.Println("[TCP] Handling AsyncBuddyListRequest")
			// Return AsyncBuddyListContentRequest (516)
			// Field 2: Buddies (AsyncBuddyList)
			// AsyncBuddyList has repeated fields, so empty message is fine.
			// We need to provide Field 2 with length 0 to ensure 'Buddies' object is created in C#.
			responsePayload = []byte{0x12, 0x00}
			responseFieldNum = 516

		case 560: // AsyncIgnoreListRequest
			fmt.Println("[TCP] Handling AsyncIgnoreListRequest")
			// Return AsyncIgnoreListContentRequest (561)
			// Field 2: Ignores (AsyncBuddyList) - Assuming same structure as BuddyList based on typical reuse
			responsePayload = []byte{0x12, 0x00}
			responseFieldNum = 561

		case 401: // AsyncDisconnectRequest
			fmt.Println("[TCP] Handling AsyncDisconnectRequest")
			responsePayload = []byte{}
			responseFieldNum = 401

		case 600: // EnterLobbyRequest
			fmt.Println("[TCP] Handling EnterLobbyRequest")
			responsePayload = []byte{}
			responseFieldNum = 601

		case 604: // LobbyPlayerListRequest
			fmt.Println("[TCP] Handling LobbyPlayerListRequest")
			// Field 1: repeated Player
			mockPlayer := getMockPlayerBytes()

			responsePayload = make([]byte, 0)
			responsePayload = append(responsePayload, 0x0a) // Field 1, WireType 2
			responsePayload = append(responsePayload, encodeVarint(uint64(len(mockPlayer)))...)
			responsePayload = append(responsePayload, mockPlayer...)

			responseFieldNum = 604

		case 609: // LobbyGameListRequest
			fmt.Println("[TCP] Handling LobbyGameListRequest")
			// Return a list containing our single mock game
			// Field 1: repeated GameDetails

			// Construct GameDetails (Message)
			// Field 1: GameId (int64)
			// Field 2: Players (repeated Player)
			// Field 3: Configuration (GameConfiguration)
			gameData := make([]byte, 0)
			gameData = append(gameData, 0x08)
			gameData = append(gameData, encodeVarint(4016461897007108096)...)

			// Players (Field 2)
			mockPlayer := getMockPlayerBytes()
			gameData = append(gameData, 0x12)
			gameData = append(gameData, encodeVarint(uint64(len(mockPlayer)))...)
			gameData = append(gameData, mockPlayer...)

			// Configuration (Field 3)
			// Mock Config for List
			mockConfig := make([]byte, 0)
			// Name
			mockConfig = append(mockConfig, 0x0a)
			mockConfig = append(mockConfig, encodeVarint(uint64(len("Partida de CustomPlayer")))...)
			mockConfig = append(mockConfig, "Partida de CustomPlayer"...)
			// Min/Max Players
			mockConfig = append(mockConfig, 0x28)
			mockConfig = append(mockConfig, encodeVarint(3)...)
			mockConfig = append(mockConfig, 0x30)
			mockConfig = append(mockConfig, encodeVarint(6)...)

			gameData = append(gameData, 0x1a)
			gameData = append(gameData, encodeVarint(uint64(len(mockConfig)))...)
			gameData = append(gameData, mockConfig...)

			// Now wrap in LobbyGameListRequest (Field 1 repeated)
			responsePayload = make([]byte, 0)
			responsePayload = append(responsePayload, 0x0a)
			responsePayload = append(responsePayload, encodeVarint(uint64(len(gameData)))...)
			responsePayload = append(responsePayload, gameData...)

			responseFieldNum = 609

		case 622: // ObservableGameListRequest
			fmt.Println("[TCP] Handling ObservableGameListRequest")
			// Similar to 609, return list of games
			// Field 1: repeated GameDetails
			// Reuse logic from 609 implicitly by copy-paste for safety

			// Construct GameDetails (Message)
			gameData := make([]byte, 0)
			gameData = append(gameData, 0x08)
			gameData = append(gameData, encodeVarint(4016461897007108096)...)

			// Players (Field 2)
			mockPlayer := getMockPlayerBytes()
			gameData = append(gameData, 0x12)
			gameData = append(gameData, encodeVarint(uint64(len(mockPlayer)))...)
			gameData = append(gameData, mockPlayer...)

			// Configuration (Field 3)
			mockConfig := make([]byte, 0)
			mockConfig = append(mockConfig, 0x0a)
			mockConfig = append(mockConfig, encodeVarint(uint64(len("Partida de CustomPlayer")))...)
			mockConfig = append(mockConfig, "Partida de CustomPlayer"...)
			mockConfig = append(mockConfig, 0x28)
			mockConfig = append(mockConfig, encodeVarint(3)...)
			mockConfig = append(mockConfig, 0x30)
			mockConfig = append(mockConfig, encodeVarint(6)...)

			gameData = append(gameData, 0x1a)
			gameData = append(gameData, encodeVarint(uint64(len(mockConfig)))...)
			gameData = append(gameData, mockConfig...)

			responsePayload = make([]byte, 0)
			responsePayload = append(responsePayload, 0x0a)
			responsePayload = append(responsePayload, encodeVarint(uint64(len(gameData)))...)
			responsePayload = append(responsePayload, gameData...)

			responseFieldNum = 622

			// ... inside main() function ...

		case 511: // WhatsNewPussycatRequest
			fmt.Println("[TCP] Handling WhatsNewPussycatRequest")

			if hasActiveGame {
				fmt.Printf("[TCP] StatusReport: Reporting Active Game %d\n", activeGameID)

				// StatusReport Message (Inner)
				innerReport := make([]byte, 0)

				// Field 1: GameId (int64)
				innerReport = append(innerReport, 0x08)
				innerReport = append(innerReport, encodeVarint(uint64(activeGameID))...)

				// Field 2: Status (Enum) = 1 (IN_PROGRESS)
				innerReport = append(innerReport, 0x10)
				innerReport = append(innerReport, encodeVarint(1)...)

				// Field 3: Data (Bytes) - From mock_gamedata.b64
				dataBytes, _ := base64.StdEncoding.DecodeString(mockGameDataBase64)
				innerReport = append(innerReport, 0x1a)
				innerReport = append(innerReport, encodeVarint(uint64(len(dataBytes)))...)
				innerReport = append(innerReport, dataBytes...)

				// Field 4: TurnId (int32) = 4
				innerReport = append(innerReport, 0x20)
				innerReport = append(innerReport, encodeVarint(4)...)

				// Field 5: NextPlayerIds (Repeated Int32)
				// 0x2A = Field 5 (0010 1) | WireType 2
				nextPlayers := []uint64{uint64(MockUserID)}
				npBytes := make([]byte, 0)
				for _, pid := range nextPlayers {
					npBytes = append(npBytes, encodeVarint(pid)...)
				}
				innerReport = append(innerReport, 0x2a)
				innerReport = append(innerReport, encodeVarint(uint64(len(npBytes)))...)
				innerReport = append(innerReport, npBytes...)

				// Field 6: Players (Repeated Player)
				mockPlayer := getMockPlayerBytes()
				innerReport = append(innerReport, 0x32)
				innerReport = append(innerReport, encodeVarint(uint64(len(mockPlayer)))...)
				innerReport = append(innerReport, mockPlayer...)

				// Field 14: Configuration (GameConfiguration)
				fallbackConfig := make([]byte, 0)
				name := "Active Game Details"
				fallbackConfig = append(fallbackConfig, 0x0a)
				fallbackConfig = append(fallbackConfig, encodeVarint(uint64(len(name)))...)
				fallbackConfig = append(fallbackConfig, name...)
				fallbackConfig = append(fallbackConfig, 0x10, 0x00, 0x18, 0x01, 0x20, 0x00)
				// MinPlayers=1
				fallbackConfig = append(fallbackConfig, 0x28, 0x01)
				// MaxPlayers=4
				fallbackConfig = append(fallbackConfig, 0x30, 0x04)
				// GameMode=0
				fallbackConfig = append(fallbackConfig, 0x48, 0x00, 0x50, 0x2d)

				innerReport = append(innerReport, 0x72)
				innerReport = append(innerReport, encodeVarint(uint64(len(fallbackConfig)))...)
				innerReport = append(innerReport, fallbackConfig...)

				// Field 16: ActivePlayer (Int32)
				innerReport = append(innerReport, 0x80, 0x01)
				innerReport = append(innerReport, encodeVarint(uint64(MockUserID))...)

				// Wrap in GameStatusReportRequest (Field 1: repeated StatusReport)
				// Note: Field 1 of GameStatusReportRequest is 'reports', which is REPEATED StatusReport.
				// Since we only have one game, we encode one StatusReport message.
				// Protobuf for repeated message: Tag, Length, Data, Tag, Length, Data... (if not packed? Messages usually not packed?)
				// Wait, repeated messages are repeated occurrences of {Tag, Length, Data}.
				// So we append 0x0a [Length] [innerReport].

				responsePayload = make([]byte, 0)
				responsePayload = append(responsePayload, 0x0a)
				responsePayload = append(responsePayload, encodeVarint(uint64(len(innerReport)))...)
				responsePayload = append(responsePayload, innerReport...)

			} else {
				// Use the embedded binary blob which mimics the official server's complex response (Idle/Lobby)
				// But we should verify if this blob contains "GameId: 12345" which might confuse things.
				// For safety, let's use it as is for now if no game is active.
				fmt.Println("[TCP] StatusReport: Reporting Idle/Lobby State (Embedded)")
				responsePayload = mockGameStatusBytes
			}

			responseFieldNum = 512
			wrapperRequestNumber = uint64(packetID)

		// ... existing handlers ...

		case 607: // LobbyCreateGameRequest
			fmt.Println("[TCP] Handling LobbyCreateGameRequest")

			// The payloadBytes we have here are the 'Message' wrapper.
			// Field 607 of 'Message' is 'LobbyCreateGameRequest'.
			// Field 1 of 'LobbyCreateGameRequest' is 'Configuration' (GameConfiguration).

			var configBytes []byte

			// 1. Scan 'Message' wrapper for Field 607
			msgReader := bytes.NewReader(payloadBytes)
			var createGameReqBytes []byte
			for {
				tag, err := readVarint(msgReader)
				if err != nil {
					break
				}
				fieldNum := tag >> 3
				wireType := tag & 0x7

				if fieldNum == 607 && wireType == 2 {
					length, _ := readVarint(msgReader)
					createGameReqBytes = make([]byte, length)
					msgReader.Read(createGameReqBytes)
					break // Found the request message
				} else {
					skipField(msgReader, wireType)
				}
			}

			// 2. Scan 'LobbyCreateGameRequest' for Field 1 (Configuration)
			if len(createGameReqBytes) > 0 {
				reqReader := bytes.NewReader(createGameReqBytes)
				for {
					tag, err := readVarint(reqReader)
					if err != nil {
						break
					}
					fieldNum := tag >> 3
					wireType := tag & 0x7

					if fieldNum == 1 && wireType == 2 {
						length, _ := readVarint(reqReader)
						configBytes = make([]byte, length)
						reqReader.Read(configBytes)
						break // Found the configuration
					} else {
						skipField(reqReader, wireType)
					}
				}
			}

			// Update Global State
			hasActiveGame = true
			activeGameID = 4016461897007108096 // Mock ID

			// Construct GameDetails (Message)
			// Field 1: GameId (int64)
			// Field 2: Players (repeated Player)
			// Field 3: Configuration (GameConfiguration)
			// Field 5: UserData (repeated PlayerPregameData) <--- NEW

			gameDetails := make([]byte, 0)

			// GameId
			gameDetails = append(gameDetails, 0x08)
			gameDetails = append(gameDetails, encodeVarint(uint64(activeGameID))...)

			// Players (Field 2)
			mockPlayer := getMockPlayerBytes()
			gameDetails = append(gameDetails, 0x12)
			gameDetails = append(gameDetails, encodeVarint(uint64(len(mockPlayer)))...)
			gameDetails = append(gameDetails, mockPlayer...)

			// Configuration (Field 3)
			if len(configBytes) > 0 {
				fmt.Println("[TCP] CreateGame: Successfully extracted Configuration. Forcing MinPlayers=1")

				// Force MinPlayers (Field 5) to 1 by appending it to the end (Last Write Wins strategy)
				// Field 5 (0010 1000) = 0x28
				configBytes = append(configBytes, 0x28, 0x01)

				gameDetails = append(gameDetails, 0x1a)
				gameDetails = append(gameDetails, encodeVarint(uint64(len(configBytes)))...)
				gameDetails = append(gameDetails, configBytes...)
			} else {
				// ... (Fallback Config) ...
				fmt.Println("[TCP] CreateGame: Using Fallback Configuration")
				fallbackConfig := make([]byte, 0)
				// 1. Name
				name := "Fallback Game"
				fallbackConfig = append(fallbackConfig, 0x0a)
				fallbackConfig = append(fallbackConfig, encodeVarint(uint64(len(name)))...)
				fallbackConfig = append(fallbackConfig, name...)
				// 2. Private, 3. Lurkable, etc
				fallbackConfig = append(fallbackConfig, 0x10, 0x00, 0x18, 0x01, 0x20, 0x00)

				// 5. MinPlayers (Int32) - FORCE TO 1
				fallbackConfig = append(fallbackConfig, 0x28)
				fallbackConfig = append(fallbackConfig, encodeVarint(1)...)

				// 6. MaxPlayers (Int32) - 6
				fallbackConfig = append(fallbackConfig, 0x30)
				fallbackConfig = append(fallbackConfig, encodeVarint(6)...)

				// 9. GameMode (Enum) - Default (0)
				fallbackConfig = append(fallbackConfig, 0x48, 0x00, 0x50, 0x2d) // Mode 0, Timeout 45

				gameDetails = append(gameDetails, 0x1a)
				gameDetails = append(gameDetails, encodeVarint(uint64(len(fallbackConfig)))...)
				gameDetails = append(gameDetails, fallbackConfig...)
			}

			// UserData (Field 5, Repeated PlayerPregameData)
			// PlayerPregameData:
			// Field 1: PlayerId (int32)
			// Field 2: Data (bytes) - Likely internal game data, send empty bytes or minimal

			// Assume Player ID is from the mock player. We need to know that ID.
			// In `getMockPlayerBytes`, the ID is likely embedded.
			// Let's create a dummy entry.
			// PlayerId: 108096 (last part of Game ID maybe? or just 1)
			// Let's define a predictable PlayerID in `getMockPlayerBytes` or just guess '0' or '1'.

			ppData := make([]byte, 0)
			ppData = append(ppData, 0x08)                                // Field 1: PlayerId
			ppData = append(ppData, encodeVarint(uint64(MockUserID))...) // Using named constant
			ppData = append(ppData, 0x12, 0x00)                          // Field 2: Data (Empty bytes)

			gameDetails = append(gameDetails, 0x2a) // Field 5, WireType 2
			gameDetails = append(gameDetails, encodeVarint(uint64(len(ppData)))...)
			gameDetails = append(gameDetails, ppData...)

			// Request 608
			responsePayload = make([]byte, 0)
			responsePayload = append(responsePayload, 0x0a)
			responsePayload = append(responsePayload, encodeVarint(uint64(len(gameDetails)))...)
			responsePayload = append(responsePayload, gameDetails...)

			responseFieldNum = 608

		case 610: // LobbyJoinGameRequest
			fmt.Println("[TCP] Handling LobbyJoinGameRequest")
			hasActiveGame = true
			activeGameID = 4016461897007108096

			responsePayload = []byte{}
			responseFieldNum = 611 // LobbyNewPlayerRequest (usually broadcasted)
			// Wait, JoinGame response is NOT 611. JoinGame usually expects a success message or just stats update.
			// But for now, let's leave it as is if it was working for Quick Match.
			// Quick match used 610 -> ???
			// Check previous successful log for 610 response. It probably triggered `StatusReport` update.

		case 602: // ExitLobbyRequest
			fmt.Println("[TCP] Handling ExitLobbyRequest")
			responsePayload = []byte{}
			responseFieldNum = 603 // LobbyExitedRequest

		case 777: // PingRequest
			fmt.Println("[TCP] Handling PingRequest")
			pingTimestamp := uint64(0)
			pReader := bytes.NewReader(payloadBytes)
			for {
				pTag, err := readVarint(pReader)
				if err != nil {
					break
				}
				pFieldNum := pTag >> 3
				pWireType := pTag & 0x7
				if pFieldNum == 1 && pWireType == 0 { // PingRequest.timestamp
					pingTimestamp, _ = readVarint(pReader)
					break
				} else {
					skipField(pReader, pWireType)
				}
			}
			responsePayload = make([]byte, 0)
			if pingTimestamp != 0 {
				responsePayload = append(responsePayload, 0x08) // field 1 (timestamp)
				responsePayload = append(responsePayload, encodeVarint(pingTimestamp)...)
			}
			responseFieldNum = 777

		default:
			fmt.Printf("[TCP] Warning: Unhandled request number: %d. Sending empty response.\n", requestNumber)
			responsePayload = []byte{}
			responseFieldNum = int(requestNumber)
		}

		message := make([]byte, 0)
		message = append(message, 0x08) // field 1 (request_number)
		if wrapperRequestNumber != 0 {
			message = append(message, encodeVarint(wrapperRequestNumber)...)
		} else {
			message = append(message, encodeVarint(uint64(responseFieldNum))...)
		}

		tagBytes := encodeVarint(uint64(uint32(responseFieldNum)<<3 | 2))
		message = append(message, tagBytes...)
		message = append(message, encodeVarint(uint64(len(responsePayload)))...)
		message = append(message, responsePayload...)

		// Wrap in Packet
		packet := make([]byte, 0)
		packet = append(packet, 0x08) // field 1 (id)
		packet = append(packet, encodeVarint(uint64(packetID))...)
		packet = append(packet, 0x1a) // field 3 (payload/Message)
		packet = append(packet, encodeVarint(uint64(len(message)))...)
		packet = append(packet, message...)

		// Send response
		binary.Write(conn, binary.BigEndian, uint32(len(packet)))
		conn.Write(packet)
		fmt.Printf("[TCP] Sent response Field %d for Request %d\n", responseFieldNum, requestNumber)
	}
}

func readVarint(r *bytes.Reader) (uint64, error) {
	return binary.ReadUvarint(r)
}

func readVarintInt64(r *bytes.Reader) (int64, error) {
	v, err := binary.ReadUvarint(r)
	return int64(v), err
}

func encodeVarint(v uint64) []byte {
	buf := make([]byte, binary.MaxVarintLen64)
	n := binary.PutUvarint(buf, v)
	return buf[:n]
}

func skipField(r *bytes.Reader, wireType uint64) {
	switch wireType {
	case 0: // Varint
		readVarint(r)
	case 1: // 64-bit
		r.Seek(8, io.SeekCurrent)
	case 2: // Length-delimited
		len, _ := readVarint(r)
		r.Seek(int64(len), io.SeekCurrent)
	case 5: // 32-bit
		r.Seek(4, io.SeekCurrent)
	}
}

func encodeDouble(v float64) []byte {
	buf := make([]byte, 8)
	binary.LittleEndian.PutUint64(buf, math.Float64bits(v))
	return buf
}
