package gameapi

import (
	"net/http"

	userv1 "hyapp.local/api/proto/user/v1"
	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/pkg/appcode"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

const diceAvatarFrameResourceType = "avatar_frame"

// enrichDiceMatchParticipants 聚合用户基础资料和佩戴头像框；展示资料失败不影响游戏事实返回。
func (h *Handler) enrichDiceMatchParticipants(request *http.Request, match *diceMatchItemData) {
	if h == nil || match == nil || len(match.Participants) == 0 {
		return
	}
	userIDs := diceParticipantUserIDs(match.Participants)
	users := h.diceUsersByID(request, userIDs)
	frames := h.diceAvatarFramesByID(request, userIDs)
	for index := range match.Participants {
		userID := match.Participants[index].UserIDNumber
		if user, ok := users[userID]; ok {
			match.Participants[index].User = user
		} else {
			match.Participants[index].User = diceUserData{UserID: int64String(userID)}
		}
		if frame, ok := frames[userID]; ok {
			match.Participants[index].AvatarFrame = frame
		}
	}
}

// enrichRoomRPSChallenges 只给房内猜拳响应补展示资料；挑战状态、手势和结算结果仍完全以 game-service 返回的事实为准。
func (h *Handler) enrichRoomRPSChallenges(request *http.Request, challenges []*roomRPSChallengeItemData) {
	if h == nil || len(challenges) == 0 {
		return
	}
	// 先从所有挑战单里去重收集双方 user_id，再批量查 user-service，避免列表页按每一行逐个 RPC 放大延迟。
	userIDs := roomRPSChallengeUserIDs(challenges)
	users := h.diceUsersByID(request, userIDs)
	for _, challenge := range challenges {
		if challenge == nil {
			continue
		}
		// 发起人必须存在于正常挑战单；如果资料查询失败，保留 proto 映射时写入的 user_id，Flutter 仍可用 ID 做兜底展示和补拉。
		if user, ok := users[challenge.Initiator.UserIDNumber]; ok {
			challenge.Initiator.User = user
		}
		// pending 订单还没有应战人，UserIDNumber 为 0 时不能写入空头像昵称，避免客户端误认为已有应战用户。
		if challenge.Challenger.UserIDNumber > 0 {
			if user, ok := users[challenge.Challenger.UserIDNumber]; ok {
				challenge.Challenger.User = user
			}
		}
	}
}

// enrichExploreWinners 只补 Square 播报展示资料；game_code、金额和时间仍以 game-service 的获胜事实为准。
func (h *Handler) enrichExploreWinners(request *http.Request, winners []exploreWinnerItemData) {
	if h == nil || len(winners) == 0 {
		return
	}
	userIDs := exploreWinnerUserIDs(winners)
	users := h.diceUsersByID(request, userIDs)
	for index := range winners {
		userID := winners[index].UserIDNumber
		if userID <= 0 {
			continue
		}
		if user, ok := users[userID]; ok {
			// user-service 是昵称头像的唯一事实来源；game-service 表里的 display_name/avatar_url 只是播报创建时的兜底快照。
			if user.Nickname != "" {
				winners[index].DisplayName = user.Nickname
			}
			if user.Avatar != "" {
				winners[index].AvatarURL = user.Avatar
			}
		}
	}
}

func exploreWinnerUserIDs(winners []exploreWinnerItemData) []int64 {
	seen := make(map[int64]struct{}, len(winners))
	out := make([]int64, 0, len(winners))
	for _, winner := range winners {
		if winner.UserIDNumber <= 0 {
			continue
		}
		if _, ok := seen[winner.UserIDNumber]; ok {
			continue
		}
		seen[winner.UserIDNumber] = struct{}{}
		out = append(out, winner.UserIDNumber)
	}
	return out
}

func roomRPSChallengeUserIDs(challenges []*roomRPSChallengeItemData) []int64 {
	seen := make(map[int64]struct{}, len(challenges)*2)
	out := make([]int64, 0, len(challenges)*2)
	for _, challenge := range challenges {
		if challenge == nil {
			continue
		}
		for _, userID := range []int64{challenge.Initiator.UserIDNumber, challenge.Challenger.UserIDNumber} {
			// 0 表示未应战或 proto 缺省值，不是一个真实用户，不能传给 user-service 批量查询。
			if userID <= 0 {
				continue
			}
			// 同一房间列表里同一个用户可能连续发起多局，去重能减少 user-service 压力，也保证响应顺序只由原 challenges 决定。
			if _, ok := seen[userID]; ok {
				continue
			}
			seen[userID] = struct{}{}
			out = append(out, userID)
		}
	}
	return out
}

func diceParticipantUserIDs(participants []diceParticipantItemData) []int64 {
	seen := make(map[int64]struct{}, len(participants))
	out := make([]int64, 0, len(participants))
	for _, participant := range participants {
		if participant.UserIDNumber <= 0 {
			continue
		}
		if _, ok := seen[participant.UserIDNumber]; ok {
			continue
		}
		seen[participant.UserIDNumber] = struct{}{}
		out = append(out, participant.UserIDNumber)
	}
	return out
}

func (h *Handler) diceUsersByID(request *http.Request, userIDs []int64) map[int64]diceUserData {
	out := make(map[int64]diceUserData, len(userIDs))
	if h.userProfileClient == nil || len(userIDs) == 0 {
		return out
	}
	// 展示资料使用当前请求的 app_code 和语言上下文；不同 App 可能有不同的资料可见性和默认昵称策略。
	resp, err := h.userProfileClient.BatchGetUsers(request.Context(), &userv1.BatchGetUsersRequest{
		Meta:    httpkit.UserMeta(request, ""),
		UserIds: userIDs,
	})
	if err != nil {
		// 资料查询只是展示增强，失败不能吞掉游戏订单；调用方会用 user_id 兜底。
		return out
	}
	for userID, user := range resp.GetUsers() {
		// 返回嵌套 user 只放公开展示字段，不暴露钱包、状态、封禁、隐私配置等内部资料。
		out[userID] = diceUserData{
			UserID:        int64String(user.GetUserId()),
			DisplayUserID: user.GetDisplayUserId(),
			Nickname:      user.GetUsername(),
			Avatar:        user.GetAvatar(),
		}
	}
	return out
}

func (h *Handler) diceAvatarFramesByID(request *http.Request, userIDs []int64) map[int64]map[string]any {
	out := make(map[int64]map[string]any, len(userIDs))
	if h.walletClient == nil || len(userIDs) == 0 {
		return out
	}
	resp, err := h.walletClient.BatchGetUserEquippedResources(request.Context(), &walletv1.BatchGetUserEquippedResourcesRequest{
		RequestId:     httpkit.RequestIDFromContext(request.Context()),
		AppCode:       appcode.FromContext(request.Context()),
		UserIds:       userIDs,
		ResourceTypes: []string{diceAvatarFrameResourceType},
	})
	if err != nil {
		return out
	}
	for _, item := range resp.GetUsers() {
		for _, entitlement := range item.GetResources() {
			resource := entitlement.GetResource()
			if resource.GetResourceType() != diceAvatarFrameResourceType {
				continue
			}
			out[item.GetUserId()] = map[string]any{
				"resource_id":    int64String(resource.GetResourceId()),
				"resource_code":  resource.GetResourceCode(),
				"name":           resource.GetName(),
				"asset_url":      resource.GetAssetUrl(),
				"preview_url":    resource.GetPreviewUrl(),
				"animation_url":  resource.GetAnimationUrl(),
				"metadata_json":  resource.GetMetadataJson(),
				"entitlement_id": entitlement.GetEntitlementId(),
				"expires_at_ms":  entitlement.GetExpiresAtMs(),
			}
			break
		}
	}
	return out
}
