package service_test

import (
	"context"
	"testing"
	"time"

	roomv1 "hyapp.local/api/proto/room/v1"
	"hyapp/pkg/appcode"
	"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"
)

func TestRobotRoomRuntimeStartRestoresLeftRobots(t *testing.T) {
	ctx := appcode.WithContext(context.Background(), appcode.Default)
	repository := mysqltest.NewRepository(t)
	svc := roomservice.New(roomservice.Config{
		NodeID:         "node-robot-runtime-restore-test",
		LeaseTTL:       10 * time.Second,
		RankLimit:      20,
		SnapshotEveryN: 1,
	}, router.NewMemoryDirectory(), repository, followTestWallet{}, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher())

	created, err := svc.AdminCreateRobotRoom(ctx, &roomv1.AdminCreateRobotRoomRequest{
		Meta:                  roomservice.NewRequestMeta("", 9001),
		OwnerRobotUserId:      1001,
		CandidateRobotUserIds: []int64{1001, 1002},
		MinRobotCount:         2,
		MaxRobotCount:         2,
		RoomName:              "Robot Runtime Restore",
		RoomAvatar:            testRoomCoverURL,
		VisibleRegionId:       686,
		GiftRule: &roomv1.AdminRobotRoomGiftRule{
			GiftIds:              []string{"normal_gift"},
			LuckyGiftIds:         []string{"lucky_gift"},
			NormalGiftIntervalMs: 10000,
			LuckyComboMin:        1,
			LuckyComboMax:        1,
			LuckyPauseMinMs:      5000,
			LuckyPauseMaxMs:      5000,
		},
		AdminId: 9001,
	})
	if err != nil {
		t.Fatalf("create robot room failed: %v", err)
	}
	roomID := created.GetRoom().GetRoomId()
	robotUserIDs := created.GetRoom().GetRobotUserIds()
	for _, userID := range robotUserIDs {
		if _, err := svc.LeaveRoom(ctx, &roomv1.LeaveRoomRequest{
			Meta: roomservice.NewRequestMeta(roomID, userID),
		}); err != nil {
			t.Fatalf("leave robot %d failed: %v", userID, err)
		}
	}

	if _, err := svc.AdminSetRobotRoomStatus(ctx, &roomv1.AdminSetRobotRoomStatusRequest{
		Meta:    roomservice.NewRequestMeta(roomID, 9001),
		Status:  "stopped",
		AdminId: 9001,
	}); err != nil {
		t.Fatalf("stop robot room failed: %v", err)
	}
	if _, err := svc.AdminSetRobotRoomStatus(ctx, &roomv1.AdminSetRobotRoomStatusRequest{
		Meta:    roomservice.NewRequestMeta(roomID, 9001),
		Status:  "active",
		AdminId: 9001,
	}); err != nil {
		t.Fatalf("restart robot room failed: %v", err)
	}

	snapshot, err := svc.GetRoomSnapshot(ctx, &roomv1.GetRoomSnapshotRequest{
		Meta:         &roomv1.RequestMeta{AppCode: appcode.Default, RoomId: roomID},
		RoomId:       roomID,
		ViewerUserId: robotUserIDs[0],
	})
	if err != nil {
		t.Fatalf("snapshot after runtime restore failed: %v", err)
	}
	for _, userID := range robotUserIDs {
		if !snapshotContainsUser(snapshot.GetRoom(), userID) {
			t.Fatalf("robot %d should be online after runtime restart", userID)
		}
		if !snapshotContainsSeatUser(snapshot.GetRoom(), userID) {
			t.Fatalf("robot %d should be on virtual mic after runtime restart", userID)
		}
	}
}

func TestRobotRoomRuntimeStartRestoresHighIndexOwnerToRealSeat(t *testing.T) {
	ctx := appcode.WithContext(context.Background(), appcode.Default)
	repository := mysqltest.NewRepository(t)
	svc := roomservice.New(roomservice.Config{
		NodeID:         "node-robot-runtime-high-index-restore-test",
		LeaseTTL:       10 * time.Second,
		RankLimit:      20,
		SnapshotEveryN: 1,
	}, router.NewMemoryDirectory(), repository, followTestWallet{}, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher())

	// 候选池有 12 个机器人而房间只有 10 个麦位；owner 排序后位于第 12 位。
	// bootstrap 会把 active owner 放到真实 seat 1，运行时恢复不能再按候选池下标猜 seat 12。
	candidates := make([]int64, 12)
	for index := range candidates {
		candidates[index] = int64(2001 + index)
	}
	ownerID := candidates[len(candidates)-1]
	created, err := svc.AdminCreateRobotRoom(ctx, &roomv1.AdminCreateRobotRoomRequest{
		Meta:                  roomservice.NewRequestMeta("", 9001),
		OwnerRobotUserId:      ownerID,
		CandidateRobotUserIds: candidates,
		MinRobotCount:         2,
		MaxRobotCount:         2,
		SeatCount:             10,
		RoomName:              "Robot Runtime High Index Restore",
		RoomAvatar:            testRoomCoverURL,
		VisibleRegionId:       686,
		GiftRule: &roomv1.AdminRobotRoomGiftRule{
			GiftIds:              []string{"normal_gift"},
			LuckyGiftIds:         []string{"lucky_gift"},
			NormalGiftIntervalMs: 10000,
			LuckyComboMin:        1,
			LuckyComboMax:        1,
			LuckyPauseMinMs:      5000,
			LuckyPauseMaxMs:      5000,
		},
		AdminId: 9001,
	})
	if err != nil {
		t.Fatalf("create high-index-owner robot room failed: %v", err)
	}
	roomID := created.GetRoom().GetRoomId()
	robotIDs := created.GetRoom().GetRobotUserIds()
	if len(robotIDs) != len(candidates) || robotIDs[len(robotIDs)-1] != ownerID {
		t.Fatalf("test requires owner at preferred seat 12, got owner=%d robots=%v", ownerID, robotIDs)
	}

	initial, err := svc.GetRoomSnapshot(ctx, &roomv1.GetRoomSnapshotRequest{
		Meta:         &roomv1.RequestMeta{AppCode: appcode.Default, RoomId: roomID},
		RoomId:       roomID,
		ViewerUserId: ownerID,
	})
	if err != nil {
		t.Fatalf("initial high-index-owner snapshot failed: %v", err)
	}
	if _, err := svc.AdminSetRobotRoomStatus(ctx, &roomv1.AdminSetRobotRoomStatusRequest{
		Meta:    roomservice.NewRequestMeta(roomID, 9001),
		Status:  "stopped",
		AdminId: 9001,
	}); err != nil {
		t.Fatalf("stop high-index-owner robot room failed: %v", err)
	}
	for _, user := range initial.GetRoom().GetOnlineUsers() {
		if _, err := svc.LeaveRoom(ctx, &roomv1.LeaveRoomRequest{Meta: roomservice.NewRequestMeta(roomID, user.GetUserId())}); err != nil {
			t.Fatalf("leave initial robot %d failed: %v", user.GetUserId(), err)
		}
	}

	if _, err := svc.AdminSetRobotRoomStatus(ctx, &roomv1.AdminSetRobotRoomStatusRequest{
		Meta:    roomservice.NewRequestMeta(roomID, 9001),
		Status:  "active",
		AdminId: 9001,
	}); err != nil {
		t.Fatalf("restart high-index-owner robot room failed: %v", err)
	}
	restored, err := svc.GetRoomSnapshot(ctx, &roomv1.GetRoomSnapshotRequest{
		Meta:         &roomv1.RequestMeta{AppCode: appcode.Default, RoomId: roomID},
		RoomId:       roomID,
		ViewerUserId: ownerID,
	})
	if err != nil {
		t.Fatalf("snapshot after high-index-owner restore failed: %v", err)
	}
	ownerSeat := int32(0)
	for _, seat := range restored.GetRoom().GetMicSeats() {
		if seat.GetUserId() == ownerID {
			ownerSeat = seat.GetSeatNo()
			break
		}
	}
	if ownerSeat <= 0 || ownerSeat > int32(len(restored.GetRoom().GetMicSeats())) {
		t.Fatalf("high-index owner must be restored to a real snapshot seat, got seat=%d", ownerSeat)
	}
}

func snapshotContainsUser(snapshot *roomv1.RoomSnapshot, userID int64) bool {
	for _, user := range snapshot.GetOnlineUsers() {
		if user.GetUserId() == userID {
			return true
		}
	}
	return false
}

func snapshotContainsSeatUser(snapshot *roomv1.RoomSnapshot, userID int64) bool {
	for _, seat := range snapshot.GetMicSeats() {
		if seat.GetUserId() == userID {
			return true
		}
	}
	return false
}
