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 TestRoomPasswordControlsJoinAndListLockedFlag(t *testing.T) {
	ctx := context.Background()
	repository := mysqltest.NewRepository(t)
	svc := roomservice.New(roomservice.Config{
		NodeID:         "node-lock-test",
		LeaseTTL:       10 * time.Second,
		RankLimit:      20,
		SnapshotEveryN: 1,
	}, router.NewMemoryDirectory(), repository, followTestWallet{}, integration.NewNoopRoomEventPublisher(), integration.NewNoopOutboxPublisher())

	roomID := "room-lock-flow"
	ownerID := int64(5101)
	viewerID := int64(5201)
	if _, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{
		Meta:            roomservice.NewRequestMeta(roomID, ownerID),
		SeatCount:       10,
		Mode:            "voice",
		VisibleRegionId: 6101,
		RoomName:        "Locked Room",
		RoomAvatar:      testRoomCoverURL,
		RoomShortId:     "lock-flow",
	}); err != nil {
		t.Fatalf("create locked room fixture failed: %v", err)
	}

	lockMeta := roomservice.NewRequestMeta(roomID, ownerID)
	lockResp, err := svc.SetRoomPassword(ctx, &roomv1.SetRoomPasswordRequest{
		Meta:     lockMeta,
		Locked:   true,
		Password: "1234",
	})
	if err != nil {
		t.Fatalf("set room password failed: %v", err)
	}
	if _, err := svc.SetRoomPassword(ctx, &roomv1.SetRoomPasswordRequest{
		Meta:     lockMeta,
		Locked:   true,
		Password: "1234",
	}); err != nil {
		t.Fatalf("same command id and same password should be idempotent: %v", err)
	}
	if _, err := svc.SetRoomPassword(ctx, &roomv1.SetRoomPasswordRequest{
		Meta:     lockMeta,
		Locked:   true,
		Password: "9999",
	}); err == nil {
		t.Fatalf("same command id must reject a different room password")
	}
	if !lockResp.GetRoom().GetLocked() {
		t.Fatalf("password response must expose locked snapshot: %+v", lockResp.GetRoom())
	}
	meta, exists, err := repository.GetRoomMeta(ctx, roomID)
	if err != nil || !exists || meta.RoomPasswordHash == "" || meta.RoomPasswordHash == "1234" {
		t.Fatalf("room meta must persist only password hash: meta=%+v exists=%t err=%v", meta, exists, err)
	}

	listResp, err := svc.ListRooms(ctx, &roomv1.ListRoomsRequest{
		Meta:            &roomv1.RequestMeta{AppCode: appcode.Default},
		ViewerUserId:    viewerID,
		VisibleRegionId: 6101,
		Tab:             "hot",
		Limit:           20,
		Query:           "lock-flow",
	})
	if err != nil {
		t.Fatalf("list locked room failed: %v", err)
	}
	if len(listResp.GetRooms()) != 1 || !listResp.GetRooms()[0].GetLocked() {
		t.Fatalf("list/search must return locked flag: %+v", listResp.GetRooms())
	}
	adminRoom, err := svc.AdminGetRoom(ctx, &roomv1.AdminGetRoomRequest{
		Meta:   &roomv1.RequestMeta{AppCode: appcode.Default},
		RoomId: roomID,
	})
	if err != nil || !adminRoom.GetRoom().GetLocked() {
		t.Fatalf("owner admin room snapshot must expose locked flag: room=%+v err=%v", adminRoom.GetRoom(), err)
	}

	if _, err := svc.JoinRoom(ctx, &roomv1.JoinRoomRequest{
		Meta:     roomservice.NewRequestMeta(roomID, viewerID),
		Role:     "audience",
		Password: "wrong",
	}); err == nil {
		t.Fatalf("wrong password must not join locked room")
	}

	joinResp, err := svc.JoinRoom(ctx, &roomv1.JoinRoomRequest{
		Meta:     roomservice.NewRequestMeta(roomID, viewerID),
		Role:     "audience",
		Password: "1234",
	})
	if err != nil {
		t.Fatalf("correct password should join locked room: %v", err)
	}
	if joinResp.GetUser().GetUserId() != viewerID || !joinResp.GetRoom().GetLocked() {
		t.Fatalf("join response mismatch: user=%+v room=%+v", joinResp.GetUser(), joinResp.GetRoom())
	}

	if _, err := svc.SetRoomPassword(ctx, &roomv1.SetRoomPasswordRequest{
		Meta:     roomservice.NewRequestMeta(roomID, ownerID),
		Locked:   true,
		Password: "5678",
	}); err != nil {
		t.Fatalf("change room password after viewer joined failed: %v", err)
	}
	if _, err := svc.JoinRoom(ctx, &roomv1.JoinRoomRequest{
		Meta:     roomservice.NewRequestMeta(roomID, viewerID),
		Role:     "audience",
		Password: "1234",
	}); err == nil {
		t.Fatalf("existing online user must not bypass changed room password")
	}
	if _, err := svc.JoinRoom(ctx, &roomv1.JoinRoomRequest{
		Meta:     roomservice.NewRequestMeta(roomID, viewerID),
		Role:     "audience",
		Password: "5678",
	}); err != nil {
		t.Fatalf("existing online user should reconnect with latest room password: %v", err)
	}
}
