package collections

import (
	"context"
	"fmt"
	"sync"
	"testing"
)

// Mock implementations for testing

// MockCollectionDatabase implements CollectionDatabase interface for testing
type MockCollectionDatabase struct {
	mu                    sync.RWMutex
	collections           []CollectionData
	collectionItems       map[int32][]CollectionItem
	collectionRewards     map[int32][]CollectionRewardData
	playerCollections     map[int32][]PlayerCollectionData
	playerCollectionItems map[string][]int32 // key: "charID-collectionID"
	failLoad              bool
	failSave              bool
}

func NewMockCollectionDatabase() *MockCollectionDatabase {
	return &MockCollectionDatabase{
		collections:           make([]CollectionData, 0),
		collectionItems:       make(map[int32][]CollectionItem),
		collectionRewards:     make(map[int32][]CollectionRewardData),
		playerCollections:     make(map[int32][]PlayerCollectionData),
		playerCollectionItems: make(map[string][]int32),
	}
}

func (m *MockCollectionDatabase) LoadCollections(ctx context.Context) ([]CollectionData, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	if m.failLoad {
		return nil, fmt.Errorf("mock load error")
	}

	return m.collections, nil
}

func (m *MockCollectionDatabase) LoadCollectionItems(ctx context.Context, collectionID int32) ([]CollectionItem, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	if m.failLoad {
		return nil, fmt.Errorf("mock load error")
	}

	items, exists := m.collectionItems[collectionID]
	if !exists {
		return []CollectionItem{}, nil
	}
	return items, nil
}

func (m *MockCollectionDatabase) LoadCollectionRewards(ctx context.Context, collectionID int32) ([]CollectionRewardData, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	if m.failLoad {
		return nil, fmt.Errorf("mock load error")
	}

	rewards, exists := m.collectionRewards[collectionID]
	if !exists {
		return []CollectionRewardData{}, nil
	}
	return rewards, nil
}

func (m *MockCollectionDatabase) LoadPlayerCollections(ctx context.Context, characterID int32) ([]PlayerCollectionData, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	if m.failLoad {
		return nil, fmt.Errorf("mock load error")
	}

	collections, exists := m.playerCollections[characterID]
	if !exists {
		return []PlayerCollectionData{}, nil
	}
	return collections, nil
}

func (m *MockCollectionDatabase) LoadPlayerCollectionItems(ctx context.Context, characterID, collectionID int32) ([]int32, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	if m.failLoad {
		return nil, fmt.Errorf("mock load error")
	}

	key := fmt.Sprintf("%d-%d", characterID, collectionID)
	items, exists := m.playerCollectionItems[key]
	if !exists {
		return []int32{}, nil
	}
	return items, nil
}

func (m *MockCollectionDatabase) SavePlayerCollection(ctx context.Context, characterID, collectionID int32, completed bool) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.failSave {
		return fmt.Errorf("mock save error")
	}

	// Update or add player collection
	found := false
	collections := m.playerCollections[characterID]
	for i := range collections {
		if collections[i].CollectionID == collectionID {
			collections[i].Completed = completed
			found = true
			break
		}
	}

	if !found {
		m.playerCollections[characterID] = append(collections, PlayerCollectionData{
			CharacterID:  characterID,
			CollectionID: collectionID,
			Completed:    completed,
		})
	}

	return nil
}

func (m *MockCollectionDatabase) SavePlayerCollectionItem(ctx context.Context, characterID, collectionID, itemID int32) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.failSave {
		return fmt.Errorf("mock save error")
	}

	key := fmt.Sprintf("%d-%d", characterID, collectionID)
	items := m.playerCollectionItems[key]
	
	// Check if already exists
	for _, existingItem := range items {
		if existingItem == itemID {
			return nil
		}
	}

	m.playerCollectionItems[key] = append(items, itemID)
	return nil
}

func (m *MockCollectionDatabase) SavePlayerCollections(ctx context.Context, characterID int32, collections []*Collection) error {
	if m.failSave {
		return fmt.Errorf("mock save error")
	}

	for _, collection := range collections {
		if collection.GetSaveNeeded() {
			err := m.SavePlayerCollection(ctx, characterID, collection.GetID(), collection.GetCompleted())
			if err != nil {
				return err
			}

			// Save items
			items := collection.GetCollectionItems()
			for _, item := range items {
				if item.Found == ItemFound {
					err := m.SavePlayerCollectionItem(ctx, characterID, collection.GetID(), item.ItemID)
					if err != nil {
						return err
					}
				}
			}
		}
	}

	return nil
}

// MockItemLookup implements ItemLookup interface for testing
type MockItemLookup struct {
	items map[int32]ItemInfo
}

func NewMockItemLookup() *MockItemLookup {
	return &MockItemLookup{
		items: make(map[int32]ItemInfo),
	}
}

func (m *MockItemLookup) GetItem(itemID int32) (ItemInfo, error) {
	if item, exists := m.items[itemID]; exists {
		return item, nil
	}
	return ItemInfo{}, fmt.Errorf("item not found")
}

func (m *MockItemLookup) ItemExists(itemID int32) bool {
	_, exists := m.items[itemID]
	return exists
}

func (m *MockItemLookup) GetItemName(itemID int32) string {
	if item, exists := m.items[itemID]; exists {
		return item.Name
	}
	return "Unknown Item"
}

// MockPlayerManager implements PlayerManager interface for testing
type MockPlayerManager struct {
	players map[int32]PlayerInfo
}

func NewMockPlayerManager() *MockPlayerManager {
	return &MockPlayerManager{
		players: make(map[int32]PlayerInfo),
	}
}

func (m *MockPlayerManager) GetPlayerInfo(characterID int32) (PlayerInfo, error) {
	if player, exists := m.players[characterID]; exists {
		return player, nil
	}
	return PlayerInfo{}, fmt.Errorf("player not found")
}

func (m *MockPlayerManager) IsPlayerOnline(characterID int32) bool {
	if player, exists := m.players[characterID]; exists {
		return player.IsOnline
	}
	return false
}

func (m *MockPlayerManager) GetPlayerLevel(characterID int32) int8 {
	if player, exists := m.players[characterID]; exists {
		return player.Level
	}
	return 0
}

// MockClientManager implements ClientManager interface for testing
type MockClientManager struct {
	mu               sync.Mutex
	sentUpdates      []int32
	sentCompletions  []int32
	sentLists        []int32
	sentProgress     []int32
	failSend         bool
}

func NewMockClientManager() *MockClientManager {
	return &MockClientManager{
		sentUpdates:     make([]int32, 0),
		sentCompletions: make([]int32, 0),
		sentLists:       make([]int32, 0),
		sentProgress:    make([]int32, 0),
	}
}

func (m *MockClientManager) SendCollectionUpdate(characterID int32, collection *Collection) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.failSend {
		return fmt.Errorf("mock send error")
	}

	m.sentUpdates = append(m.sentUpdates, characterID)
	return nil
}

func (m *MockClientManager) SendCollectionComplete(characterID int32, collection *Collection) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.failSend {
		return fmt.Errorf("mock send error")
	}

	m.sentCompletions = append(m.sentCompletions, characterID)
	return nil
}

func (m *MockClientManager) SendCollectionList(characterID int32, collections []CollectionInfo) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.failSend {
		return fmt.Errorf("mock send error")
	}

	m.sentLists = append(m.sentLists, characterID)
	return nil
}

func (m *MockClientManager) SendCollectionProgress(characterID int32, progress []CollectionProgress) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.failSend {
		return fmt.Errorf("mock send error")
	}

	m.sentProgress = append(m.sentProgress, characterID)
	return nil
}

// MockRewardProvider implements RewardProvider interface for testing
type MockRewardProvider struct {
	mu         sync.Mutex
	givenItems []struct {
		CharacterID int32
		ItemID      int32
		Quantity    int8
	}
	givenCoin []struct {
		CharacterID int32
		Amount      int64
	}
	givenXP []struct {
		CharacterID int32
		Amount      int64
	}
	failGive     bool
	failValidate bool
}

func NewMockRewardProvider() *MockRewardProvider {
	return &MockRewardProvider{}
}

func (m *MockRewardProvider) GiveItem(characterID int32, itemID int32, quantity int8) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.failGive {
		return fmt.Errorf("mock give error")
	}

	m.givenItems = append(m.givenItems, struct {
		CharacterID int32
		ItemID      int32
		Quantity    int8
	}{characterID, itemID, quantity})
	return nil
}

func (m *MockRewardProvider) GiveCoin(characterID int32, amount int64) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.failGive {
		return fmt.Errorf("mock give error")
	}

	m.givenCoin = append(m.givenCoin, struct {
		CharacterID int32
		Amount      int64
	}{characterID, amount})
	return nil
}

func (m *MockRewardProvider) GiveXP(characterID int32, amount int64) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.failGive {
		return fmt.Errorf("mock give error")
	}

	m.givenXP = append(m.givenXP, struct {
		CharacterID int32
		Amount      int64
	}{characterID, amount})
	return nil
}

func (m *MockRewardProvider) ValidateRewards(characterID int32, rewards []CollectionRewardItem, coin, xp int64) error {
	if m.failValidate {
		return fmt.Errorf("mock validate error")
	}
	return nil
}

// Tests for Collection

func TestNewCollection(t *testing.T) {
	c := NewCollection()
	c.SetID(1)
	c.SetName("Test Collection")
	c.SetCategory("Test Category")
	c.SetLevel(10)

	if c.GetID() != 1 {
		t.Errorf("Expected ID 1, got %d", c.GetID())
	}
	if c.GetName() != "Test Collection" {
		t.Errorf("Expected name 'Test Collection', got %s", c.GetName())
	}
	if c.GetCategory() != "Test Category" {
		t.Errorf("Expected category 'Test Category', got %s", c.GetCategory())
	}
	if c.GetLevel() != 10 {
		t.Errorf("Expected level 10, got %d", c.GetLevel())
	}
	if c.GetCompleted() {
		t.Error("Expected collection to be incomplete")
	}
	if c.GetSaveNeeded() {
		t.Error("Expected saveNeeded to be false")
	}
}

func TestCollectionCopy(t *testing.T) {
	c1 := NewCollection()
	c1.SetID(1)
	c1.SetName("Original")
	c1.SetCategory("Category")
	c1.SetLevel(5)
	c1.SetRewardCoin(1000)
	c1.SetRewardXP(5000)
	c1.AddCollectionItem(CollectionItem{ItemID: 100, Index: 0, Found: ItemNotFound})

	c2 := NewCollectionFromData(c1)

	// Verify copy has same values
	if c2.GetID() != c1.GetID() {
		t.Error("Copy has different ID")
	}
	if c2.GetName() != c1.GetName() {
		t.Error("Copy has different name")
	}
	if c2.GetRewardCoin() != c1.GetRewardCoin() {
		t.Error("Copy has different reward coin")
	}

	// Verify deep copy (modifying copy doesn't affect original)
	c2.SetRewardCoin(2000)
	if c1.GetRewardCoin() == c2.GetRewardCoin() {
		t.Error("Modifying copy affected original")
	}
}

func TestCollectionItems(t *testing.T) {
	c := NewCollection()
	c.SetID(1)
	c.SetName("Test")
	c.SetCategory("Category")
	c.SetLevel(1)

	// Add items
	c.AddCollectionItem(CollectionItem{ItemID: 100, Index: 0, Found: ItemNotFound})
	c.AddCollectionItem(CollectionItem{ItemID: 101, Index: 1, Found: ItemNotFound})
	c.AddCollectionItem(CollectionItem{ItemID: 102, Index: 2, Found: ItemNotFound})

	items := c.GetCollectionItems()
	if len(items) != 3 {
		t.Errorf("Expected 3 items, got %d", len(items))
	}

	// Test finding item
	found := c.MarkItemFound(101)
	if !found {
		t.Error("Expected to find item 101")
	}
	if !c.GetSaveNeeded() {
		t.Error("Expected saveNeeded to be true after finding item")
	}

	// Test finding non-existent item
	found = c.MarkItemFound(999)
	if found {
		t.Error("Should not find non-existent item")
	}

	// Check if item is needed
	if !c.NeedsItem(100) {
		t.Error("Expected NeedsItem to return true for unfound item")
	}
	if c.NeedsItem(101) {
		t.Error("Expected NeedsItem to return false for found item")
	}

	// Check ready status
	if c.GetIsReadyToTurnIn() {
		t.Error("Collection should not be ready with unfound items")
	}

	// Find remaining items
	c.MarkItemFound(100)
	c.MarkItemFound(102)

	if !c.GetIsReadyToTurnIn() {
		t.Error("Collection should be ready when all items found")
	}
}

func TestCollectionRewards(t *testing.T) {
	c := NewCollection()
	c.SetID(1)
	c.SetName("Test")
	c.SetCategory("Category")
	c.SetLevel(1)

	// Set rewards
	c.SetRewardCoin(1000)
	c.SetRewardXP(5000)

	// Add reward items
	c.AddRewardItem(CollectionRewardItem{ItemID: 200, Quantity: 1})
	c.AddRewardItem(CollectionRewardItem{ItemID: 201, Quantity: 5})

	// Add selectable rewards
	c.AddSelectableRewardItem(CollectionRewardItem{ItemID: 300, Quantity: 1})
	c.AddSelectableRewardItem(CollectionRewardItem{ItemID: 301, Quantity: 1})

	if c.GetRewardCoin() != 1000 {
		t.Errorf("Expected reward coin 1000, got %d", c.GetRewardCoin())
	}
	if c.GetRewardXP() != 5000 {
		t.Errorf("Expected reward XP 5000, got %d", c.GetRewardXP())
	}

	rewards := c.GetRewardItems()
	if len(rewards) != 2 {
		t.Errorf("Expected 2 reward items, got %d", len(rewards))
	}

	selectable := c.GetSelectableRewardItems()
	if len(selectable) != 2 {
		t.Errorf("Expected 2 selectable rewards, got %d", len(selectable))
	}
}

func TestCollectionCompletion(t *testing.T) {
	c := NewCollection()
	c.SetID(1)
	c.SetName("Test")
	c.SetCategory("Category")
	c.SetLevel(1)
	c.AddCollectionItem(CollectionItem{ItemID: 100, Index: 0, Found: ItemNotFound})

	// Check not ready to complete without all items
	if c.GetIsReadyToTurnIn() {
		t.Error("Should not be ready to complete without all items")
	}

	// Find item
	c.MarkItemFound(100)
	if !c.GetIsReadyToTurnIn() {
		t.Error("Should be ready to complete when all items found")
	}

	// Mark as completed
	c.SetCompleted(true)
	if !c.GetCompleted() {
		t.Error("Expected collection to be completed")
	}
}

func TestCollectionProgress(t *testing.T) {
	c := NewCollection()
	c.SetID(1)
	c.SetName("Test")
	c.SetCategory("Category")
	c.SetLevel(1)
	c.AddCollectionItem(CollectionItem{ItemID: 100, Index: 0, Found: ItemNotFound})
	c.AddCollectionItem(CollectionItem{ItemID: 101, Index: 1, Found: ItemNotFound})
	c.AddCollectionItem(CollectionItem{ItemID: 102, Index: 2, Found: ItemNotFound})
	c.AddCollectionItem(CollectionItem{ItemID: 103, Index: 3, Found: ItemNotFound})

	// Initial progress
	progress := c.GetProgress()
	if progress != 0.0 {
		t.Errorf("Expected 0%% progress, got %.2f%%", progress)
	}

	// Find one item
	c.MarkItemFound(100)
	progress = c.GetProgress()
	if progress != 25.0 {
		t.Errorf("Expected 25%% progress, got %.2f%%", progress)
	}

	// Find more items
	c.MarkItemFound(101)
	c.MarkItemFound(102)
	progress = c.GetProgress()
	if progress != 75.0 {
		t.Errorf("Expected 75%% progress, got %.2f%%", progress)
	}

	// Complete collection
	c.MarkItemFound(103)
	c.SetCompleted(true)
	progress = c.GetProgress()
	if progress != 100.0 {
		t.Errorf("Expected 100%% progress, got %.2f%%", progress)
	}
}

// Tests for MasterCollectionList

func TestNewMasterCollectionList(t *testing.T) {
	db := NewMockCollectionDatabase()
	ml := NewMasterCollectionList(db)

	if ml == nil {
		t.Fatal("Failed to create MasterCollectionList")
	}
}

func TestMasterCollectionListLoad(t *testing.T) {
	db := NewMockCollectionDatabase()
	
	// Add test data
	db.collections = []CollectionData{
		{ID: 1, Name: "Collection 1", Category: "Category A", Level: 10},
		{ID: 2, Name: "Collection 2", Category: "Category B", Level: 20},
	}
	
	db.collectionItems[1] = []CollectionItem{
		{ItemID: 100, Index: 0, Found: ItemNotFound},
		{ItemID: 101, Index: 1, Found: ItemNotFound},
	}
	
	db.collectionItems[2] = []CollectionItem{
		{ItemID: 200, Index: 0, Found: ItemNotFound},
	}
	
	db.collectionRewards[1] = []CollectionRewardData{
		{CollectionID: 1, RewardType: RewardTypeCoin, RewardValue: "1000", Quantity: 1},
		{CollectionID: 1, RewardType: RewardTypeXP, RewardValue: "5000", Quantity: 1},
	}

	// Set up item lookup
	itemLookup := NewMockItemLookup()
	itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"}
	itemLookup.items[101] = ItemInfo{ID: 101, Name: "Test Item 2"}
	itemLookup.items[200] = ItemInfo{ID: 200, Name: "Test Item 3"}
	
	ml := NewMasterCollectionList(db)
	err := ml.Initialize(context.Background(), itemLookup)
	if err != nil {
		t.Fatalf("Failed to load collections: %v", err)
	}

	// Verify collections loaded
	collection := ml.GetCollection(1)
	if collection == nil {
		t.Fatal("Collection 1 not found")
	}
	if collection.GetName() != "Collection 1" {
		t.Errorf("Expected name 'Collection 1', got %s", collection.GetName())
	}

	// Verify items loaded
	items := collection.GetCollectionItems()
	if len(items) != 2 {
		t.Errorf("Expected 2 items, got %d", len(items))
	}

	// Verify rewards loaded
	if collection.GetRewardCoin() != 1000 {
		t.Errorf("Expected reward coin 1000, got %d", collection.GetRewardCoin())
	}
}

func TestMasterCollectionListSearch(t *testing.T) {
	db := NewMockCollectionDatabase()
	
	db.collections = []CollectionData{
		{ID: 1, Name: "Ancient Artifacts", Category: "Archaeology", Level: 10},
		{ID: 2, Name: "Ancient Weapons", Category: "Combat", Level: 20},
		{ID: 3, Name: "Modern Art", Category: "Art", Level: 15},
	}
	
	// Add required items for each collection
	db.collectionItems[1] = []CollectionItem{
		{ItemID: 100, Index: 0, Found: ItemNotFound},
	}
	db.collectionItems[2] = []CollectionItem{
		{ItemID: 200, Index: 0, Found: ItemNotFound},
	}
	db.collectionItems[3] = []CollectionItem{
		{ItemID: 300, Index: 0, Found: ItemNotFound},
	}

	// Set up item lookup
	itemLookup := NewMockItemLookup()
	itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"}
	itemLookup.items[200] = ItemInfo{ID: 200, Name: "Test Item 2"}
	itemLookup.items[300] = ItemInfo{ID: 300, Name: "Test Item 3"}

	ml := NewMasterCollectionList(db)
	ml.Initialize(context.Background(), itemLookup)

	// Search by name
	results := ml.FindCollectionsByName("Ancient")
	if len(results) != 2 {
		t.Errorf("Expected 2 results for 'Ancient', got %d", len(results))
	}

	// Search by category
	results = ml.GetCollectionsByCategory("Art")
	if len(results) != 1 {
		t.Errorf("Expected 1 result for category 'Art', got %d", len(results))
	}

	// Search by level range
	results = ml.GetCollectionsByLevel(15, 25)
	if len(results) != 2 {
		t.Errorf("Expected 2 results for level range 15-25, got %d", len(results))
	}
}

// Tests for PlayerCollectionList

func TestNewPlayerCollectionList(t *testing.T) {
	db := NewMockCollectionDatabase()
	pl := NewPlayerCollectionList(1001, db)

	if pl == nil {
		t.Fatal("Failed to create PlayerCollectionList")
	}
	if pl.GetCharacterID() != 1001 {
		t.Errorf("Expected character ID 1001, got %d", pl.GetCharacterID())
	}
}

func TestPlayerCollectionListLoad(t *testing.T) {
	db := NewMockCollectionDatabase()
	masterList := NewMasterCollectionList(db)

	// Set up master collections
	db.collections = []CollectionData{
		{ID: 1, Name: "Collection 1", Category: "Category A", Level: 10},
		{ID: 2, Name: "Collection 2", Category: "Category B", Level: 20},
	}
	db.collectionItems[1] = []CollectionItem{
		{ItemID: 100, Index: 0, Found: ItemNotFound},
		{ItemID: 101, Index: 1, Found: ItemNotFound},
	}
	db.collectionItems[2] = []CollectionItem{
		{ItemID: 200, Index: 0, Found: ItemNotFound},
	}

	// Set up item lookup
	itemLookup := NewMockItemLookup()
	itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"}
	itemLookup.items[101] = ItemInfo{ID: 101, Name: "Test Item 2"}
	itemLookup.items[200] = ItemInfo{ID: 200, Name: "Test Item 3"}
	
	masterList.Initialize(context.Background(), itemLookup)

	// Set up player data
	db.playerCollections[1001] = []PlayerCollectionData{
		{CharacterID: 1001, CollectionID: 1, Completed: false},
		{CharacterID: 1001, CollectionID: 2, Completed: true},
	}
	db.playerCollectionItems["1001-1"] = []int32{100} // Found item 100 in collection 1

	pl := NewPlayerCollectionList(1001, db)
	err := pl.Initialize(context.Background(), masterList)
	if err != nil {
		t.Fatalf("Failed to load player collections: %v", err)
	}

	// Check loaded collections
	collection := pl.GetCollection(1)
	if collection == nil {
		t.Fatal("Collection 1 not found")
	}
	if collection.GetCompleted() {
		t.Error("Collection 1 should not be completed")
	}

	// Check found items
	if collection.NeedsItem(100) {
		t.Error("Item 100 should be found")
	}
	if !collection.NeedsItem(101) {
		t.Error("Item 101 should not be found")
	}

	// Check completed collection
	collection2 := pl.GetCollection(2)
	if collection2 == nil {
		t.Fatal("Collection 2 not found")
	}
	if !collection2.GetCompleted() {
		t.Error("Collection 2 should be completed")
	}
}

func TestPlayerCollectionListAddItem(t *testing.T) {
	db := NewMockCollectionDatabase()
	masterList := NewMasterCollectionList(db)

	// Set up collections
	db.collections = []CollectionData{
		{ID: 1, Name: "Collection 1", Category: "Category A", Level: 10},
	}
	db.collectionItems[1] = []CollectionItem{
		{ItemID: 100, Index: 0, Found: ItemNotFound},
		{ItemID: 101, Index: 1, Found: ItemNotFound},
	}

	// Set up item lookup
	itemLookup := NewMockItemLookup()
	itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"}
	itemLookup.items[101] = ItemInfo{ID: 101, Name: "Test Item 2"}

	masterList.Initialize(context.Background(), itemLookup)

	pl := NewPlayerCollectionList(1001, db)
	
	// Try to add collection item directly - this will depend on the actual API
	// For now, just test basic functionality
	if pl.GetCharacterID() != 1001 {
		t.Errorf("Expected character ID 1001, got %d", pl.GetCharacterID())
	}

	// Load collections first
	err := pl.Initialize(context.Background(), masterList)
	if err != nil {
		t.Fatalf("Failed to load player collections: %v", err)
	}
}

// Tests for CollectionManager

func TestNewCollectionManager(t *testing.T) {
	db := NewMockCollectionDatabase()
	itemLookup := NewMockItemLookup()
	
	cm := NewCollectionManager(db, itemLookup)
	if cm == nil {
		t.Fatal("Failed to create CollectionManager")
	}
}

func TestCollectionManagerInitialize(t *testing.T) {
	db := NewMockCollectionDatabase()
	itemLookup := NewMockItemLookup()
	
	// Add test data
	db.collections = []CollectionData{
		{ID: 1, Name: "Collection 1", Category: "Category A", Level: 10},
	}
	db.collectionItems[1] = []CollectionItem{
		{ItemID: 100, Index: 0, Found: ItemNotFound},
	}
	
	// Set up item lookup
	itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"}
	
	cm := NewCollectionManager(db, itemLookup)
	err := cm.Initialize(context.Background())
	if err != nil {
		t.Fatalf("Failed to initialize: %v", err)
	}

	// Verify master list loaded
	stats := cm.GetSystemStatistics()
	if stats.TotalCollections != 1 {
		t.Errorf("Expected 1 collection, got %d", stats.TotalCollections)
	}
}

func TestCollectionManagerPlayerOperations(t *testing.T) {
	db := NewMockCollectionDatabase()
	itemLookup := NewMockItemLookup()
	
	// Add test data
	db.collections = []CollectionData{
		{ID: 1, Name: "Collection 1", Category: "Category A", Level: 10},
	}
	db.collectionItems[1] = []CollectionItem{
		{ItemID: 100, Index: 0, Found: ItemNotFound},
		{ItemID: 101, Index: 1, Found: ItemNotFound},
	}

	// Set up item lookup
	itemLookup.items[100] = ItemInfo{ID: 100, Name: "Test Item 1"}
	itemLookup.items[101] = ItemInfo{ID: 101, Name: "Test Item 2"}

	cm := NewCollectionManager(db, itemLookup)
	err := cm.Initialize(context.Background())
	if err != nil {
		t.Fatalf("Failed to initialize: %v", err)
	}

	// Verify statistics
	stats := cm.GetSystemStatistics()
	if stats.TotalCollections != 1 {
		t.Errorf("Expected 1 collection, got %d", stats.TotalCollections)
	}

	// Get collection copy
	collection := cm.GetCollectionCopy(1)
	if collection == nil {
		t.Fatal("Failed to get collection copy")
	}
	if collection.GetName() != "Collection 1" {
		t.Errorf("Expected name 'Collection 1', got %s", collection.GetName())
	}
}

// Note: Database integration tests were removed to eliminate dependency on internal/database wrapper.
// Database functionality is tested through unit tests with mocks above.

// Tests for EntityCollectionAdapter

// Mock entity for testing
type mockEntity struct {
	id int32
}

func (m *mockEntity) GetID() int32 {
	return m.id
}

func TestEntityCollectionAdapter(t *testing.T) {
	entity := &mockEntity{id: 1001}

	// Mock player manager
	pm := NewMockPlayerManager()
	pm.players[1001] = PlayerInfo{
		CharacterID: 1001,
		Level:       50,
	}

	adapter := &EntityCollectionAdapter{
		entity: entity,
		playerManager: pm,
	}

	if adapter.GetCharacterID() != 1001 {
		t.Errorf("Expected character ID 1001, got %d", adapter.GetCharacterID())
	}

	if adapter.GetLevel() != 50 {
		t.Errorf("Expected level 50, got %d", adapter.GetLevel())
	}

	// Test HasItem (should return false as placeholder)
	if adapter.HasItem(100) {
		t.Error("HasItem should return false")
	}
}

// Concurrency tests

func TestCollectionConcurrency(t *testing.T) {
	c := NewCollection()
	c.SetID(1)
	c.SetName("Test")
	c.SetCategory("Category")
	c.SetLevel(1)
	
	// Add items
	for i := 0; i < 100; i++ {
		c.AddCollectionItem(CollectionItem{ItemID: int32(i), Index: int8(i), Found: ItemNotFound})
	}

	// Concurrent reads and writes
	var wg sync.WaitGroup
	errors := make(chan error, 100)

	// Writers
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func(itemID int32) {
			defer wg.Done()
			c.MarkItemFound(itemID)
		}(int32(i))
	}

	// Readers
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			_ = c.GetProgress()
			_ = c.GetCollectionItems()
			_ = c.GetIsReadyToTurnIn()
		}()
	}

	wg.Wait()
	close(errors)

	// Check for errors
	for err := range errors {
		if err != nil {
			t.Errorf("Concurrent operation error: %v", err)
		}
	}
}

func TestMasterCollectionListConcurrency(t *testing.T) {
	db := NewMockCollectionDatabase()
	
	// Add test data
	for i := 1; i <= 100; i++ {
		db.collections = append(db.collections, CollectionData{
			ID:       int32(i),
			Name:     fmt.Sprintf("Collection %d", i),
			Category: fmt.Sprintf("Category %d", i%10),
			Level:    int8(i % 50),
		})
		
		// Add at least one item per collection
		db.collectionItems[int32(i)] = []CollectionItem{
			{ItemID: int32(i * 100), Index: 0, Found: ItemNotFound},
		}
	}

	// Set up item lookup for all items
	itemLookup := NewMockItemLookup()
	for i := 1; i <= 100; i++ {
		itemLookup.items[int32(i*100)] = ItemInfo{ID: int32(i * 100), Name: fmt.Sprintf("Item %d", i)}
	}

	ml := NewMasterCollectionList(db)
	ml.Initialize(context.Background(), itemLookup)

	var wg sync.WaitGroup

	// Concurrent reads
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(id int32) {
			defer wg.Done()
			_ = ml.GetCollection(id)
			_ = ml.FindCollectionsByName("Collection 1")
			_ = ml.GetCollectionsByCategory("Category 1")
			_ = ml.GetAllCollections()
		}(int32(i + 1))
	}

	wg.Wait()
}

// Benchmarks

func BenchmarkCollectionMarkItemFound(b *testing.B) {
	c := NewCollection()
	c.SetID(1)
	c.SetName("Test")
	c.SetCategory("Category")
	c.SetLevel(1)
	
	// Add items
	for i := 0; i < 100; i++ {
		c.AddCollectionItem(CollectionItem{ItemID: int32(i), Index: int8(i), Found: ItemNotFound})
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		c.MarkItemFound(int32(i % 100))
	}
}

func BenchmarkMasterCollectionListSearch(b *testing.B) {
	db := NewMockCollectionDatabase()
	
	// Add test data with diverse names
	for i := 1; i <= 1000; i++ {
		db.collections = append(db.collections, CollectionData{
			ID:       int32(i),
			Name:     fmt.Sprintf("Collection %d", i),
			Category: fmt.Sprintf("Category %d", i%10),
			Level:    int8(i % 50),
		})
		
		// Add at least one item per collection
		db.collectionItems[int32(i)] = []CollectionItem{
			{ItemID: int32(i * 100), Index: 0, Found: ItemNotFound},
		}
	}

	// Set up item lookup for all items
	itemLookup := NewMockItemLookup()
	for i := 1; i <= 1000; i++ {
		itemLookup.items[int32(i*100)] = ItemInfo{ID: int32(i * 100), Name: fmt.Sprintf("Item %d", i)}
	}

	ml := NewMasterCollectionList(db)
	ml.Initialize(context.Background(), itemLookup)

	// Test with various search patterns
	searches := []string{
		"Collection 500", // Exact match - 1 result
		"Collection 5",   // Prefix match - ~111 results (5, 50-59, 150-159, etc.)
		"NonExistent",    // No matches - 0 results
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		search := searches[i%len(searches)]
		_ = ml.FindCollectionsByName(search)
	}
}

func BenchmarkPlayerCollectionListLoad(b *testing.B) {
	db := NewMockCollectionDatabase()
	masterList := NewMasterCollectionList(db)

	// Set up collections
	for i := 1; i <= 100; i++ {
		db.collections = append(db.collections, CollectionData{
			ID:       int32(i),
			Name:     fmt.Sprintf("Collection %d", i),
			Category: "Category",
			Level:    10,
		})
		
		items := make([]CollectionItem, 10)
		for j := 0; j < 10; j++ {
			items[j] = CollectionItem{
				ItemID: int32(i*100 + j),
				Index:  int8(j),
				Found:  ItemNotFound,
			}
		}
		db.collectionItems[int32(i)] = items
	}

	// Set up item lookup for all items
	itemLookup := NewMockItemLookup()
	for i := 1; i <= 100; i++ {
		for j := 0; j < 10; j++ {
			itemID := int32(i*100 + j)
			itemLookup.items[itemID] = ItemInfo{ID: itemID, Name: fmt.Sprintf("Item %d", itemID)}
		}
	}

	masterList.Initialize(context.Background(), itemLookup)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		pl := NewPlayerCollectionList(1001, db)
		pl.Initialize(context.Background(), masterList)
	}
}

// Test constants
func TestConstants(t *testing.T) {
	// Test reward types
	if RewardTypeItem != "Item" {
		t.Errorf("Expected RewardTypeItem to be 'Item', got %s", RewardTypeItem)
	}
	if RewardTypeSelectable != "Selectable" {
		t.Errorf("Expected RewardTypeSelectable to be 'Selectable', got %s", RewardTypeSelectable)
	}
	if RewardTypeCoin != "Coin" {
		t.Errorf("Expected RewardTypeCoin to be 'Coin', got %s", RewardTypeCoin)
	}
	if RewardTypeXP != "XP" {
		t.Errorf("Expected RewardTypeXP to be 'XP', got %s", RewardTypeXP)
	}

	// Test item states
	if ItemNotFound != 0 {
		t.Errorf("Expected ItemNotFound to be 0, got %d", ItemNotFound)
	}
	if ItemFound != 1 {
		t.Errorf("Expected ItemFound to be 1, got %d", ItemFound)
	}

	// Test collection states
	if CollectionIncomplete != false {
		t.Error("Expected CollectionIncomplete to be false")
	}
	if CollectionCompleted != true {
		t.Error("Expected CollectionCompleted to be true")
	}
}