// Package rank 维护 Room Cell 内部的房间本地礼物榜。
package rank

import (
	"sort"
	"time"

	"hyapp/services/room-service/internal/room/state"
)

// LocalRank 在 Room Cell 内同步维护房间礼物榜。
type LocalRank struct {
	// items 按 user_id 聚合累计分数，只有 Room Cell 串行任务会修改它。
	items map[int64]*state.RankItem
	// limit 控制 Top 返回的榜单长度，0 或负数表示不裁剪。
	limit int
}

// New 初始化一个空的本地榜单。
func New(limit int) *LocalRank {
	// LocalRank 不自己加锁，因为它只在 Room Cell mailbox 内被串行访问。
	return &LocalRank{
		items: make(map[int64]*state.RankItem),
		limit: limit,
	}
}

// FromState 用快照中的榜单结果恢复本地榜索引。
func FromState(items []state.RankItem, limit int) *LocalRank {
	rank := New(limit)

	for _, item := range items {
		// 快照切片转 map 索引，后续送礼更新可以 O(1) 找到用户榜单项。
		copied := item
		rank.items[item.UserID] = &copied
	}

	return rank
}

// Clone 返回当前榜单的独立副本。
func (r *LocalRank) Clone() *LocalRank {
	if r == nil {
		// nil 榜单通常只出现在防御性调用，保持 nil 语义。
		return nil
	}

	cloned := New(r.limit)

	for userID, item := range r.items {
		// 深拷贝 RankItem，避免调用方通过返回值修改 Room Cell 内部榜单。
		copied := *item
		cloned.items[userID] = &copied
	}

	return cloned
}

// ReplaceWith 用新榜单整体替换旧榜单。
func (r *LocalRank) ReplaceWith(next *LocalRank) {
	// 整体替换可以避免部分字段变更失败后留下旧引用。
	r.items = next.Clone().items
	r.limit = next.limit
}

// ApplyGift 把礼物值累计到指定用户的榜单项上，并返回最新 top N。
func (r *LocalRank) ApplyGift(userID int64, giftValue int64, now time.Time) []state.RankItem {
	item, exists := r.items[userID]
	if !exists {
		// 首次送礼时创建榜单项，分数和礼物值都由本次礼物累计。
		item = &state.RankItem{
			UserID: userID,
		}

		r.items[userID] = item
	}

	// 当前版本 Score 与 GiftValue 同步增长，后续可以把 Score 替换成活动权重。
	item.Score += giftValue
	item.GiftValue += giftValue
	item.UpdatedAtMS = now.UnixMilli()

	return r.Top()
}

// Top 返回当前榜单按分数排序后的前 N 项。
func (r *LocalRank) Top() []state.RankItem {
	// 返回值是值拷贝切片，调用方不能反向修改 LocalRank 内部 map。
	items := make([]state.RankItem, 0, len(r.items))

	for _, item := range r.items {
		items = append(items, *item)
	}

	sort.Slice(items, func(i int, j int) bool {
		if items[i].Score == items[j].Score {
			// 分数相同时最近更新靠前，符合房间内即时展示预期。
			return items[i].UpdatedAtMS > items[j].UpdatedAtMS
		}

		return items[i].Score > items[j].Score
	})

	if r.limit > 0 && len(items) > r.limit {
		items = items[:r.limit]
	}

	return items
}
