package httproutes

import (
	"net/http"

	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

const (
	APIV1Prefix = "/api/v1"
	APIV2Prefix = "/api/v2"
)

type Wrapper func(http.HandlerFunc) http.Handler

type Config struct {
	PublicWrap  Wrapper
	AuthWrap    Wrapper
	ProfileWrap Wrapper
	Auth        AuthHandlers
	App         AppHandlers
	User        UserHandlers
	Manager     ManagerHandlers
	Room        RoomHandlers
	Message     MessageHandlers
	Task        TaskHandlers
	Level       LevelHandlers
	Achievement AchievementHandlers
	Wallet      WalletHandlers
	VIP         VIPHandlers
	Game        GameHandlers
}

type AuthHandlers struct {
	LoginPassword             http.HandlerFunc
	SetPassword               http.HandlerFunc
	QuickCreateAccount        http.HandlerFunc
	LoginThirdParty           http.HandlerFunc
	RegisterThirdParty        http.HandlerFunc
	CheckThirdPartyRegistered http.HandlerFunc
	RefreshToken              http.HandlerFunc
	Logout                    http.HandlerFunc
}

type AppHandlers struct {
	ListRegistrationCountries     http.HandlerFunc
	ListLoginRiskBlockedCountries http.HandlerFunc
	GetAppBootstrap               http.HandlerFunc
	ListH5Links                   http.HandlerFunc
	ListExploreTabs               http.HandlerFunc
	ListAppBanners                http.HandlerFunc
	ListSplashScreens             http.HandlerFunc
	ListPopups                    http.HandlerFunc
	GetAppVersion                 http.HandlerFunc
	GetResourceGroup              http.HandlerFunc
	ListResourceShopItems         http.HandlerFunc
	PurchaseResourceShopItem      http.HandlerFunc
	ListGifts                     http.HandlerFunc
	ListGiftTabs                  http.HandlerFunc
	ListEmojiPacks                http.HandlerFunc
	IssueTencentIMUserSig         http.HandlerFunc
	IssueTencentRTCToken          http.HandlerFunc
	HandleTencentIMCallback       http.HandlerFunc
	HandleTencentRTCCallback      http.HandlerFunc
	UploadFile                    http.HandlerFunc
	UploadAvatar                  http.HandlerFunc
	UploadUserAvatar              http.HandlerFunc
	UploadRegistrationAvatar      http.HandlerFunc
	ProxyEffectMedia              http.HandlerFunc
	HandlePushToken               http.HandlerFunc
	AppHeartbeat                  http.HandlerFunc
	ReportAppEvents               http.HandlerFunc
}

type UserHandlers struct {
	ResolveDisplayUserID          http.HandlerFunc
	BatchUserProfiles             http.HandlerFunc
	BatchRoomDisplayProfiles      http.HandlerFunc
	BatchUserVoiceRoomPresences   http.HandlerFunc
	GetMyOverview                 http.HandlerFunc
	GetMyInviteOverview           http.HandlerFunc
	GetMyInviteReferrer           http.HandlerFunc
	SearchInviteReferrer          http.HandlerFunc
	BindInviteReferrer            http.HandlerFunc
	GetMyGiftWall                 http.HandlerFunc
	GetMyIdentity                 http.HandlerFunc
	GetMyHostIdentity             http.HandlerFunc
	GetMyRoleSummary              http.HandlerFunc
	SearchHostAgencies            http.HandlerFunc
	ApplyToHostAgency             http.HandlerFunc
	GetAgencyProfile              http.HandlerFunc
	ListAgencyRooms               http.HandlerFunc
	UpdateAgencyProfile           http.HandlerFunc
	GetHostCenterAgency           http.HandlerFunc
	GetHostCenterStats            http.HandlerFunc
	GetHostCenterPlatformPolicy   http.HandlerFunc
	GetAgencyCenterOverview       http.HandlerFunc
	GetAgencyCenterStats          http.HandlerFunc
	GetAgencyCenterPlatformPolicy http.HandlerFunc
	ListAgencyCenterHosts         http.HandlerFunc
	ListAgencyCenterApplications  http.HandlerFunc
	ReviewAgencyCenterApplication http.HandlerFunc
	RemoveAgencyCenterHost        http.HandlerFunc
	InviteAgencyCenterHost        http.HandlerFunc
	GetBDLeaderCenterOverview     http.HandlerFunc
	ListBDLeaderCenterBDs         http.HandlerFunc
	ListBDLeaderCenterAgencies    http.HandlerFunc
	InviteBDLeaderBD              http.HandlerFunc
	InviteBDLeaderAgency          http.HandlerFunc
	GetBDCenterOverview           http.HandlerFunc
	GetBDCenterStats              http.HandlerFunc
	ListBDCenterAgencies          http.HandlerFunc
	InviteBDAgency                http.HandlerFunc
	ListMyRoleInvitations         http.HandlerFunc
	ProcessMyRoleInvitation       http.HandlerFunc
	CompleteMyOnboarding          http.HandlerFunc
	UpdateMyProfile               http.HandlerFunc
	UpdateMyProfileBackground     http.HandlerFunc
	UpdateMyContactInfo           http.HandlerFunc
	ChangeMyCountry               http.HandlerFunc
	ChangeMyDisplayUserID         http.HandlerFunc
	ApplyMyPrettyDisplayUserID    http.HandlerFunc
	ListAvailablePrettyDisplayIDs http.HandlerFunc
	ApplyPrettyDisplayIDFromPool  http.HandlerFunc
	ListMyProfileVisitors         http.HandlerFunc
	ListMyFollowing               http.HandlerFunc
	ListMyFriends                 http.HandlerFunc
	ListMyFriendApplications      http.HandlerFunc
	ListCPApplications            http.HandlerFunc
	AcceptCPApplication           http.HandlerFunc
	RejectCPApplication           http.HandlerFunc
	ListCPRelationships           http.HandlerFunc
	ListCPIntimacyLeaderboard     http.HandlerFunc
	ListCPFormationGiftFeed       http.HandlerFunc
	BreakCPRelationship           http.HandlerFunc
	GetMyProfile                  http.HandlerFunc
	GetUserProfile                http.HandlerFunc
	GetMyAppearance               http.HandlerFunc
	GetUserAppearance             http.HandlerFunc
	ListMyResources               http.HandlerFunc
	EquipMyResource               http.HandlerFunc
	UnequipMyResource             http.HandlerFunc
	UserSocialAction              http.HandlerFunc
	SubmitReport                  http.HandlerFunc
}

type ManagerHandlers struct {
	ListManagerGrantResources  http.HandlerFunc
	GrantManagerResource       http.HandlerFunc
	LookupBusinessUsers        http.HandlerFunc
	GetManagerOverview         http.HandlerFunc
	SearchManagerUsers         http.HandlerFunc
	GrantManagerVIP            http.HandlerFunc
	ManagerBlocks              http.HandlerFunc
	ListManagerBlocks          http.HandlerFunc
	CreateManagerBlock         http.HandlerFunc
	UnblockManagerBlock        http.HandlerFunc
	SetManagerUserLevel        http.HandlerFunc
	CreateManagerBDLeader      http.HandlerFunc
	TransferManagerUserCountry http.HandlerFunc
}

type RoomHandlers struct {
	GetMyRoom               http.HandlerFunc
	ListRoomFeeds           http.HandlerFunc
	ListRoomFilters         http.HandlerFunc
	ListRooms               http.HandlerFunc
	GetCurrentRoom          http.HandlerFunc
	GetRoomSnapshot         http.HandlerFunc
	GetRoomDetail           http.HandlerFunc
	GetRoomShare            http.HandlerFunc
	GetRoomShareLanding     http.HandlerFunc
	ListRoomOnlineUsers     http.HandlerFunc
	ListRoomBannedUsers     http.HandlerFunc
	GetRoomContributionRank http.HandlerFunc
	GetRoomGiftPanel        http.HandlerFunc
	GetRoomRocket           http.HandlerFunc
	FollowRoom              http.HandlerFunc
	CreateRoom              http.HandlerFunc
	UpdateRoomProfile       http.HandlerFunc
	SaveRoomBackground      http.HandlerFunc
	ListRoomBackgrounds     http.HandlerFunc
	SetRoomBackground       http.HandlerFunc
	UploadRoomCover         http.HandlerFunc
	UploadRoomBackground    http.HandlerFunc
	UploadRoomImage         http.HandlerFunc
	SaveRoomBorder          http.HandlerFunc
	ApplyRoomDecoration     http.HandlerFunc
	SendRoomImage           http.HandlerFunc
	JoinRoom                http.HandlerFunc
	RoomHeartbeat           http.HandlerFunc
	LeaveRoom               http.HandlerFunc
	CloseRoom               http.HandlerFunc
	MicUp                   http.HandlerFunc
	MicDown                 http.HandlerFunc
	ChangeMicSeat           http.HandlerFunc
	ConfirmMicPublishing    http.HandlerFunc
	MicHeartbeat            http.HandlerFunc
	SetMicMute              http.HandlerFunc
	SetMicSeatLock          http.HandlerFunc
	SetChatEnabled          http.HandlerFunc
	SetRoomPassword         http.HandlerFunc
	SetRoomAdmin            http.HandlerFunc
	MuteUser                http.HandlerFunc
	KickUser                http.HandlerFunc
	UnbanUser               http.HandlerFunc
	SendGift                http.HandlerFunc
	SendGiftBatch           http.HandlerFunc
	SendGiftV2              http.HandlerFunc
}

type MessageHandlers struct {
	ListMessageTabs          http.HandlerFunc
	MarkInboxSectionRead     http.HandlerFunc
	MarkInboxMessageReadPath http.HandlerFunc
	DeleteInboxMessagePath   http.HandlerFunc
	ListInboxMessages        http.HandlerFunc
	AcceptActionConfirm      http.HandlerFunc
	RejectActionConfirm      http.HandlerFunc
	BatchActionConfirmStatus http.HandlerFunc
	ListActionConfirms       http.HandlerFunc
}

type TaskHandlers struct {
	ListTaskTabs                      http.HandlerFunc
	ClaimTaskReward                   http.HandlerFunc
	GetRegistrationRewardEligibility  http.HandlerFunc
	GetFirstRechargeRewardStatus      http.HandlerFunc
	GetCumulativeRechargeRewardStatus http.HandlerFunc
	GetInviteActivityRewardStatus     http.HandlerFunc
	ClaimInviteActivityReward         http.HandlerFunc
	ListInviteActivityLeaderboard     http.HandlerFunc
	ListInviteActivityInvitees        http.HandlerFunc
	GetSevenDayCheckInStatus          http.HandlerFunc
	SignSevenDayCheckIn               http.HandlerFunc
	ListUserLeaderboards              http.HandlerFunc
	CheckLuckyGift                    http.HandlerFunc
	GetRoomTurnoverRewardStatus       http.HandlerFunc
	GetWeeklyStarCurrent              http.HandlerFunc
	ListWeeklyStarLeaderboard         http.HandlerFunc
	ListWeeklyStarHistory             http.HandlerFunc
	GetCPWeeklyRankStatus             http.HandlerFunc
	GetAgencyOpeningStatus            http.HandlerFunc
	ApplyAgencyOpening                http.HandlerFunc
	ListAgencyOpeningLeaderboard      http.HandlerFunc
	GetWheelConfig                    http.HandlerFunc
	DrawWheel                         http.HandlerFunc
	ListWheelHistory                  http.HandlerFunc
	ListWheelHints                    http.HandlerFunc

	GetCurrentActivityTemplate           http.HandlerFunc
	VisitActivityTemplate                http.HandlerFunc
	ListActivityTemplateTasks            http.HandlerFunc
	ClaimActivityTemplateTask            http.HandlerFunc
	ListActivityTemplateDailyLeaderboard http.HandlerFunc
	ListActivityTemplateTotalLeaderboard http.HandlerFunc
}

type LevelHandlers struct {
	GetMyLevelOverview http.HandlerFunc
	GetLevelTrack      http.HandlerFunc
	ListLevelRewards   http.HandlerFunc
}

type AchievementHandlers struct {
	ListAchievements http.HandlerFunc
	ListMyBadges     http.HandlerFunc
	SetBadgeDisplay  http.HandlerFunc
}

type WalletHandlers struct {
	GetWalletOverview                 http.HandlerFunc
	GetMyBalances                     http.HandlerFunc
	ListRechargeProducts              http.HandlerFunc
	GetH5RechargeContext              http.HandlerFunc
	ListH5RechargeOptions             http.HandlerFunc
	CreateH5RechargeOrder             http.HandlerFunc
	ListTemporaryPayMethods           http.HandlerFunc
	CreateTemporaryPayLink            http.HandlerFunc
	GetTemporaryPayLink               http.HandlerFunc
	ListTemporaryPayLinks             http.HandlerFunc
	SubmitH5RechargeTx                http.HandlerFunc
	GetH5RechargeOrder                http.HandlerFunc
	HandleMifapayNotify               http.HandlerFunc
	HandleV5PayNotify                 http.HandlerFunc
	ConfirmGooglePayment              http.HandlerFunc
	GetDiamondExchangeConfig          http.HandlerFunc
	ListCoinTransactions              http.HandlerFunc
	ListWalletTransactions            http.HandlerFunc
	ListDiamondTransactions           http.HandlerFunc
	ListCoinSellers                   http.HandlerFunc
	TransferCoinFromSeller            http.HandlerFunc
	GetCoinSellerSubSellerPermission  http.HandlerFunc
	InviteCoinSellerSubSeller         http.HandlerFunc
	ListCoinSellerSubSellers          http.HandlerFunc
	TransferCoinSellerToSubSeller     http.HandlerFunc
	GetCoinSellerOverview             http.HandlerFunc
	CreateSubCoinSeller               http.HandlerFunc
	ListSubCoinSellers                http.HandlerFunc
	TransferCoinSellerStockToChild    http.HandlerFunc
	SendDirectGift                    http.HandlerFunc
	GetSalaryWalletOverview           http.HandlerFunc
	GetSalaryWalletHistory            http.HandlerFunc
	SearchSalaryWalletSeller          http.HandlerFunc
	ExchangeSalaryToCoin              http.HandlerFunc
	TransferSalaryToSeller            http.HandlerFunc
	HandleSalaryWalletWithdrawAddress http.HandlerFunc
	WithdrawSalaryWallet              http.HandlerFunc
	GetPointWalletOverview            http.HandlerFunc
	GetPointWalletExchangeConfig      http.HandlerFunc
	WithdrawPointWallet               http.HandlerFunc
	TransferPointWalletToCoinSeller   http.HandlerFunc
	ExchangePointWalletToCoins        http.HandlerFunc
	GetPointDiamondWalletOverview     http.HandlerFunc
	GetPointDiamondWalletHistory      http.HandlerFunc
	ExchangePointDiamondWalletToCoins http.HandlerFunc
	WithdrawPointDiamondWallet        http.HandlerFunc
	TransferPointDiamondToCoinSeller  http.HandlerFunc
	HandlePointDiamondWithdrawAddress http.HandlerFunc
	GetRedPacketConfig                http.HandlerFunc
	ListRoomRedPackets                http.HandlerFunc
	CreateRoomRedPacket               http.HandlerFunc
	GetRedPacket                      http.HandlerFunc
	ClaimRedPacket                    http.HandlerFunc
	GetCurrentRegionIMGroup           http.HandlerFunc
	GetRedPacketIMGroup               http.HandlerFunc
	SendVoiceRoomRedPacket            http.HandlerFunc
	ListActiveRedPackets              http.HandlerFunc
	ClaimVoiceRoomRedPacket           http.HandlerFunc
	GetVoiceRoomRedPacket             http.HandlerFunc
	ListRedPacketClaims               http.HandlerFunc
	ListSentRedPackets                http.HandlerFunc
}

type VIPHandlers struct {
	GetMyVIP               http.HandlerFunc
	ListVIPPackages        http.HandlerFunc
	PurchaseVIP            http.HandlerFunc
	EquipVIPTrialCard      http.HandlerFunc
	UnequipVIPTrialCard    http.HandlerFunc
	TriggerOnlineNotice    http.HandlerFunc
	HandleVIPSettings      http.HandlerFunc
	GetCurrentCoinRebate   http.HandlerFunc
	ListCoinRebateStatuses http.HandlerFunc
	ClaimCoinRebate        http.HandlerFunc
}

type GameHandlers struct {
	ListGames                  http.HandlerFunc
	ListRecentGames            http.HandlerFunc
	ListExploreWinners         http.HandlerFunc
	GetBridgeScript            http.HandlerFunc
	LaunchGame                 http.HandlerFunc
	GetDiceConfig              http.HandlerFunc
	MatchDice                  http.HandlerFunc
	CreateDiceMatch            http.HandlerFunc
	JoinDiceMatch              http.HandlerFunc
	GetDiceMatch               http.HandlerFunc
	RollDiceMatch              http.HandlerFunc
	CancelDiceMatch            http.HandlerFunc
	GetRPSConfig               http.HandlerFunc
	MatchRPS                   http.HandlerFunc
	CreateRPSMatch             http.HandlerFunc
	JoinRPSMatch               http.HandlerFunc
	GetRPSMatch                http.HandlerFunc
	RollRPSMatch               http.HandlerFunc
	CancelRPSMatch             http.HandlerFunc
	ReportSelfGameH5Event      http.HandlerFunc
	GetRoomRPSConfig           http.HandlerFunc
	ListRoomRPSChallenges      http.HandlerFunc
	CreateRoomRPSChallenge     http.HandlerFunc
	AcceptRoomRPSChallenge     http.HandlerFunc
	GetRoomRPSChallenge        http.HandlerFunc
	HandleCallback             http.HandlerFunc
	HandleZGameCallback        http.HandlerFunc
	HandleAMGUser              http.HandlerFunc
	HandleAMGSettlement        http.HandlerFunc
	HandleHotgameGetUserInfo   http.HandlerFunc
	HandleHotgameUpdateBalance http.HandlerFunc
}

type routes struct {
	mux    *http.ServeMux
	config Config
}

// New 只维护 gateway 对外 HTTP 路径到 transport handler 的绑定。
// 业务状态和命令执行仍然全部下沉到 owner service，路由层不承载业务规则。
func New(config Config) http.Handler {
	mux := http.NewServeMux()
	routes := routes{mux: mux, config: config}

	routes.registerAuthRoutes()
	routes.registerAppRoutes()
	routes.registerUserRoutes()
	routes.registerManagerRoutes()
	routes.registerRoomRoutes()
	routes.registerMessageRoutes()
	routes.registerTaskRoutes()
	routes.registerLevelRoutes()
	routes.registerAchievementRoutes()
	routes.registerWalletRoutes()
	routes.registerVIPRoutes()
	routes.registerGameRoutes()

	return mux
}

func (r routes) public(path string, method string, next http.HandlerFunc) {
	r.register(path, method, r.config.PublicWrap, next)
}

func (r routes) rootPublic(path string, method string, next http.HandlerFunc) {
	r.registerRaw(path, method, r.config.PublicWrap, next)
}

func (r routes) auth(path string, method string, next http.HandlerFunc) {
	r.register(path, method, r.config.AuthWrap, next)
}

func (r routes) profile(path string, method string, next http.HandlerFunc) {
	r.register(path, method, r.config.ProfileWrap, next)
}

func (r routes) profileV2(path string, method string, next http.HandlerFunc) {
	r.registerRaw(APIV2Prefix+path, method, r.config.ProfileWrap, next)
}

func (r routes) register(path string, method string, wrap Wrapper, next http.HandlerFunc) {
	r.registerRaw(APIV1Prefix+path, method, wrap, next)
}

func (r routes) registerRaw(path string, method string, wrap Wrapper, next http.HandlerFunc) {
	if method != "" {
		// method 继续走 gateway envelope，而不是使用 ServeMux 自动 405。
		next = httpkit.RequireMethod(method, next)
	}
	r.mux.Handle(path, wrap(next))
}

func (r routes) registerAuthRoutes() {
	h := r.config.Auth
	r.public("/auth/account/login", "", h.LoginPassword)
	r.auth("/auth/password/set", "", h.SetPassword)
	r.public("/auth/account/quick-create", http.MethodPost, h.QuickCreateAccount)
	r.public("/auth/third-party/login", "", h.LoginThirdParty)
	r.public("/auth/third-party/register", http.MethodPost, h.RegisterThirdParty)
	r.public("/auth/third-party/registered/check", http.MethodPost, h.CheckThirdPartyRegistered)
	r.public("/auth/token/refresh", "", h.RefreshToken)
	r.public("/auth/logout", "", h.Logout)
}

func (r routes) registerAppRoutes() {
	h := r.config.App
	r.public("/countries", "", h.ListRegistrationCountries)
	r.public("/login-risk/blocked-countries", http.MethodGet, h.ListLoginRiskBlockedCountries)
	r.public("/app/bootstrap", "", h.GetAppBootstrap)
	r.public("/app/h5-links", "", h.ListH5Links)
	r.public("/app/explore-tabs", http.MethodGet, h.ListExploreTabs)
	r.public("/app/banners", "", h.ListAppBanners)
	r.public("/app/splash-screens", http.MethodGet, h.ListSplashScreens)
	r.public("/app/popups", http.MethodGet, h.ListPopups)
	r.public("/app/version", http.MethodGet, h.GetAppVersion)
	r.public("/resource-groups/{group_id}", "", h.GetResourceGroup)
	r.public("/resource-shop/items", http.MethodGet, h.ListResourceShopItems)
	r.profile("/resource-shop/items/{shop_item_id}/purchase", http.MethodPost, h.PurchaseResourceShopItem)
	r.public("/gifts", "", h.ListGifts)
	r.public("/gift-tabs", http.MethodGet, h.ListGiftTabs)
	r.public("/emoji-packs", http.MethodGet, h.ListEmojiPacks)
	r.profile("/im/usersig", http.MethodGet, h.IssueTencentIMUserSig)
	r.profile("/rtc/token", http.MethodPost, h.IssueTencentRTCToken)
	r.public("/tencent-im/callback", "", h.HandleTencentIMCallback)
	r.public("/tencent-rtc/callback", "", h.HandleTencentRTCCallback)
	r.auth("/files/upload", "", h.UploadFile)
	r.auth("/files/avatar/upload", "", h.UploadAvatar)
	r.profile("/users/me/avatar/upload", http.MethodPost, h.UploadUserAvatar)
	r.public("/files/registration/avatar/upload", http.MethodPost, h.UploadRegistrationAvatar)
	r.public("/media/effects/proxy", http.MethodGet, h.ProxyEffectMedia)
	r.auth("/app/heartbeat", http.MethodPost, h.AppHeartbeat)
	r.public("/app/events", http.MethodPost, h.ReportAppEvents)
	r.profile("/devices/push-token", "", h.HandlePushToken)
}

func (r routes) registerUserRoutes() {
	h := r.config.User
	r.public("/users/by-display-user-id/{display_user_id}", "", h.ResolveDisplayUserID)
	r.public("/users/by-id/{user_id}/profile", http.MethodGet, h.GetUserProfile)
	r.public("/users/by-id/{user_id}/appearance", http.MethodGet, h.GetUserAppearance)
	r.profile("/users/profiles:batch", "", h.BatchUserProfiles)
	r.profile("/users/room-display-profiles:batch", http.MethodGet, h.BatchRoomDisplayProfiles)
	r.profile("/users/voice-room-presence:batch", http.MethodGet, h.BatchUserVoiceRoomPresences)
	r.profile("/users/me/overview", "", h.GetMyOverview)
	r.profile("/users/me/invite-overview", http.MethodGet, h.GetMyInviteOverview)
	r.profile("/users/me/invite-referrer", http.MethodGet, h.GetMyInviteReferrer)
	r.profile("/users/me/invite-referrer/search", http.MethodGet, h.SearchInviteReferrer)
	r.profile("/users/me/invite-referrer/bind", http.MethodPost, h.BindInviteReferrer)
	r.profile("/users/me/gift-wall", http.MethodGet, h.GetMyGiftWall)
	r.auth("/users/me/identity", "", h.GetMyIdentity)
	r.profile("/users/me/host-identity", "", h.GetMyHostIdentity)
	r.profile("/users/me/role-summary", "", h.GetMyRoleSummary)
	r.profile("/host/agencies/search", http.MethodGet, h.SearchHostAgencies)
	r.profile("/host/agency-applications", http.MethodPost, h.ApplyToHostAgency)
	r.profile("/agencies/profile", http.MethodGet, h.GetAgencyProfile)
	r.profile("/agencies/rooms", http.MethodGet, h.ListAgencyRooms)
	r.profile("/agencies/profile/update", http.MethodPost, h.UpdateAgencyProfile)
	r.profile("/host-center/agency", http.MethodGet, h.GetHostCenterAgency)
	r.profile("/host-center/stats", http.MethodGet, h.GetHostCenterStats)
	r.profile("/host-center/platform-policy", http.MethodGet, h.GetHostCenterPlatformPolicy)
	r.profile("/agency-center/overview", http.MethodGet, h.GetAgencyCenterOverview)
	r.profile("/agency-center/stats", http.MethodGet, h.GetAgencyCenterStats)
	r.profile("/agency-center/platform-policy", http.MethodGet, h.GetAgencyCenterPlatformPolicy)
	r.profile("/agency-center/hosts", http.MethodGet, h.ListAgencyCenterHosts)
	r.profile("/agency-center/applications", http.MethodGet, h.ListAgencyCenterApplications)
	r.profile("/agency-center/applications/{application_id}/review", http.MethodPost, h.ReviewAgencyCenterApplication)
	r.profile("/agency-center/hosts/{host_user_id}/remove", http.MethodPost, h.RemoveAgencyCenterHost)
	r.profile("/agency-center/invitations/host", http.MethodPost, h.InviteAgencyCenterHost)
	r.profile("/bd-leader-center/overview", http.MethodGet, h.GetBDLeaderCenterOverview)
	r.profile("/bd-leader-center/bds", http.MethodGet, h.ListBDLeaderCenterBDs)
	r.profile("/bd-leader-center/agencies", http.MethodGet, h.ListBDLeaderCenterAgencies)
	r.profile("/bd-leader/invitations/bd", http.MethodPost, h.InviteBDLeaderBD)
	r.profile("/bd-leader/invitations/agency", http.MethodPost, h.InviteBDLeaderAgency)
	r.profile("/bd-center/overview", http.MethodGet, h.GetBDCenterOverview)
	r.profile("/bd-center/stats", http.MethodGet, h.GetBDCenterStats)
	r.profile("/bd-center/agencies", http.MethodGet, h.ListBDCenterAgencies)
	r.profile("/bd/invitations/agency", http.MethodPost, h.InviteBDAgency)
	r.profile("/role-invitations", http.MethodGet, h.ListMyRoleInvitations)
	r.profile("/role-invitations/{invitation_id}/process", http.MethodPost, h.ProcessMyRoleInvitation)
	r.auth("/users/me/onboarding/complete", "", h.CompleteMyOnboarding)
	r.profile("/users/me/profile/update", "", h.UpdateMyProfile)
	r.profile("/users/me/profile-bg-img", http.MethodPost, h.UpdateMyProfileBackground)
	r.profile("/users/me/contact-info", http.MethodPost, h.UpdateMyContactInfo)
	r.profile("/users/me/country/change", "", h.ChangeMyCountry)
	r.auth("/users/me/display-id/change", "", h.ChangeMyDisplayUserID)
	r.auth("/users/me/display-id/pretty/apply", "", h.ApplyMyPrettyDisplayUserID)
	r.auth("/users/pretty-ids/available", http.MethodGet, h.ListAvailablePrettyDisplayIDs)
	r.auth("/users/pretty-ids/apply", http.MethodPost, h.ApplyPrettyDisplayIDFromPool)
	r.profile("/users/me/visitors", http.MethodGet, h.ListMyProfileVisitors)
	r.profile("/users/me/following", http.MethodGet, h.ListMyFollowing)
	r.profile("/users/me/friends", http.MethodGet, h.ListMyFriends)
	r.profile("/users/me/friend-requests", http.MethodGet, h.ListMyFriendApplications)
	r.profile("/cp/applications", http.MethodGet, h.ListCPApplications)
	r.profile("/cp/applications/{application_id}/accept", http.MethodPost, h.AcceptCPApplication)
	r.profile("/cp/applications/{application_id}/reject", http.MethodPost, h.RejectCPApplication)
	r.profile("/cp/relationships", http.MethodGet, h.ListCPRelationships)
	r.profile("/cp/intimacy-leaderboard", http.MethodGet, h.ListCPIntimacyLeaderboard)
	r.profile("/cp/formation-gift-feed", http.MethodGet, h.ListCPFormationGiftFeed)
	r.profile("/cp/relationships/{relationship_id}/break", http.MethodPost, h.BreakCPRelationship)
	r.profile("/appearance", http.MethodGet, h.GetMyAppearance)
	r.profile("/users/me/appearance", http.MethodGet, h.GetMyAppearance)
	r.profile("/users/me", "", h.GetMyProfile)
	r.profile("/users/me/resources", "", h.ListMyResources)
	r.profile("/users/me/resources/{resource_id}/equip", "", h.EquipMyResource)
	r.profile("/users/me/resources/{resource_type}/unequip", http.MethodDelete, h.UnequipMyResource)
	r.profile("/reports", http.MethodPost, h.SubmitReport)
	r.profile("/users/{user_id}/{action...}", "", h.UserSocialAction)
}

func (r routes) registerManagerRoutes() {
	h := r.config.Manager
	r.profile("/manager-center/overview", http.MethodGet, h.GetManagerOverview)
	r.profile("/manager-center/users/search", http.MethodGet, h.SearchManagerUsers)
	r.profile("/manager-center/resource-grants/resources", http.MethodGet, h.ListManagerGrantResources)
	r.profile("/manager-center/resource-grants", http.MethodPost, h.GrantManagerResource)
	r.profile("/manager-center/vip-grants", http.MethodPost, h.GrantManagerVIP)
	r.profile("/manager-center/blocks", "", h.ManagerBlocks)
	r.profile("/manager-center/blocks/{block_id}/unblock", http.MethodPost, h.UnblockManagerBlock)
	r.profile("/manager-center/levels", http.MethodPost, h.SetManagerUserLevel)
	r.profile("/manager-center/bd-leaders", http.MethodPost, h.CreateManagerBDLeader)
	r.profile("/manager-center/users/country", http.MethodPost, h.TransferManagerUserCountry)
	r.profile("/business/users/lookup", http.MethodGet, h.LookupBusinessUsers)
}

func (r routes) registerRoomRoutes() {
	h := r.config.Room
	r.rootPublic("/share/rooms/{room_id}", http.MethodGet, h.GetRoomShareLanding)
	r.profile("/rooms/filters", http.MethodGet, h.ListRoomFilters)
	r.profile("/rooms/me", http.MethodGet, h.GetMyRoom)
	r.profile("/rooms/feeds", http.MethodGet, h.ListRoomFeeds)
	r.profile("/rooms", http.MethodGet, h.ListRooms)
	r.profile("/rooms/current", http.MethodGet, h.GetCurrentRoom)
	r.profile("/rooms/snapshot", http.MethodGet, h.GetRoomSnapshot)
	r.profile("/rooms/{room_id}/detail", http.MethodGet, h.GetRoomDetail)
	r.profile("/rooms/{room_id}/share", http.MethodGet, h.GetRoomShare)
	r.profile("/rooms/{room_id}/online-users", http.MethodGet, h.ListRoomOnlineUsers)
	r.profile("/rooms/{room_id}/banned-users", http.MethodGet, h.ListRoomBannedUsers)
	r.profile("/rooms/{room_id}/contribution-rank", http.MethodGet, h.GetRoomContributionRank)
	r.profile("/rooms/{room_id}/gift-panel", http.MethodGet, h.GetRoomGiftPanel)
	r.profile("/rooms/{room_id}/rocket", http.MethodGet, h.GetRoomRocket)
	r.profile("/rooms/{room_id}/follow", "", h.FollowRoom)
	r.profile("/rooms/covers/upload", http.MethodPost, h.UploadRoomCover)
	r.profile("/rooms/{room_id}/backgrounds", http.MethodGet, h.ListRoomBackgrounds)
	r.profile("/rooms/{room_id}/backgrounds/upload", http.MethodPost, h.UploadRoomBackground)
	r.profile("/rooms/{room_id}/messages/images/upload", http.MethodPost, h.UploadRoomImage)
	r.profile("/rooms/{room_id}/messages/image", http.MethodPost, h.SendRoomImage)
	r.profile("/rooms/{room_id}/border/save", http.MethodPost, h.SaveRoomBorder)
	r.profile("/rooms/{room_id}/decorations/apply", http.MethodPost, h.ApplyRoomDecoration)
	r.profile("/rooms/create", http.MethodPost, h.CreateRoom)
	r.profile("/rooms/profile/update", http.MethodPost, h.UpdateRoomProfile)
	r.profile("/rooms/backgrounds/save", http.MethodPost, h.SaveRoomBackground)
	r.profile("/rooms/backgrounds/set", http.MethodPost, h.SetRoomBackground)
	r.profile("/rooms/join", http.MethodPost, h.JoinRoom)
	r.profile("/rooms/heartbeat", http.MethodPost, h.RoomHeartbeat)
	r.profile("/rooms/leave", http.MethodPost, h.LeaveRoom)
	r.profile("/rooms/close", http.MethodPost, h.CloseRoom)
	r.profile("/rooms/mic/up", http.MethodPost, h.MicUp)
	r.profile("/rooms/mic/down", http.MethodPost, h.MicDown)
	r.profile("/rooms/mic/change", http.MethodPost, h.ChangeMicSeat)
	r.profile("/rooms/mic/publishing/confirm", http.MethodPost, h.ConfirmMicPublishing)
	r.profile("/rooms/mic/heartbeat", http.MethodPost, h.MicHeartbeat)
	r.profile("/rooms/mic/mute", http.MethodPost, h.SetMicMute)
	r.profile("/rooms/mic/lock", http.MethodPost, h.SetMicSeatLock)
	r.profile("/rooms/chat/enabled", http.MethodPost, h.SetChatEnabled)
	r.profile("/rooms/password", http.MethodPost, h.SetRoomPassword)
	r.profile("/rooms/admin/set", http.MethodPost, h.SetRoomAdmin)
	r.profile("/rooms/user/mute", http.MethodPost, h.MuteUser)
	r.profile("/rooms/user/kick", http.MethodPost, h.KickUser)
	r.profile("/rooms/user/unban", http.MethodPost, h.UnbanUser)
	r.profile("/rooms/gift/send", http.MethodPost, h.SendGift)
	r.profile("/rooms/gift/batch-send", http.MethodPost, h.SendGiftBatch)
	// V2 adds micro-batch identity and committed-result replay while the two V1 routes remain byte-level
	// compatible for already released Flutter versions.
	r.profileV2("/rooms/gift/send", http.MethodPost, h.SendGiftV2)
}

func (r routes) registerMessageRoutes() {
	h := r.config.Message
	r.profile("/messages/tabs", "", h.ListMessageTabs)
	r.profile("/messages/read-all", "", h.MarkInboxSectionRead)
	r.profile("/messages/{message_id}/read", "", h.MarkInboxMessageReadPath)
	r.profile("/messages/{message_id}", "", h.DeleteInboxMessagePath)
	r.profile("/message/action-confirms/{confirm_id}/accept", http.MethodPost, h.AcceptActionConfirm)
	r.profile("/message/action-confirms/{confirm_id}/reject", http.MethodPost, h.RejectActionConfirm)
	r.profile("/message/action-confirms/status", http.MethodPost, h.BatchActionConfirmStatus)
	r.profile("/message/action-confirms", http.MethodGet, h.ListActionConfirms)
	r.profile("/messages", "", h.ListInboxMessages)
}

func (r routes) registerTaskRoutes() {
	h := r.config.Task
	r.profile("/activities/registration-reward/eligibility", http.MethodGet, h.GetRegistrationRewardEligibility)
	r.profile("/activities/first-recharge-reward", http.MethodGet, h.GetFirstRechargeRewardStatus)
	r.profile("/activities/cumulative-recharge-reward", http.MethodGet, h.GetCumulativeRechargeRewardStatus)
	r.profile("/activities/invite-reward", http.MethodGet, h.GetInviteActivityRewardStatus)
	r.profile("/activities/invite-reward/invitees", http.MethodGet, h.ListInviteActivityInvitees)
	r.profile("/activities/invite-reward/leaderboard", http.MethodGet, h.ListInviteActivityLeaderboard)
	r.profile("/activities/invite-reward/claim", http.MethodPost, h.ClaimInviteActivityReward)
	r.profile("/activities/seven-day-checkin", http.MethodGet, h.GetSevenDayCheckInStatus)
	r.profile("/activities/seven-day-checkin/sign", http.MethodPost, h.SignSevenDayCheckIn)
	r.profile("/activities/user-leaderboards", http.MethodGet, h.ListUserLeaderboards)
	r.profile("/activities/room-turnover-reward", http.MethodGet, h.GetRoomTurnoverRewardStatus)
	r.profile("/activities/weekly-star/current", http.MethodGet, h.GetWeeklyStarCurrent)
	r.profile("/activities/weekly-star/leaderboard", http.MethodGet, h.ListWeeklyStarLeaderboard)
	r.profile("/activities/weekly-star/history", http.MethodGet, h.ListWeeklyStarHistory)
	r.profile("/activities/cp-weekly-rank", http.MethodGet, h.GetCPWeeklyRankStatus)
	r.profile("/activities/agency-opening-event", http.MethodGet, h.GetAgencyOpeningStatus)
	r.profile("/activities/agency-opening-event/apply", http.MethodPost, h.ApplyAgencyOpening)
	r.profile("/activities/agency-opening-event/leaderboard", http.MethodGet, h.ListAgencyOpeningLeaderboard)
	r.profile("/activities/templates/current", http.MethodGet, h.GetCurrentActivityTemplate)
	// Banner 落地页携带不可变 template_code，显式读取对应运行态；旧 current
	// 入口继续保留给只允许单活动的历史客户端。
	r.profile("/activities/templates/{template_code}", http.MethodGet, h.GetCurrentActivityTemplate)
	r.profile("/activities/templates/{template_code}/visit", http.MethodPost, h.VisitActivityTemplate)
	r.profile("/activities/templates/{template_code}/tasks", http.MethodGet, h.ListActivityTemplateTasks)
	r.profile("/activities/templates/{template_code}/tasks/{task_key}/claim", http.MethodPost, h.ClaimActivityTemplateTask)
	r.profile("/activities/templates/{template_code}/leaderboards/daily", http.MethodGet, h.ListActivityTemplateDailyLeaderboard)
	r.profile("/activities/templates/{template_code}/leaderboards/total", http.MethodGet, h.ListActivityTemplateTotalLeaderboard)
	r.public("/activity/wheel/{wheel_id}/config", http.MethodGet, h.GetWheelConfig)
	r.profile("/activity/wheel/{wheel_id}/draw", http.MethodPost, h.DrawWheel)
	r.profile("/activity/wheel/{wheel_id}/history", http.MethodPost, h.ListWheelHistory)
	r.profile("/activity/wheel/{wheel_id}/hints", http.MethodPost, h.ListWheelHints)
	// App 侧接口保持稳定；内部仍按 pool_id 走当前 v2 规则、抽奖和返奖实现。
	r.profile("/activities/lucky-gifts/check", http.MethodPost, h.CheckLuckyGift)
	r.profile("/tasks/tabs", http.MethodGet, h.ListTaskTabs)
	r.profile("/tasks/claim", http.MethodPost, h.ClaimTaskReward)
}

func (r routes) registerLevelRoutes() {
	h := r.config.Level
	r.profile("/levels/me/overview", http.MethodGet, h.GetMyLevelOverview)
	r.profile("/levels/tracks/{track}", http.MethodGet, h.GetLevelTrack)
	r.profile("/levels/rewards", http.MethodGet, h.ListLevelRewards)
}

func (r routes) registerAchievementRoutes() {
	h := r.config.Achievement
	r.public("/achievements", http.MethodGet, h.ListAchievements)
	r.profile("/badges/me", http.MethodGet, h.ListMyBadges)
	r.profile("/badges/display", http.MethodPost, h.SetBadgeDisplay)
}

func (r routes) registerWalletRoutes() {
	h := r.config.Wallet
	r.profile("/wallet/me/overview", http.MethodGet, h.GetWalletOverview)
	r.profile("/wallet/me/balances", "", h.GetMyBalances)
	r.profile("/wallet/recharge/products", http.MethodGet, h.ListRechargeProducts)
	r.public("/recharge/h5/context", http.MethodGet, h.GetH5RechargeContext)
	r.public("/recharge/h5/options", http.MethodGet, h.ListH5RechargeOptions)
	r.public("/recharge/h5/orders", http.MethodPost, h.CreateH5RechargeOrder)
	r.public("/recharge/h5/temporary/methods", http.MethodGet, h.ListTemporaryPayMethods)
	r.public("/recharge/h5/temporary/orders", http.MethodPost, h.CreateTemporaryPayLink)
	r.public("/recharge/h5/temporary/order-list", http.MethodGet, h.ListTemporaryPayLinks)
	r.public("/recharge/h5/temporary/orders/{order_id}", http.MethodGet, h.GetTemporaryPayLink)
	r.public("/recharge/h5/orders/{order_id}/tx", http.MethodPost, h.SubmitH5RechargeTx)
	r.public("/recharge/h5/orders/{order_id}", http.MethodGet, h.GetH5RechargeOrder)
	r.public("/payment/mifapay/notify", http.MethodPost, h.HandleMifapayNotify)
	r.public("/payment/v5pay/notify", http.MethodPost, h.HandleV5PayNotify)
	r.profile("/wallet/payments/google/confirm", http.MethodPost, h.ConfirmGooglePayment)
	r.profile("/wallet/diamond-exchange/config", http.MethodGet, h.GetDiamondExchangeConfig)
	r.profile("/wallet/coin-transactions", http.MethodGet, h.ListCoinTransactions)
	r.profile("/wallet/transactions", http.MethodGet, h.ListWalletTransactions)
	r.profile("/wallet/diamond-transactions", http.MethodGet, h.ListDiamondTransactions)
	r.profile("/wallet/coin-sellers", http.MethodGet, h.ListCoinSellers)
	r.profile("/wallet/coin-seller/transfer", "", h.TransferCoinFromSeller)
	r.profile("/wallet/coin-seller/sub-sellers/permission", http.MethodGet, h.GetCoinSellerSubSellerPermission)
	r.profile("/wallet/coin-seller/sub-sellers", "", func(writer http.ResponseWriter, request *http.Request) {
		switch request.Method {
		case http.MethodGet:
			h.ListCoinSellerSubSellers(writer, request)
		case http.MethodPost:
			h.InviteCoinSellerSubSeller(writer, request)
		default:
			httpkit.RequireMethod(http.MethodGet, h.ListCoinSellerSubSellers)(writer, request)
		}
	})
	r.profile("/wallet/coin-seller/sub-sellers/transfer", http.MethodPost, h.TransferCoinSellerToSubSeller)
	r.profile("/coin-seller/overview", http.MethodGet, h.GetCoinSellerOverview)
	r.profile("/coin-seller/sub-sellers", "", func(writer http.ResponseWriter, request *http.Request) {
		switch request.Method {
		case http.MethodGet:
			h.ListSubCoinSellers(writer, request)
		case http.MethodPost:
			h.CreateSubCoinSeller(writer, request)
		default:
			httpkit.RequireMethod(http.MethodGet, h.ListSubCoinSellers)(writer, request)
		}
	})
	r.profile("/coin-seller/sub-sellers/{child_user_id}/transfer", http.MethodPost, h.TransferCoinSellerStockToChild)
	r.profile("/messages/direct-gifts/send", http.MethodPost, h.SendDirectGift)
	r.profile("/salary-wallet/overview", http.MethodGet, h.GetSalaryWalletOverview)
	r.profile("/salary-wallet/history", http.MethodGet, h.GetSalaryWalletHistory)
	r.profile("/salary-wallet/coin-sellers/search", http.MethodGet, h.SearchSalaryWalletSeller)
	r.profile("/salary-wallet/exchange", http.MethodPost, h.ExchangeSalaryToCoin)
	r.profile("/salary-wallet/transfer-to-coin-seller", http.MethodPost, h.TransferSalaryToSeller)
	r.profile("/salary-wallet/withdraw-address", "", h.HandleSalaryWalletWithdrawAddress)
	r.profile("/salary-wallet/withdraw", http.MethodPost, h.WithdrawSalaryWallet)
	r.profile("/point-wallet/overview", http.MethodGet, h.GetPointWalletOverview)
	r.profile("/point-wallet/exchange-config", http.MethodGet, h.GetPointWalletExchangeConfig)
	r.profile("/point-wallet/withdraw", http.MethodPost, h.WithdrawPointWallet)
	r.profile("/point-wallet/transfer-to-coin-seller", http.MethodPost, h.TransferPointWalletToCoinSeller)
	r.profile("/point-wallet/exchange-to-coins", http.MethodPost, h.ExchangePointWalletToCoins)
	// 永久钻石积分钱包只要求有效登录。各 handler 按资金动作自行 GetUser 获取实时 region，
	// 不能让 profile-completed 中间件阻断已有成功 command_id 的幂等重试。
	r.auth("/point-diamond-wallet/overview", http.MethodGet, h.GetPointDiamondWalletOverview)
	r.auth("/point-diamond-wallet/history", http.MethodGet, h.GetPointDiamondWalletHistory)
	r.auth("/point-diamond-wallet/exchange-to-coins", http.MethodPost, h.ExchangePointDiamondWalletToCoins)
	r.auth("/point-diamond-wallet/withdraw", http.MethodPost, h.WithdrawPointDiamondWallet)
	r.auth("/point-diamond-wallet/transfer-to-coin-seller", http.MethodPost, h.TransferPointDiamondToCoinSeller)
	r.auth("/point-diamond-wallet/withdraw-address", "", h.HandlePointDiamondWithdrawAddress)
	r.profile("/red-packets/config", http.MethodGet, h.GetRedPacketConfig)
	r.profile("/rooms/{room_id}/red-packets", http.MethodGet, h.ListRoomRedPackets)
	r.profile("/rooms/{room_id}/red-packets/create", http.MethodPost, h.CreateRoomRedPacket)
	r.profile("/red-packets/{packet_id}", http.MethodGet, h.GetRedPacket)
	r.profile("/red-packets/{packet_id}/claim", http.MethodPost, h.ClaimRedPacket)
	r.profile("/app/region-im-group/current", http.MethodGet, h.GetCurrentRegionIMGroup)
	r.profile("/app/voice-room/red-packet/im-group", http.MethodGet, h.GetRedPacketIMGroup)
	r.profile("/app/voice-room/red-packet/config", http.MethodGet, h.GetRedPacketConfig)
	r.profile("/app/voice-room/red-packet/send", http.MethodPost, h.SendVoiceRoomRedPacket)
	r.profile("/app/voice-room/red-packet/active", http.MethodGet, h.ListActiveRedPackets)
	r.profile("/app/voice-room/red-packet/claim", http.MethodPost, h.ClaimVoiceRoomRedPacket)
	r.profile("/app/voice-room/red-packet/detail", http.MethodGet, h.GetVoiceRoomRedPacket)
	r.profile("/app/voice-room/red-packet/claim-records", http.MethodGet, h.ListRedPacketClaims)
	r.profile("/app/voice-room/red-packet/sent-records", http.MethodGet, h.ListSentRedPackets)
}

func (r routes) registerVIPRoutes() {
	h := r.config.VIP
	r.profile("/vip/me", http.MethodGet, h.GetMyVIP)
	r.profile("/vip/packages", http.MethodGet, h.ListVIPPackages)
	r.profile("/vip/purchase", http.MethodPost, h.PurchaseVIP)
	r.profile("/vip/trial-cards/{entitlement_id}/equip", http.MethodPost, h.EquipVIPTrialCard)
	r.profile("/vip/trial-cards/equipped", http.MethodDelete, h.UnequipVIPTrialCard)
	r.profile("/vip/online-notice", http.MethodPost, h.TriggerOnlineNotice)
	r.profile("/vip/settings", "", h.HandleVIPSettings)
	r.profile("/vip/coin-rebates/current", http.MethodGet, h.GetCurrentCoinRebate)
	r.profile("/vip/coin-rebates/statuses", http.MethodPost, h.ListCoinRebateStatuses)
	r.profile("/vip/coin-rebates/{rebate_id}/claim", http.MethodPost, h.ClaimCoinRebate)
}

func (r routes) registerGameRoutes() {
	h := r.config.Game
	r.profile("/games", http.MethodGet, h.ListGames)
	r.profile("/games/recent", http.MethodGet, h.ListRecentGames)
	r.profile("/games/explore-winners", http.MethodGet, h.ListExploreWinners)
	r.profile("/games/bridge-script", http.MethodGet, h.GetBridgeScript)
	r.profile("/games/{game_id}/launch", http.MethodPost, h.LaunchGame)
	r.profile("/games/dice/config", http.MethodGet, h.GetDiceConfig)
	r.profile("/games/dice/match", http.MethodPost, h.MatchDice)
	r.profile("/games/dice/matches", http.MethodPost, h.CreateDiceMatch)
	r.profile("/games/dice/matches/{match_id}/join", http.MethodPost, h.JoinDiceMatch)
	r.profile("/games/dice/matches/{match_id}", http.MethodGet, h.GetDiceMatch)
	r.profile("/games/dice/matches/{match_id}/roll", http.MethodPost, h.RollDiceMatch)
	r.profile("/games/dice/matches/{match_id}/cancel", http.MethodPost, h.CancelDiceMatch)
	r.profile("/games/rps/config", http.MethodGet, h.GetRPSConfig)
	r.profile("/games/rps/match", http.MethodPost, h.MatchRPS)
	r.profile("/games/rps/matches", http.MethodPost, h.CreateRPSMatch)
	r.profile("/games/rps/matches/{match_id}/join", http.MethodPost, h.JoinRPSMatch)
	r.profile("/games/rps/matches/{match_id}", http.MethodGet, h.GetRPSMatch)
	r.profile("/games/rps/matches/{match_id}/roll", http.MethodPost, h.RollRPSMatch)
	r.profile("/games/rps/matches/{match_id}/cancel", http.MethodPost, h.CancelRPSMatch)
	r.profile("/games/self/report-events", http.MethodPost, h.ReportSelfGameH5Event)
	r.profile("/games/room-rps/config", http.MethodGet, h.GetRoomRPSConfig)
	r.profile("/games/room-rps/challenges", http.MethodGet, h.ListRoomRPSChallenges)
	// 当前 ServeMux 包装按 path 注册，不能把 GET 列表和 POST 创建同时挂在 /challenges；创建动作单独放 /create，避免路由覆盖。
	r.profile("/games/room-rps/challenges/create", http.MethodPost, h.CreateRoomRPSChallenge)
	r.profile("/games/room-rps/challenges/{challenge_id}", http.MethodGet, h.GetRoomRPSChallenge)
	// 应战只挂在具体 challenge 下，保证客户端必须拿到服务端生成的挑战单 ID 才能进入锁单流程。
	r.profile("/games/room-rps/challenges/{challenge_id}/accept", http.MethodPost, h.AcceptRoomRPSChallenge)
	// 热游后台只能配置根路径回调；这里仍复用 public wrapper，让请求日志、request_id 和 app_code 解析保持一致。
	r.rootPublic("/getUserInfo", http.MethodPost, h.HandleHotgameGetUserInfo)
	r.rootPublic("/updateBalance", http.MethodPost, h.HandleHotgameUpdateBalance)
	r.public("/zgame/callback/{operation...}", http.MethodPost, h.HandleZGameCallback)
	// AMG 后台只配置 api_url，厂商会固定追加 /amg/user 与 /amg/settlement；因此两条路径不能复用带 operation 的通用 POST 路由。
	r.public("/amg/user", http.MethodGet, h.HandleAMGUser)
	r.public("/amg/settlement", http.MethodPost, h.HandleAMGSettlement)
	r.public("/game-callbacks/{platform_code}/{operation}", http.MethodPost, h.HandleCallback)
}
