package mictime

import (
	"context"
	"database/sql"

	"google.golang.org/protobuf/proto"
	roomeventsv1 "hyapp.local/api/proto/events/room/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	mictimedomain "hyapp/services/user-service/internal/domain/mictime"
)

// RoomOutboxReader scans room-service outbox without mutating room-service delivery state.
type RoomOutboxReader struct {
	db *sql.DB
}

// NewRoomOutboxReader creates a read-only scanner for RoomMicChanged events.
func NewRoomOutboxReader(db *sql.DB) *RoomOutboxReader {
	return &RoomOutboxReader{db: db}
}

// ListRoomMicEventsAfter returns RoomMicChanged events after the caller's in-memory cursor.
func (r *RoomOutboxReader) ListRoomMicEventsAfter(ctx context.Context, cursor mictimedomain.RoomOutboxCursor, batchSize int) ([]mictimedomain.RoomMicEvent, error) {
	if r == nil || r.db == nil {
		return nil, xerr.New(xerr.Unavailable, "room outbox reader is not configured")
	}
	if batchSize <= 0 {
		batchSize = 100
	}
	rows, err := r.db.QueryContext(ctx, `
		SELECT event_id, envelope, created_at_ms
		FROM room_outbox
		WHERE event_type = ?
		  AND (created_at_ms > ? OR (created_at_ms = ? AND event_id > ?))
		ORDER BY created_at_ms ASC, event_id ASC
		LIMIT ?
	`, mictimedomain.RoomMicChangedEventType, cursor.CreatedAtMs, cursor.CreatedAtMs, cursor.EventID, batchSize)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	events := make([]mictimedomain.RoomMicEvent, 0, batchSize)
	for rows.Next() {
		var eventID string
		var envelopeBytes []byte
		var createdAtMS int64
		if err := rows.Scan(&eventID, &envelopeBytes, &createdAtMS); err != nil {
			return nil, err
		}
		var envelope roomeventsv1.EventEnvelope
		if err := proto.Unmarshal(envelopeBytes, &envelope); err != nil {
			return nil, err
		}
		var body roomeventsv1.RoomMicChanged
		if err := proto.Unmarshal(envelope.GetBody(), &body); err != nil {
			return nil, err
		}
		decodedEventID := envelope.GetEventId()
		if decodedEventID == "" {
			decodedEventID = eventID
		}
		events = append(events, mictimedomain.RoomMicEvent{
			AppCode:            appcode.Normalize(envelope.GetAppCode()),
			EventID:            decodedEventID,
			EventType:          envelope.GetEventType(),
			RoomID:             envelope.GetRoomId(),
			RoomVersion:        envelope.GetRoomVersion(),
			OutboxCreatedAtMs:  createdAtMS,
			OccurredAtMs:       envelope.GetOccurredAtMs(),
			ActorUserID:        body.GetActorUserId(),
			TargetUserID:       body.GetTargetUserId(),
			FromSeat:           body.GetFromSeat(),
			ToSeat:             body.GetToSeat(),
			Action:             body.GetAction(),
			MicSessionID:       body.GetMicSessionId(),
			PublishState:       body.GetPublishState(),
			Reason:             body.GetReason(),
			PublishDeadlineMs:  body.GetPublishDeadlineMs(),
			PublishEventTimeMs: body.GetPublishEventTimeMs(),
		})
	}
	return events, rows.Err()
}
