package service_test

import (
	"context"
	"errors"
	"testing"
	"time"

	roomv1 "hyapp.local/api/proto/room/v1"
	"hyapp/pkg/tencentim"
	"hyapp/pkg/xerr"
	"hyapp/services/room-service/internal/integration"
	roomservice "hyapp/services/room-service/internal/room/service"
	"hyapp/services/room-service/internal/router"
	"hyapp/services/room-service/internal/testutil/mysqltest"
)

const testRoomCoverURL = "https://cdn.example.com/room-cover.png"

type createRoomIMPublisher struct {
	err         error
	roomID      string
	ownerUserID int64
	callCount   int
}

func (p *createRoomIMPublisher) EnsureRoomGroup(_ context.Context, roomID string, ownerUserID int64) error {
	p.callCount++
	p.roomID = roomID
	p.ownerUserID = ownerUserID
	return p.err
}

func (p *createRoomIMPublisher) PublishRoomEvent(context.Context, tencentim.RoomEvent) error {
	return nil
}

func TestCreateRoomRequiresRoomAvatar(t *testing.T) {
	ctx := context.Background()
	repository := mysqltest.NewRepository(t)
	svc := roomservice.New(roomservice.Config{
		NodeID:         "node-create-room-avatar-test",
		LeaseTTL:       10 * time.Second,
		RankLimit:      20,
		SnapshotEveryN: 1,
	}, router.NewMemoryDirectory(), repository, followTestWallet{}, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher())

	_, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{
		Meta:        roomservice.NewRequestMeta("room-avatar-required", 7000),
		SeatCount:   10,
		Mode:        "voice",
		RoomName:    "Avatar Required",
		RoomShortId: "avatar-required",
	})
	if !xerr.IsCode(err, xerr.InvalidArgument) {
		// 房间封面为空必须在 Room owner service 拒绝，避免 gateway 以外的内部调用创建空 cover_url 房间。
		t.Fatalf("expected INVALID_ARGUMENT without room avatar, got %v", err)
	}
	if _, exists, err := repository.GetRoomMeta(ctx, "room-avatar-required"); err != nil || exists {
		t.Fatalf("room meta must not persist without avatar: exists=%t err=%v", exists, err)
	}
}

func TestCreateRoomSynchronouslyEnsuresIMGroup(t *testing.T) {
	ctx := context.Background()
	repository := mysqltest.NewRepository(t)
	publisher := &createRoomIMPublisher{}
	svc := roomservice.New(roomservice.Config{
		NodeID:         "node-create-room-im-test",
		LeaseTTL:       10 * time.Second,
		RankLimit:      20,
		SnapshotEveryN: 1,
	}, router.NewMemoryDirectory(), repository, followTestWallet{}, publisher, integration.NewNoopOutboxPublisher())

	roomID := "room-sync-im-ok"
	ownerID := int64(7001)
	resp, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{
		Meta:            roomservice.NewRequestMeta(roomID, ownerID),
		SeatCount:       10,
		Mode:            "voice",
		VisibleRegionId: 1001,
		RoomName:        "Sync IM Room",
		RoomAvatar:      testRoomCoverURL,
		RoomShortId:     "sync-im-ok",
	})
	if err != nil {
		t.Fatalf("create room failed: %v", err)
	}
	if publisher.callCount != 1 || publisher.roomID != roomID || publisher.ownerUserID != ownerID {
		t.Fatalf("IM group must be ensured once before create returns: %+v", publisher)
	}
	if resp.GetRoom().GetRoomId() != roomID || !resp.GetResult().GetApplied() {
		t.Fatalf("create response mismatch: %+v", resp)
	}
}

func TestCreateRoomDoesNotPersistWhenIMGroupEnsureFails(t *testing.T) {
	ctx := context.Background()
	repository := mysqltest.NewRepository(t)
	publisher := &createRoomIMPublisher{err: errors.New("tencent im unavailable")}
	svc := roomservice.New(roomservice.Config{
		NodeID:         "node-create-room-im-fail-test",
		LeaseTTL:       10 * time.Second,
		RankLimit:      20,
		SnapshotEveryN: 1,
	}, router.NewMemoryDirectory(), repository, followTestWallet{}, publisher, integration.NewNoopOutboxPublisher())

	roomID := "room-sync-im-fail"
	ownerID := int64(7002)
	if _, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{
		Meta:            roomservice.NewRequestMeta(roomID, ownerID),
		SeatCount:       10,
		Mode:            "voice",
		VisibleRegionId: 1001,
		RoomName:        "Sync IM Failure",
		RoomAvatar:      testRoomCoverURL,
		RoomShortId:     "sync-im-fail",
	}); err == nil {
		t.Fatalf("create room must fail when IM group cannot be created")
	}
	if publisher.callCount != 1 || publisher.roomID != roomID || publisher.ownerUserID != ownerID {
		t.Fatalf("IM group ensure call mismatch: %+v", publisher)
	}
	if meta, exists, err := repository.GetRoomMeta(ctx, roomID); err != nil || exists {
		t.Fatalf("room meta must not persist after IM failure: meta=%+v exists=%t err=%v", meta, exists, err)
	}
}
