package main

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

type Statement struct {
	text   string
	author TestUserInfo
}

type RoomData struct {
	convo []Statement
}

type TestUserInfo struct {
	Name string
}

func TestRoomsZero(t *testing.T) {
	rooms := NewRooms[RoomData, TestUserInfo]()
	assert.NotNil(t, rooms)
}

func TestRoomsMany(t *testing.T) {
	names := []string{"a", "b"}
	rooms := NewRooms[RoomData, TestUserInfo](names...)
	assert.NotNil(t, rooms)
	assert.Equal(t, 2, len(rooms.names))

	// Visit
	seen := 0
	rooms.Visit(func(name string) { seen++ })
	assert.Equal(t, seen, 2)

	// GetRoom fail
	_, ok := rooms.Get("z")
	assert.False(t, ok)
	// GetRoom
	rm, ok := rooms.Get("a")
	assert.True(t, ok)
	assert.NotNil(t, rm)
	assert.Equal(t, string("a"), rm.Name)
}

type DummySyncable struct {
	room        *Room[RoomData, TestUserInfo]
	timesCalled int
}

func (ds *DummySyncable) Sync() {
	// Data() hits deadlock conditions from Publish()
	ds.room.GetData()
	ds.timesCalled++
}
func TestRoomJoinLeaveChannels(t *testing.T) {
	rooms := NewRooms[RoomData, TestUserInfo](string("a"))
	rm, _ := rooms.Get("a")
	u1 := TestUserInfo{"Bob"}
	u1Context := DummySyncable{room: rm}

	rooms.Start()
	defer rooms.Stop()
	uas := UserAndSync[RoomData, TestUserInfo]{user: &u1, sync: &u1Context}

	// Joining a room does *not* mark it dirty. It's on the user to call Sync() -
	// so the user gets the update immediately.
	rm.Join(&uas)

	// // Give it time to process
	time.Sleep(1 * time.Millisecond)

	assert.Equal(t, rm.dirty, false)
	assert.Equal(t, len(rm.members), 1)

	// Room Data
	rm.UpdateData(func(data *RoomData) {
		data.convo = append(data.convo, Statement{"Hello", u1})
	})
	assert.Equal(t, rm.dirty, true)

	data := rm.GetData()
	assert.Equal(t, len(data.convo), 1)

	// BROADCAST to connected users. Clears the dirty flag.
	rm.Publish()
	time.Sleep(1 * time.Millisecond)
	assert.Equal(t, rm.dirty, false)
	assert.Equal(t, u1Context.timesCalled, 1)

	// Leave
	rm.Leave(&u1)
	time.Sleep(1 * time.Millisecond)

	assert.Equal(t, len(rm.members), 0)
}
