package service_test

import (
	"context"
	"fmt"
	"testing"
	"time"

	roomeventsv1 "hyapp.local/api/proto/events/room/v1"
	"hyapp/pkg/appcode"
	"hyapp/services/room-service/internal/integration"
	"hyapp/services/room-service/internal/room/outbox"
	roomservice "hyapp/services/room-service/internal/room/service"
	"hyapp/services/room-service/internal/router"
	"hyapp/services/room-service/internal/testutil/mysqltest"
)

// 一轮清理必须按批删空所有过期 delivered 事件，同时保留窗口内 delivered 和未完成投递的事件。
func TestProcessOutboxRetentionDrainsExpiredDelivered(t *testing.T) {
	ctx := context.Background()
	repository := mysqltest.NewRepository(t)
	svc := roomservice.New(roomservice.Config{
		NodeID: "node-outbox-retention-test",
	}, router.NewMemoryDirectory(), repository, followTestWallet{}, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher())

	roomID := "room-outbox-retention"
	newRecord := func(eventID string) outbox.Record {
		return outbox.Record{
			AppCode:   appcode.Default,
			EventID:   eventID,
			EventType: "RoomGiftSent",
			RoomID:    roomID,
			Status:    outbox.StatusPending,
			Envelope: &roomeventsv1.EventEnvelope{
				AppCode:   appcode.Default,
				EventId:   eventID,
				RoomId:    roomID,
				EventType: "RoomGiftSent",
			},
			CreatedAtMS: 1000,
		}
	}

	expiredMS := time.Now().UTC().Add(-48 * time.Hour).UnixMilli()
	records := make([]outbox.Record, 0, 7)
	expired := make([]string, 0, 5)
	for index := 0; index < 5; index++ {
		eventID := fmt.Sprintf("evt-retention-expired-%d", index)
		expired = append(expired, eventID)
		records = append(records, newRecord(eventID))
	}
	records = append(records, newRecord("evt-retention-fresh"), newRecord("evt-retention-pending"))
	if err := repository.SaveOutbox(ctx, records); err != nil {
		t.Fatalf("save outbox failed: %v", err)
	}
	for _, eventID := range expired {
		repository.SetOutboxDeliveredAt(eventID, expiredMS)
	}
	if err := repository.MarkOutboxDelivered(ctx, "evt-retention-fresh"); err != nil {
		t.Fatalf("mark fresh delivered failed: %v", err)
	}

	// BatchSize=2 强制一轮内分 3 批删完 5 条过期事件，覆盖“整批删满继续、删空退出”的循环边界。
	total, err := svc.ProcessOutboxRetention(ctx, roomservice.OutboxRetentionOptions{
		MaxAge:    24 * time.Hour,
		BatchSize: 2,
	})
	if err != nil {
		t.Fatalf("process outbox retention failed: %v", err)
	}
	if total != int64(len(expired)) {
		t.Fatalf("retention deleted %d rows, want %d", total, len(expired))
	}

	for _, eventID := range expired {
		if _, ok := repository.OutboxRecord(eventID); ok {
			t.Fatalf("expired delivered event %s must be purged", eventID)
		}
	}
	if record, ok := repository.OutboxRecord("evt-retention-fresh"); !ok || record.Status != outbox.StatusDelivered {
		t.Fatalf("fresh delivered event must survive: ok=%v record=%+v", ok, record)
	}
	if record, ok := repository.OutboxRecord("evt-retention-pending"); !ok || record.Status != outbox.StatusPending {
		t.Fatalf("pending event must survive: ok=%v record=%+v", ok, record)
	}
}
