package service

import (
	"context"
	"sync"
	"time"

	"hyapp/pkg/appcode"
	"hyapp/services/room-service/internal/room/outbox"
)

const (
	// robotDisplaySampleWindow 是 v1 展示降采样窗口：同房间同事件类型 2 秒内只直发一条机器人展示 IM。
	robotDisplaySampleWindow = 2 * time.Second
)

type robotDisplaySampleKey struct {
	appCode   string
	roomID    string
	eventType string
}

type robotDisplaySampler struct {
	mu         sync.Mutex
	lastWindow map[robotDisplaySampleKey]int64
}

func newRobotDisplaySampler() *robotDisplaySampler {
	return &robotDisplaySampler{lastWindow: make(map[robotDisplaySampleKey]int64)}
}

func (s *Service) sampleRobotDisplayRecords(ctx context.Context, records []outbox.Record) []outbox.Record {
	if len(records) == 0 || s == nil || s.robotDisplaySampler == nil {
		return records
	}
	return s.robotDisplaySampler.filter(records, robotDisplaySampleWindow, appcode.FromContext(ctx))
}

func (s *robotDisplaySampler) filter(records []outbox.Record, window time.Duration, scopedApp string) []outbox.Record {
	if s == nil || window <= 0 || len(records) == 0 {
		return records
	}

	windowMS := window.Milliseconds()
	out := make([]outbox.Record, 0, len(records))
	s.mu.Lock()
	defer s.mu.Unlock()

	var newestWindow int64
	for _, record := range records {
		key := robotDisplaySampleKey{
			// Build 阶段故意不猜 App；降采样发生在发布边界补值之前，因此必须使用当前命令上下文兜底，
			// 否则不同 App 的同房间标识会共用默认 Lalu 窗口并误丢展示事件。
			appCode:   appcode.Normalize(firstNonEmpty(record.AppCode, scopedApp)),
			roomID:    record.RoomID,
			eventType: record.EventType,
		}
		createdAtMS := record.CreatedAtMS
		if createdAtMS <= 0 && record.Envelope != nil {
			createdAtMS = record.Envelope.GetOccurredAtMs()
		}
		windowStartMS := createdAtMS / windowMS * windowMS
		if windowStartMS > newestWindow {
			newestWindow = windowStartMS
		}
		if last, exists := s.lastWindow[key]; exists && windowStartMS <= last {
			// 机器人送礼没有 Room Cell 持久状态；展示过密时直接丢当前窗口重复 IM，下一窗口仍可正常展示。
			continue
		}
		s.lastWindow[key] = windowStartMS
		out = append(out, record)
	}

	if newestWindow > 0 && len(s.lastWindow) > 1024 {
		// 机器人房数量和事件类型有限，超过阈值说明进程运行很久；保留最近几个窗口即可避免 map 常驻增长。
		s.pruneLocked(newestWindow - 4*windowMS)
	}
	return out
}

func (s *robotDisplaySampler) pruneLocked(beforeWindowMS int64) {
	for key, windowStartMS := range s.lastWindow {
		if windowStartMS < beforeWindowMS {
			delete(s.lastWindow, key)
		}
	}
}
