package integration

import (
	"context"
	"hash/fnv"
	"sync"

	roomeventsv1 "hyapp.local/api/proto/events/room/v1"
)

// ShardedOutboxPublisher 只在 IM bridge 侧使用：不同 room_id 可以并发，同一个房间仍串行调用下游发布器。
type ShardedOutboxPublisher struct {
	inner OutboxPublisher
	locks []sync.Mutex
}

// NewShardedOutboxPublisher 用 room_id 分片保护下游发布器的房间内顺序；concurrency<=1 时保持历史串行实现。
func NewShardedOutboxPublisher(inner OutboxPublisher, concurrency int) OutboxPublisher {
	if inner == nil {
		return NewNoopOutboxPublisher()
	}
	if concurrency <= 1 {
		return inner
	}
	return &ShardedOutboxPublisher{
		inner: inner,
		locks: make([]sync.Mutex, concurrency),
	}
}

// PublishOutboxEvent 对同一 room_id 使用同一把锁，避免 RocketMQ consumer 并发后把礼物和麦位展示乱序发给腾讯 IM。
func (p *ShardedOutboxPublisher) PublishOutboxEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) error {
	if p == nil || p.inner == nil || envelope == nil {
		return nil
	}
	if len(p.locks) == 0 {
		return p.inner.PublishOutboxEvent(ctx, envelope)
	}
	lock := &p.locks[p.shardIndex(envelope.GetRoomId())]
	lock.Lock()
	defer lock.Unlock()

	return p.inner.PublishOutboxEvent(ctx, envelope)
}

func (p *ShardedOutboxPublisher) shardIndex(roomID string) uint32 {
	if len(p.locks) == 1 {
		return 0
	}
	hash := fnv.New32a()
	_, _ = hash.Write([]byte(roomID))
	return hash.Sum32() % uint32(len(p.locks))
}
