package service_test

import (
	"context"
	"testing"
	"time"

	"google.golang.org/protobuf/proto"
	roomeventsv1 "hyapp.local/api/proto/events/room/v1"
	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 TestAdminCreateRobotRoomProjectsOwnerRegionAndCountry(t *testing.T) {
	ctx := appcode.WithContext(context.Background(), appcode.Default)
	repository := mysqltest.NewRepository(t)
	svc := roomservice.New(roomservice.Config{
		NodeID:         "node-admin-robot-room-owner-region-country-test",
		LeaseTTL:       10 * time.Second,
		RankLimit:      20,
		SnapshotEveryN: 1,
	}, router.NewMemoryDirectory(), repository, followTestWallet{}, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher())

	resp, err := svc.AdminCreateRobotRoom(ctx, &roomv1.AdminCreateRobotRoomRequest{
		Meta:                  roomservice.NewRequestMeta("", 9001),
		OwnerRobotUserId:      325379237278126080,
		CandidateRobotUserIds: []int64{325379237278126080},
		MinRobotCount:         1,
		MaxRobotCount:         1,
		RoomName:              "Robot Owner",
		RoomAvatar:            testRoomCoverURL,
		VisibleRegionId:       686,
		OwnerCountryCode:      "ae",
		GiftRule: &roomv1.AdminRobotRoomGiftRule{
			GiftIds:              []string{"84"},
			LuckyGiftIds:         []string{"28"},
			NormalGiftIntervalMs: 10000,
			LuckyComboMin:        100,
			LuckyComboMax:        10000,
			LuckyPauseMinMs:      5000,
			LuckyPauseMaxMs:      20000,
		},
		AdminId: 9001,
	})
	if err != nil {
		t.Fatalf("AdminCreateRobotRoom failed: %v", err)
	}
	roomID := resp.GetRoom().GetRoomId()
	if roomID == "" {
		t.Fatalf("created robot room id is empty")
	}

	entries, err := repository.ListRoomListEntries(ctx, roomservice.RoomListQuery{VisibleRegionID: 686, CountryCode: "AE", Tab: "new", Limit: 10})
	if err != nil {
		t.Fatalf("list robot room entries failed: %v", err)
	}
	if len(entries) != 1 || entries[0].RoomID != roomID || entries[0].VisibleRegionID != 686 || entries[0].OwnerCountryCode != "AE" {
		t.Fatalf("robot room owner region/country projection mismatch: %+v", entries)
	}
}

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

	resp, err := svc.AdminCreateRobotRoom(ctx, &roomv1.AdminCreateRobotRoomRequest{
		Meta:                  roomservice.NewRequestMeta("", 9001),
		OwnerRobotUserId:      1001,
		CandidateRobotUserIds: []int64{1001, 1002, 1003, 1004},
		MinRobotCount:         2,
		MaxRobotCount:         2,
		RoomName:              "Robot Pool",
		RoomAvatar:            testRoomCoverURL,
		VisibleRegionId:       686,
		OwnerCountryCode:      "ae",
		GiftRule: &roomv1.AdminRobotRoomGiftRule{
			GiftIds:              []string{"84"},
			LuckyGiftIds:         []string{"28"},
			NormalGiftIntervalMs: 10000,
			LuckyComboMin:        1,
			LuckyComboMax:        1,
			LuckyPauseMinMs:      5000,
			LuckyPauseMaxMs:      20000,
			RobotStayMinMs:       180000,
			RobotStayMaxMs:       600000,
			RobotReplaceMinMs:    0,
			RobotReplaceMaxMs:    60000,
			MaxGiftSenders:       2,
		},
		AdminId: 9001,
	})
	if err != nil {
		t.Fatalf("AdminCreateRobotRoom failed: %v", err)
	}
	roomID := resp.GetRoom().GetRoomId()
	if got := len(resp.GetRoom().GetRobotUserIds()); got != 4 {
		t.Fatalf("robot room should persist full candidate pool for replacement, got %d", got)
	}
	if resp.GetRoom().GetGiftRule().GetMaxGiftSenders() != 2 {
		t.Fatalf("max gift senders should round-trip through room config")
	}

	snapshot, err := svc.GetRoomSnapshot(ctx, &roomv1.GetRoomSnapshotRequest{
		Meta:         &roomv1.RequestMeta{AppCode: appcode.Default, RoomId: roomID},
		RoomId:       roomID,
		ViewerUserId: 1001,
	})
	if err != nil {
		t.Fatalf("snapshot failed: %v", err)
	}
	if got := len(snapshot.GetRoom().GetOnlineUsers()); got != 2 {
		t.Fatalf("robot room should only bootstrap active robot count online, got %d", got)
	}
	if got := occupiedSeatUserCount(snapshot.GetRoom()); got != 2 {
		t.Fatalf("robot room should only put active robots on virtual mic, got %d", got)
	}
}

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

	_, err := svc.AdminCreateRobotRoom(ctx, &roomv1.AdminCreateRobotRoomRequest{
		Meta:                  roomservice.NewRequestMeta("", 9001),
		OwnerRobotUserId:      2001,
		CandidateRobotUserIds: []int64{2001, 2002},
		MinRobotCount:         2,
		MaxRobotCount:         2,
		RoomName:              "Robot Join Outbox",
		RoomAvatar:            testRoomCoverURL,
		VisibleRegionId:       686,
		OwnerCountryCode:      "ae",
		GiftRule: &roomv1.AdminRobotRoomGiftRule{
			GiftIds:              []string{"84"},
			LuckyGiftIds:         []string{"28"},
			NormalGiftIntervalMs: 10000,
			LuckyComboMin:        100,
			LuckyComboMax:        10000,
			LuckyPauseMinMs:      5000,
			LuckyPauseMaxMs:      20000,
		},
		AdminId: 9001,
	})
	if err != nil {
		t.Fatalf("AdminCreateRobotRoom failed: %v", err)
	}

	records, err := repository.ListPendingOutbox(ctx, 20)
	if err != nil {
		t.Fatalf("list pending outbox failed: %v", err)
	}
	joinEvents := 0
	bootstrapRobotJoined := false
	for _, record := range records {
		if record.EventType != "RoomUserJoined" {
			continue
		}
		joinEvents++
		var joined roomeventsv1.RoomUserJoined
		if err := proto.Unmarshal(record.Envelope.GetBody(), &joined); err != nil {
			t.Fatalf("decode room join event failed: %v", err)
		}
		if !joined.GetIsRobot() {
			t.Fatalf("robot join outbox must mark is_robot=true: %+v", &joined)
		}
		bootstrapRobotJoined = bootstrapRobotJoined || joined.GetUserId() == 2002
	}
	if joinEvents == 0 {
		t.Fatalf("robot room creation should write RoomUserJoined outbox")
	}
	if !bootstrapRobotJoined {
		t.Fatalf("bootstrap robot should write RoomUserJoined outbox")
	}
}

func occupiedSeatUserCount(snapshot *roomv1.RoomSnapshot) int {
	count := 0
	for _, seat := range snapshot.GetMicSeats() {
		if seat.GetUserId() > 0 {
			count++
		}
	}
	return count
}
