package roomclient

import (
	"context"
	"fmt"
	"strings"
	"time"

	"hyapp-admin-server/internal/appctx"
	roomv1 "hyapp.local/api/proto/room/v1"

	"google.golang.org/grpc"
)

type Client interface {
	ListRooms(ctx context.Context, req ListRoomsRequest) (*ListRoomsResult, error)
	GetRoom(ctx context.Context, req GetRoomRequest) (*Room, error)
	UpdateRoom(ctx context.Context, req UpdateRoomRequest) (*CloseRoomResult, error)
	DeleteRoom(ctx context.Context, req DeleteRoomRequest) (*CloseRoomResult, error)
	GetRoomRocketConfig(ctx context.Context) (RoomRocketConfig, error)
	UpdateRoomRocketConfig(ctx context.Context, req UpdateRoomRocketConfigRequest) (RoomRocketConfig, error)
	GetRoomSeatConfig(ctx context.Context) (RoomSeatConfig, error)
	UpdateRoomSeatConfig(ctx context.Context, req UpdateRoomSeatConfigRequest) (RoomSeatConfig, error)
	ListRoomPins(ctx context.Context, req ListRoomPinsRequest) (ListRoomPinsResult, error)
	CreateRoomPin(ctx context.Context, req CreateRoomPinRequest) (RoomPin, error)
	CancelRoomPin(ctx context.Context, req CancelRoomPinRequest) (RoomPin, error)
	CloseRoom(ctx context.Context, req CloseRoomRequest) (*CloseRoomResult, error)
	ReopenRoom(ctx context.Context, req ReopenRoomRequest) (*CloseRoomResult, error)
}

type ListRoomsRequest struct {
	Page          int
	PageSize      int
	Keyword       string
	Status        string
	RegionID      int64
	OwnerUserID   int64
	SortBy        string
	SortDirection string
}

type ListRoomsResult struct {
	Rooms        []Room
	Total        int64
	ServerTimeMs int64
}

type GetRoomRequest struct {
	RoomID string
}

type UpdateRoomRequest struct {
	RequestID       string
	RoomID          string
	ActorUserID     int64
	Title           *string
	CoverURL        *string
	Description     *string
	Mode            *string
	Status          *string
	VisibleRegionID *int64
	CloseReason     string
	AdminID         uint64
	AdminName       string
}

type DeleteRoomRequest struct {
	RequestID   string
	RoomID      string
	ActorUserID int64
	AdminID     uint64
	AdminName   string
}

type Room struct {
	RoomID            string
	RoomShortID       string
	VisibleRegionID   int64
	OwnerUserID       int64
	Title             string
	CoverURL          string
	Mode              string
	Status            string
	CloseReason       string
	ClosedByAdminID   uint64
	ClosedByAdminName string
	ClosedAtMS        int64
	Heat              int64
	OnlineCount       int32
	SeatCount         int32
	OccupiedSeatCount int32
	SortScore         int64
	CreatedAtMS       int64
	UpdatedAtMS       int64
}

type RoomRocketConfig struct {
	AppCode           string
	Enabled           bool
	ConfigVersion     int64
	FuelSource        string
	LaunchDelayMS     int64
	BroadcastEnabled  bool
	BroadcastScope    string
	BroadcastDelayMS  int64
	RewardStackPolicy string
	Levels            []RoomRocketLevelConfig
	GiftFuelRules     []GiftFuelRuleConfig
	UpdatedByAdminID  int64
	CreatedAtMS       int64
	UpdatedAtMS       int64
}

type RoomRocketLevelConfig struct {
	Level              int32
	FuelThreshold      int64
	CoverURL           string
	AnimationURL       string
	LaunchAnimationURL string
	LaunchedImageURL   string
	InRoomRewards      []RoomRocketRewardItem
	Top1Rewards        []RoomRocketRewardItem
	IgniterRewards     []RoomRocketRewardItem
}

type RoomRocketRewardItem struct {
	RewardItemID    string
	ResourceGroupID int64
	Weight          int64
	Quantity        int64
	DisplayName     string
	IconURL         string
}

type GiftFuelRuleConfig struct {
	RuleID        string
	GiftID        string
	GiftTypeCode  string
	MultiplierPPM int64
	FixedFuel     int64
	Excluded      bool
}

type UpdateRoomRocketConfigRequest struct {
	Config  RoomRocketConfig
	AdminID int64
}

type RoomSeatConfig struct {
	CandidateSeatCounts []int32
	AllowedSeatCounts   []int32
	DefaultSeatCount    int32
	UpdatedAtMS         int64
}

type UpdateRoomSeatConfigRequest struct {
	AllowedSeatCounts []int32
	DefaultSeatCount  int32
}

type RoomPinRoom struct {
	RoomID          string
	RoomShortID     string
	VisibleRegionID int64
	OwnerUserID     int64
	Title           string
	CoverURL        string
	Status          string
}

type RoomPin struct {
	ID                 int64
	VisibleRegionID    int64
	PinType            string
	RoomID             string
	Weight             int64
	Status             string
	PinnedAtMS         int64
	ExpiresAtMS        int64
	CancelledAtMS      int64
	CreatedByAdminID   uint64
	CancelledByAdminID uint64
	CreatedAtMS        int64
	UpdatedAtMS        int64
	Room               RoomPinRoom
}

type ListRoomPinsRequest struct {
	Page     int
	PageSize int
	Keyword  string
	Status   string
	RegionID int64
	PinType  string
}

type ListRoomPinsResult struct {
	Pins  []RoomPin
	Total int64
}

type CreateRoomPinRequest struct {
	RoomID       string
	PinType      string
	Weight       int64
	DurationDays int64
	PinnedAtMS   int64
	ExpiresAtMS  int64
	AdminID      uint64
}

type CancelRoomPinRequest struct {
	PinID   int64
	AdminID uint64
}

type CloseRoomRequest struct {
	RequestID   string
	RoomID      string
	ActorUserID int64
	Reason      string
}

type CloseRoomResult struct {
	Applied       bool
	RoomVersion   int64
	ServerTimeMs  int64
	RoomStatus    string
	OnlineUserNum int
}

type ReopenRoomRequest struct {
	RequestID   string
	RoomID      string
	ActorUserID int64
}

type GRPCClient struct {
	client      roomv1.RoomCommandServiceClient
	queryClient roomv1.RoomQueryServiceClient
}

func NewGRPC(conn grpc.ClientConnInterface) *GRPCClient {
	return &GRPCClient{client: roomv1.NewRoomCommandServiceClient(conn), queryClient: roomv1.NewRoomQueryServiceClient(conn)}
}

func (c *GRPCClient) ListRooms(ctx context.Context, req ListRoomsRequest) (*ListRoomsResult, error) {
	resp, err := c.queryClient.AdminListRooms(ctx, &roomv1.AdminListRoomsRequest{
		Meta:            requestMeta(ctx, "", 0, "admin-list-rooms"),
		Page:            int32(req.Page),
		PageSize:        int32(req.PageSize),
		Keyword:         strings.TrimSpace(req.Keyword),
		Status:          strings.TrimSpace(req.Status),
		VisibleRegionId: req.RegionID,
		OwnerUserId:     req.OwnerUserID,
		SortBy:          strings.TrimSpace(req.SortBy),
		SortDirection:   strings.TrimSpace(req.SortDirection),
	})
	if err != nil {
		return nil, err
	}
	rooms := make([]Room, 0, len(resp.GetRooms()))
	for _, item := range resp.GetRooms() {
		rooms = append(rooms, roomFromProto(item))
	}
	return &ListRoomsResult{Rooms: rooms, Total: resp.GetTotal(), ServerTimeMs: resp.GetServerTimeMs()}, nil
}

func (c *GRPCClient) GetRoom(ctx context.Context, req GetRoomRequest) (*Room, error) {
	resp, err := c.queryClient.AdminGetRoom(ctx, &roomv1.AdminGetRoomRequest{
		Meta:   requestMeta(ctx, strings.TrimSpace(req.RoomID), 0, "admin-get-room"),
		RoomId: strings.TrimSpace(req.RoomID),
	})
	if err != nil {
		return nil, err
	}
	room := roomFromProto(resp.GetRoom())
	return &room, nil
}

func (c *GRPCClient) UpdateRoom(ctx context.Context, req UpdateRoomRequest) (*CloseRoomResult, error) {
	resp, err := c.client.AdminUpdateRoom(ctx, &roomv1.AdminUpdateRoomRequest{
		Meta:            requestMeta(ctx, req.RoomID, req.ActorUserID, firstNonEmpty(req.RequestID, "admin-update-room")),
		Title:           req.Title,
		CoverUrl:        req.CoverURL,
		Description:     req.Description,
		Mode:            req.Mode,
		Status:          req.Status,
		VisibleRegionId: req.VisibleRegionID,
		CloseReason:     strings.TrimSpace(req.CloseReason),
		AdminId:         req.AdminID,
		AdminName:       strings.TrimSpace(req.AdminName),
	})
	if err != nil {
		return nil, err
	}
	return closeRoomResultFromCommand(resp.GetResult(), resp.GetRoom()), nil
}

func (c *GRPCClient) DeleteRoom(ctx context.Context, req DeleteRoomRequest) (*CloseRoomResult, error) {
	resp, err := c.client.AdminDeleteRoom(ctx, &roomv1.AdminDeleteRoomRequest{
		Meta:      requestMeta(ctx, req.RoomID, req.ActorUserID, firstNonEmpty(req.RequestID, "admin-delete-room")),
		AdminId:   req.AdminID,
		AdminName: strings.TrimSpace(req.AdminName),
	})
	if err != nil {
		return nil, err
	}
	return closeRoomResultFromCommand(resp.GetResult(), resp.GetRoom()), nil
}

func (c *GRPCClient) GetRoomRocketConfig(ctx context.Context) (RoomRocketConfig, error) {
	resp, err := c.queryClient.AdminGetRoomRocketConfig(ctx, &roomv1.AdminGetRoomRocketConfigRequest{
		Meta: requestMeta(ctx, "", 0, "admin-get-room-rocket-config"),
	})
	if err != nil {
		return RoomRocketConfig{}, err
	}
	return roomRocketConfigFromProto(resp.GetConfig()), nil
}

func (c *GRPCClient) UpdateRoomRocketConfig(ctx context.Context, req UpdateRoomRocketConfigRequest) (RoomRocketConfig, error) {
	resp, err := c.client.AdminUpdateRoomRocketConfig(ctx, &roomv1.AdminUpdateRoomRocketConfigRequest{
		Meta:    requestMeta(ctx, "", req.AdminID, "admin-update-room-rocket-config"),
		Config:  roomRocketConfigToProto(req.Config),
		AdminId: req.AdminID,
	})
	if err != nil {
		return RoomRocketConfig{}, err
	}
	return roomRocketConfigFromProto(resp.GetConfig()), nil
}

func (c *GRPCClient) GetRoomSeatConfig(ctx context.Context) (RoomSeatConfig, error) {
	resp, err := c.queryClient.AdminGetRoomSeatConfig(ctx, &roomv1.AdminGetRoomSeatConfigRequest{Meta: requestMeta(ctx, "", 0, "admin-get-room-seat-config")})
	if err != nil {
		return RoomSeatConfig{}, err
	}
	return roomSeatConfigFromProto(resp.GetConfig()), nil
}

func (c *GRPCClient) UpdateRoomSeatConfig(ctx context.Context, req UpdateRoomSeatConfigRequest) (RoomSeatConfig, error) {
	resp, err := c.client.AdminUpdateRoomSeatConfig(ctx, &roomv1.AdminUpdateRoomSeatConfigRequest{
		Meta:              requestMeta(ctx, "", 0, "admin-update-room-seat-config"),
		AllowedSeatCounts: req.AllowedSeatCounts,
		DefaultSeatCount:  req.DefaultSeatCount,
	})
	if err != nil {
		return RoomSeatConfig{}, err
	}
	return roomSeatConfigFromProto(resp.GetConfig()), nil
}

func (c *GRPCClient) ListRoomPins(ctx context.Context, req ListRoomPinsRequest) (ListRoomPinsResult, error) {
	resp, err := c.queryClient.AdminListRoomPins(ctx, &roomv1.AdminListRoomPinsRequest{
		Meta:            requestMeta(ctx, "", 0, "admin-list-room-pins"),
		Page:            int32(req.Page),
		PageSize:        int32(req.PageSize),
		Keyword:         strings.TrimSpace(req.Keyword),
		Status:          strings.TrimSpace(req.Status),
		VisibleRegionId: req.RegionID,
		PinType:         strings.TrimSpace(req.PinType),
	})
	if err != nil {
		return ListRoomPinsResult{}, err
	}
	pins := make([]RoomPin, 0, len(resp.GetPins()))
	for _, pin := range resp.GetPins() {
		pins = append(pins, roomPinFromProto(pin))
	}
	return ListRoomPinsResult{Pins: pins, Total: resp.GetTotal()}, nil
}

func (c *GRPCClient) CreateRoomPin(ctx context.Context, req CreateRoomPinRequest) (RoomPin, error) {
	resp, err := c.client.AdminCreateRoomPin(ctx, &roomv1.AdminCreateRoomPinRequest{
		Meta:         requestMeta(ctx, strings.TrimSpace(req.RoomID), int64(req.AdminID), "admin-create-room-pin"),
		RoomId:       strings.TrimSpace(req.RoomID),
		PinType:      strings.TrimSpace(req.PinType),
		Weight:       req.Weight,
		DurationDays: req.DurationDays,
		PinnedAtMs:   req.PinnedAtMS,
		ExpiresAtMs:  req.ExpiresAtMS,
		AdminId:      req.AdminID,
	})
	if err != nil {
		return RoomPin{}, err
	}
	return roomPinFromProto(resp.GetPin()), nil
}

func (c *GRPCClient) CancelRoomPin(ctx context.Context, req CancelRoomPinRequest) (RoomPin, error) {
	resp, err := c.client.AdminCancelRoomPin(ctx, &roomv1.AdminCancelRoomPinRequest{
		Meta:    requestMeta(ctx, "", int64(req.AdminID), "admin-cancel-room-pin"),
		PinId:   req.PinID,
		AdminId: req.AdminID,
	})
	if err != nil {
		return RoomPin{}, err
	}
	return roomPinFromProto(resp.GetPin()), nil
}

func (c *GRPCClient) CloseRoom(ctx context.Context, req CloseRoomRequest) (*CloseRoomResult, error) {
	return c.setRoomLifecycle(ctx, req.RequestID, req.RoomID, req.ActorUserID, firstNonEmpty(req.Reason, "admin_closed"))
}

func (c *GRPCClient) ReopenRoom(ctx context.Context, req ReopenRoomRequest) (*CloseRoomResult, error) {
	return c.setRoomLifecycle(ctx, req.RequestID, req.RoomID, req.ActorUserID, "admin_reopen")
}

func (c *GRPCClient) setRoomLifecycle(ctx context.Context, rawRequestID string, rawRoomID string, actorUserID int64, rawReason string) (*CloseRoomResult, error) {
	if c == nil || c.client == nil {
		return nil, fmt.Errorf("room service client is not configured")
	}
	roomID := strings.TrimSpace(rawRoomID)
	reason := strings.TrimSpace(rawReason)
	if roomID == "" || actorUserID <= 0 || reason == "" {
		return nil, fmt.Errorf("room lifecycle request is incomplete")
	}
	requestID := strings.TrimSpace(rawRequestID)
	if requestID == "" {
		requestID = fmt.Sprintf("admin-room-lifecycle-%s-%d", roomID, time.Now().UnixMilli())
	}
	resp, err := c.client.CloseRoom(ctx, &roomv1.CloseRoomRequest{
		Meta:   requestMeta(ctx, roomID, actorUserID, requestID+":"+reason),
		Reason: reason,
	})
	if err != nil {
		return nil, err
	}
	return closeRoomResultFromCommand(resp.GetResult(), resp.GetRoom()), nil
}

func closeRoomResultFromCommand(result *roomv1.CommandResult, room *roomv1.RoomSnapshot) *CloseRoomResult {
	return &CloseRoomResult{
		Applied:       result.GetApplied(),
		RoomVersion:   result.GetRoomVersion(),
		ServerTimeMs:  result.GetServerTimeMs(),
		RoomStatus:    room.GetStatus(),
		OnlineUserNum: len(room.GetOnlineUsers()),
	}
}

func commandID(requestID string, reason string, roomID string) string {
	base := strings.TrimSpace(requestID)
	if base == "" {
		base = fmt.Sprintf("admin-room-lifecycle-%d", time.Now().UnixMilli())
	}
	value := base + ":" + strings.TrimSpace(reason) + ":" + strings.TrimSpace(roomID)
	if len(value) <= 128 {
		return value
	}
	return value[:128]
}

func requestMeta(ctx context.Context, roomID string, actorUserID int64, seed string) *roomv1.RequestMeta {
	requestID := strings.TrimSpace(seed)
	if requestID == "" {
		requestID = fmt.Sprintf("admin-room-%d", time.Now().UnixMilli())
	}
	return &roomv1.RequestMeta{
		RequestId:   requestID,
		CommandId:   commandID(requestID, "admin", roomID),
		ActorUserId: actorUserID,
		RoomId:      strings.TrimSpace(roomID),
		AppCode:     appctx.FromContext(ctx),
		SentAtMs:    time.Now().UnixMilli(),
	}
}

func roomFromProto(item *roomv1.AdminRoomListItem) Room {
	if item == nil {
		return Room{}
	}
	return Room{
		RoomID:            item.GetRoomId(),
		RoomShortID:       item.GetRoomShortId(),
		VisibleRegionID:   item.GetVisibleRegionId(),
		OwnerUserID:       item.GetOwnerUserId(),
		Title:             item.GetTitle(),
		CoverURL:          item.GetCoverUrl(),
		Mode:              item.GetMode(),
		Status:            item.GetStatus(),
		CloseReason:       item.GetCloseReason(),
		ClosedByAdminID:   item.GetClosedByAdminId(),
		ClosedByAdminName: item.GetClosedByAdminName(),
		ClosedAtMS:        item.GetClosedAtMs(),
		Heat:              item.GetHeat(),
		OnlineCount:       item.GetOnlineCount(),
		SeatCount:         item.GetSeatCount(),
		OccupiedSeatCount: item.GetOccupiedSeatCount(),
		SortScore:         item.GetSortScore(),
		CreatedAtMS:       item.GetCreatedAtMs(),
		UpdatedAtMS:       item.GetUpdatedAtMs(),
	}
}

func roomRocketConfigFromProto(input *roomv1.AdminRoomRocketConfig) RoomRocketConfig {
	if input == nil {
		return RoomRocketConfig{}
	}
	return RoomRocketConfig{
		AppCode:           input.GetAppCode(),
		Enabled:           input.GetEnabled(),
		ConfigVersion:     input.GetConfigVersion(),
		FuelSource:        input.GetFuelSource(),
		LaunchDelayMS:     input.GetLaunchDelayMs(),
		BroadcastEnabled:  input.GetBroadcastEnabled(),
		BroadcastScope:    input.GetBroadcastScope(),
		BroadcastDelayMS:  input.GetBroadcastDelayMs(),
		RewardStackPolicy: input.GetRewardStackPolicy(),
		Levels:            roomRocketLevelsFromProto(input.GetLevels()),
		GiftFuelRules:     roomRocketFuelRulesFromProto(input.GetGiftFuelRules()),
		UpdatedByAdminID:  input.GetUpdatedByAdminId(),
		CreatedAtMS:       input.GetCreatedAtMs(),
		UpdatedAtMS:       input.GetUpdatedAtMs(),
	}
}

func roomSeatConfigFromProto(input *roomv1.AdminRoomSeatConfig) RoomSeatConfig {
	if input == nil {
		return RoomSeatConfig{}
	}
	return RoomSeatConfig{
		CandidateSeatCounts: input.GetCandidateSeatCounts(),
		AllowedSeatCounts:   input.GetAllowedSeatCounts(),
		DefaultSeatCount:    input.GetDefaultSeatCount(),
		UpdatedAtMS:         input.GetUpdatedAtMs(),
	}
}

func roomPinFromProto(input *roomv1.AdminRoomPin) RoomPin {
	if input == nil {
		return RoomPin{}
	}
	room := input.GetRoom()
	return RoomPin{
		ID:                 input.GetId(),
		VisibleRegionID:    input.GetVisibleRegionId(),
		PinType:            input.GetPinType(),
		RoomID:             input.GetRoomId(),
		Weight:             input.GetWeight(),
		Status:             input.GetStatus(),
		PinnedAtMS:         input.GetPinnedAtMs(),
		ExpiresAtMS:        input.GetExpiresAtMs(),
		CancelledAtMS:      input.GetCancelledAtMs(),
		CreatedByAdminID:   input.GetCreatedByAdminId(),
		CancelledByAdminID: input.GetCancelledByAdminId(),
		CreatedAtMS:        input.GetCreatedAtMs(),
		UpdatedAtMS:        input.GetUpdatedAtMs(),
		Room: RoomPinRoom{
			RoomID:          room.GetRoomId(),
			RoomShortID:     room.GetRoomShortId(),
			VisibleRegionID: room.GetVisibleRegionId(),
			OwnerUserID:     room.GetOwnerUserId(),
			Title:           room.GetTitle(),
			CoverURL:        room.GetCoverUrl(),
			Status:          room.GetStatus(),
		},
	}
}

func roomRocketConfigToProto(input RoomRocketConfig) *roomv1.AdminRoomRocketConfig {
	return &roomv1.AdminRoomRocketConfig{
		AppCode:           input.AppCode,
		Enabled:           input.Enabled,
		FuelSource:        input.FuelSource,
		LaunchDelayMs:     input.LaunchDelayMS,
		BroadcastEnabled:  input.BroadcastEnabled,
		BroadcastScope:    input.BroadcastScope,
		BroadcastDelayMs:  input.BroadcastDelayMS,
		RewardStackPolicy: input.RewardStackPolicy,
		Levels:            roomRocketLevelsToProto(input.Levels),
		GiftFuelRules:     roomRocketFuelRulesToProto(input.GiftFuelRules),
	}
}

func roomRocketLevelsFromProto(input []*roomv1.RoomRocketLevel) []RoomRocketLevelConfig {
	out := make([]RoomRocketLevelConfig, 0, len(input))
	for _, level := range input {
		if level == nil {
			continue
		}
		out = append(out, RoomRocketLevelConfig{
			Level:              level.GetLevel(),
			FuelThreshold:      level.GetFuelThreshold(),
			CoverURL:           level.GetCoverUrl(),
			AnimationURL:       level.GetAnimationUrl(),
			LaunchAnimationURL: level.GetLaunchAnimationUrl(),
			LaunchedImageURL:   level.GetLaunchedImageUrl(),
			InRoomRewards:      roomRocketRewardsFromProto(level.GetInRoomRewards()),
			Top1Rewards:        roomRocketRewardsFromProto(level.GetTop1Rewards()),
			IgniterRewards:     roomRocketRewardsFromProto(level.GetIgniterRewards()),
		})
	}
	return out
}

func roomRocketLevelsToProto(input []RoomRocketLevelConfig) []*roomv1.RoomRocketLevel {
	out := make([]*roomv1.RoomRocketLevel, 0, len(input))
	for _, level := range input {
		out = append(out, &roomv1.RoomRocketLevel{
			Level:              level.Level,
			FuelThreshold:      level.FuelThreshold,
			CoverUrl:           level.CoverURL,
			AnimationUrl:       level.AnimationURL,
			LaunchAnimationUrl: level.LaunchAnimationURL,
			LaunchedImageUrl:   level.LaunchedImageURL,
			InRoomRewards:      roomRocketRewardsToProto(level.InRoomRewards),
			Top1Rewards:        roomRocketRewardsToProto(level.Top1Rewards),
			IgniterRewards:     roomRocketRewardsToProto(level.IgniterRewards),
		})
	}
	return out
}

func roomRocketRewardsFromProto(input []*roomv1.RoomRocketRewardItem) []RoomRocketRewardItem {
	out := make([]RoomRocketRewardItem, 0, len(input))
	for _, reward := range input {
		if reward == nil {
			continue
		}
		out = append(out, RoomRocketRewardItem{
			RewardItemID:    reward.GetRewardItemId(),
			ResourceGroupID: reward.GetResourceGroupId(),
			Weight:          reward.GetWeight(),
			Quantity:        reward.GetQuantity(),
			DisplayName:     reward.GetDisplayName(),
			IconURL:         reward.GetIconUrl(),
		})
	}
	return out
}

func roomRocketRewardsToProto(input []RoomRocketRewardItem) []*roomv1.RoomRocketRewardItem {
	out := make([]*roomv1.RoomRocketRewardItem, 0, len(input))
	for _, reward := range input {
		out = append(out, &roomv1.RoomRocketRewardItem{
			RewardItemId:    reward.RewardItemID,
			ResourceGroupId: reward.ResourceGroupID,
			Weight:          reward.Weight,
			Quantity:        reward.Quantity,
			DisplayName:     reward.DisplayName,
			IconUrl:         reward.IconURL,
		})
	}
	return out
}

func roomRocketFuelRulesFromProto(input []*roomv1.RoomRocketGiftFuelRule) []GiftFuelRuleConfig {
	out := make([]GiftFuelRuleConfig, 0, len(input))
	for _, rule := range input {
		if rule == nil {
			continue
		}
		out = append(out, GiftFuelRuleConfig{
			RuleID:        rule.GetRuleId(),
			GiftID:        rule.GetGiftId(),
			GiftTypeCode:  rule.GetGiftTypeCode(),
			MultiplierPPM: rule.GetMultiplierPpm(),
			FixedFuel:     rule.GetFixedFuel(),
			Excluded:      rule.GetExcluded(),
		})
	}
	return out
}

func roomRocketFuelRulesToProto(input []GiftFuelRuleConfig) []*roomv1.RoomRocketGiftFuelRule {
	out := make([]*roomv1.RoomRocketGiftFuelRule, 0, len(input))
	for _, rule := range input {
		out = append(out, &roomv1.RoomRocketGiftFuelRule{
			RuleId:        rule.RuleID,
			GiftId:        rule.GiftID,
			GiftTypeCode:  rule.GiftTypeCode,
			MultiplierPpm: rule.MultiplierPPM,
			FixedFuel:     rule.FixedFuel,
			Excluded:      rule.Excluded,
		})
	}
	return out
}

func firstNonEmpty(values ...string) string {
	for _, value := range values {
		if strings.TrimSpace(value) != "" {
			return value
		}
	}
	return ""
}
