package backendclient import ( "context" "net/http" "net/url" ) // The structs below mirror the backend's JSON DTOs (backend/internal/server // /dto.go). The transcode layer maps them to and from the FlatBuffers edge // payloads. // SessionResp is the credential minted by an auth operation. type SessionResp struct { Token string `json:"token"` UserID string `json:"user_id"` IsGuest bool `json:"is_guest"` DisplayName string `json:"display_name"` } // ProfileResp is an account's own profile. type ProfileResp struct { UserID string `json:"user_id"` DisplayName string `json:"display_name"` PreferredLanguage string `json:"preferred_language"` TimeZone string `json:"time_zone"` HintBalance int `json:"hint_balance"` BlockChat bool `json:"block_chat"` BlockFriendRequests bool `json:"block_friend_requests"` IsGuest bool `json:"is_guest"` } // TileJSON is one placed tile, used in both play requests and move responses. type TileJSON struct { Row int `json:"row"` Col int `json:"col"` Letter string `json:"letter"` Blank bool `json:"blank"` } // MoveRecordResp is a decoded move. type MoveRecordResp struct { Player int `json:"player"` Action string `json:"action"` Dir string `json:"dir"` MainRow int `json:"main_row"` MainCol int `json:"main_col"` Tiles []TileJSON `json:"tiles"` Words []string `json:"words"` Count int `json:"count"` Score int `json:"score"` Total int `json:"total"` } // SeatResp is one seat's public standing. type SeatResp struct { Seat int `json:"seat"` AccountID string `json:"account_id"` Score int `json:"score"` HintsUsed int `json:"hints_used"` IsWinner bool `json:"is_winner"` } // GameResp is the shared game summary. type GameResp struct { ID string `json:"id"` Variant string `json:"variant"` DictVersion string `json:"dict_version"` Status string `json:"status"` Players int `json:"players"` ToMove int `json:"to_move"` TurnTimeoutSecs int `json:"turn_timeout_secs"` MoveCount int `json:"move_count"` EndReason string `json:"end_reason"` Seats []SeatResp `json:"seats"` } // MoveResultResp is the outcome of a committed move. type MoveResultResp struct { Move MoveRecordResp `json:"move"` Game GameResp `json:"game"` } // StateResp is a player's view of a game. type StateResp struct { Game GameResp `json:"game"` Seat int `json:"seat"` Rack []string `json:"rack"` BagLen int `json:"bag_len"` HintsRemaining int `json:"hints_remaining"` } // MatchResp reports an auto-match outcome. type MatchResp struct { Matched bool `json:"matched"` Game *GameResp `json:"game,omitempty"` } // ChatResp is a stored chat message. type ChatResp struct { ID string `json:"id"` GameID string `json:"game_id"` SenderID string `json:"sender_id"` Kind string `json:"kind"` Body string `json:"body"` CreatedAtUnix int64 `json:"created_at_unix"` } // TelegramAuth provisions/finds the Telegram account and mints a session. func (c *Client) TelegramAuth(ctx context.Context, externalID string) (SessionResp, error) { var out SessionResp err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/telegram", "", "", map[string]string{"external_id": externalID}, &out) return out, err } // GuestAuth provisions a guest account and mints a session. func (c *Client) GuestAuth(ctx context.Context) (SessionResp, error) { var out SessionResp err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/guest", "", "", struct{}{}, &out) return out, err } // EmailRequest asks the backend to mail a login code. func (c *Client) EmailRequest(ctx context.Context, email string) error { return c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/email/request", "", "", map[string]string{"email": email}, nil) } // EmailLogin verifies a login code and mints a session. func (c *Client) EmailLogin(ctx context.Context, email, code string) (SessionResp, error) { var out SessionResp err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/email/login", "", "", map[string]string{"email": email, "code": code}, &out) return out, err } // ResolveSession maps a token to its account id (gateway session-cache miss). func (c *Client) ResolveSession(ctx context.Context, token string) (string, error) { var out struct { UserID string `json:"user_id"` } err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/resolve", "", "", map[string]string{"token": token}, &out) return out.UserID, err } // Profile returns the authenticated account's profile. func (c *Client) Profile(ctx context.Context, userID string) (ProfileResp, error) { var out ProfileResp err := c.do(ctx, http.MethodGet, "/api/v1/user/profile", userID, "", nil, &out) return out, err } // SubmitPlay commits a placement on the player's turn. func (c *Client) SubmitPlay(ctx context.Context, userID, gameID, dir string, tiles []TileJSON) (MoveResultResp, error) { var out MoveResultResp body := map[string]any{"dir": dir, "tiles": tiles} err := c.do(ctx, http.MethodPost, "/api/v1/user/games/"+url.PathEscape(gameID)+"/play", userID, "", body, &out) return out, err } // GameState returns the player's view of a game. func (c *Client) GameState(ctx context.Context, userID, gameID string) (StateResp, error) { var out StateResp err := c.do(ctx, http.MethodGet, "/api/v1/user/games/"+url.PathEscape(gameID)+"/state", userID, "", nil, &out) return out, err } // Enqueue joins the auto-match pool for a variant. func (c *Client) Enqueue(ctx context.Context, userID, variant string) (MatchResp, error) { var out MatchResp err := c.do(ctx, http.MethodPost, "/api/v1/user/lobby/enqueue", userID, "", map[string]string{"variant": variant}, &out) return out, err } // Poll reports whether the caller has been paired since queueing. func (c *Client) Poll(ctx context.Context, userID string) (MatchResp, error) { var out MatchResp err := c.do(ctx, http.MethodGet, "/api/v1/user/lobby/poll", userID, "", nil, &out) return out, err } // ChatPost stores a chat message, forwarding the client IP for moderation. func (c *Client) ChatPost(ctx context.Context, userID, gameID, body, clientIP string) (ChatResp, error) { var out ChatResp err := c.do(ctx, http.MethodPost, "/api/v1/user/games/"+url.PathEscape(gameID)+"/chat", userID, clientIP, map[string]string{"body": body}, &out) return out, err }