// Package outbox 定义 room-service 房间外事件的统一封装和状态模型。
package outbox

import (
	"time"

	"google.golang.org/protobuf/proto"
	roomeventsv1 "hyapp.local/api/proto/events/room/v1"
	"hyapp/pkg/idgen"
)

const (
	// StatusPending 表示事件已经入库，但尚未投递完成。
	StatusPending = "pending"
	// StatusDelivering 表示事件已被某个 worker 抢占，正在投递中。
	StatusDelivering = "delivering"
	// StatusRetryable 表示上次投递失败，后续 worker 可继续重试。
	StatusRetryable = "retryable"
	// StatusFailed 表示事件不可重试，需要人工或专项补偿处理。
	StatusFailed = "failed"
	// StatusDelivered 表示事件已成功投递到外部消费者。
	StatusDelivered = "delivered"
)

// Record 表达 room_outbox 表的一条逻辑记录。
type Record struct {
	// AppCode 是事件所属 App，补偿投递和消费者都不能跨 App 合并房间事件。
	AppCode string
	// EventID 是 outbox 事件幂等键，同时会传给腾讯云 IM 作为房间系统事件 event_id。
	EventID string
	// EventType 是事件类型字符串，例如 RoomGiftSent、RoomMicChanged。
	EventType string
	// RoomID 是事件所属房间，外部消费者可按房间分区处理。
	RoomID string
	// Status 表示投递状态，只在 repository/worker 侧变更。
	Status string
	// Envelope 是 protobuf 统一信封，body 内是具体事件消息的序列化结果。
	Envelope *roomeventsv1.EventEnvelope
	// CreatedAtMS 是事件入库的 UTC 毫秒时间戳，也是待投递扫描的排序依据。
	CreatedAtMS int64
	// RetryCount 记录补偿投递失败次数，便于后续告警和退避策略。
	RetryCount int
	// LastError 保存最近一次投递失败原因。
	LastError string
	// WorkerID 记录当前抢占该事件的 worker。
	WorkerID string
	// LockUntilMS 是 delivering 锁的截止时间。
	LockUntilMS int64
	// NextRetryAtMS 是 retryable 事件下一次允许抢占的 UTC 毫秒时间。
	NextRetryAtMS int64
}

// Build 把具体事件体封装成统一 outbox 记录。
func Build(roomID string, eventType string, roomVersion int64, occurredAt time.Time, body proto.Message) (Record, error) {
	// 事件体先序列化到信封 body，避免 outbox 表按事件类型拆列。
	bodyBytes, err := proto.Marshal(body)
	if err != nil {
		return Record{}, err
	}

	// EventID 在 room-service 生成，后续同步广播和异步补偿都用同一个 ID 去重。
	eventID := idgen.New("evt")

	// Build 没有请求上下文，不能在多 App 环境猜租户。SaveMutation/SaveOutbox 会在持久化前用
	// 当前请求的 app_code 同时补齐 Record 和 Envelope；直发 IM/机器人展示也在发布边界做同样处理。
	return Record{
		EventID:   eventID,
		EventType: eventType,
		RoomID:    roomID,
		Status:    StatusPending,
		Envelope: &roomeventsv1.EventEnvelope{
			EventId:      eventID,
			RoomId:       roomID,
			EventType:    eventType,
			RoomVersion:  roomVersion,
			OccurredAtMs: occurredAt.UnixMilli(),
			Body:         bodyBytes,
		},
		CreatedAtMS: occurredAt.UnixMilli(),
	}, nil
}
