package collections

import (
	"context"
	"fmt"
	"sort"
	"strings"
)

// NewMasterCollectionList creates a new master collection list
func NewMasterCollectionList(database CollectionDatabase) *MasterCollectionList {
	return &MasterCollectionList{
		collections: make(map[int32]*Collection),
		database:    database,
	}
}

// Initialize loads all collections from the database
func (mcl *MasterCollectionList) Initialize(ctx context.Context, itemLookup ItemLookup) error {
	mcl.mu.Lock()
	defer mcl.mu.Unlock()

	// Load collection data
	collectionData, err := mcl.database.LoadCollections(ctx)
	if err != nil {
		return fmt.Errorf("failed to load collections: %w", err)
	}

	totalItems := 0
	totalRewards := 0

	for _, data := range collectionData {
		collection := NewCollection()
		collection.SetID(data.ID)
		collection.SetName(data.Name)
		collection.SetCategory(data.Category)
		collection.SetLevel(data.Level)

		// Load collection items
		items, err := mcl.database.LoadCollectionItems(ctx, data.ID)
		if err != nil {
			return fmt.Errorf("failed to load items for collection %d: %w", data.ID, err)
		}

		for _, item := range items {
			// Validate item exists
			if itemLookup != nil && !itemLookup.ItemExists(item.ItemID) {
				continue // Skip non-existent items
			}
			collection.AddCollectionItem(item)
			totalItems++
		}

		// Load collection rewards
		rewards, err := mcl.database.LoadCollectionRewards(ctx, data.ID)
		if err != nil {
			return fmt.Errorf("failed to load rewards for collection %d: %w", data.ID, err)
		}

		if err := collection.LoadFromRewardData(rewards); err != nil {
			return fmt.Errorf("failed to load reward data for collection %d: %w", data.ID, err)
		}
		totalRewards += len(rewards)

		// Validate collection before adding
		if err := collection.Validate(); err != nil {
			return fmt.Errorf("invalid collection %d (%s): %w", data.ID, data.Name, err)
		}

		if !mcl.addCollectionNoLock(collection) {
			return fmt.Errorf("duplicate collection ID: %d", data.ID)
		}
	}

	return nil
}

// AddCollection adds a collection to the master list
func (mcl *MasterCollectionList) AddCollection(collection *Collection) bool {
	mcl.mu.Lock()
	defer mcl.mu.Unlock()
	return mcl.addCollectionNoLock(collection)
}

// addCollectionNoLock adds a collection without acquiring the lock
func (mcl *MasterCollectionList) addCollectionNoLock(collection *Collection) bool {
	if collection == nil {
		return false
	}

	id := collection.GetID()
	if _, exists := mcl.collections[id]; exists {
		return false
	}

	mcl.collections[id] = collection
	return true
}

// GetCollection retrieves a collection by ID
func (mcl *MasterCollectionList) GetCollection(collectionID int32) *Collection {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()
	return mcl.collections[collectionID]
}

// GetCollectionCopy retrieves a copy of a collection by ID
func (mcl *MasterCollectionList) GetCollectionCopy(collectionID int32) *Collection {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	if collection, exists := mcl.collections[collectionID]; exists {
		return NewCollectionFromData(collection)
	}
	return nil
}

// ClearCollections removes all collections
func (mcl *MasterCollectionList) ClearCollections() {
	mcl.mu.Lock()
	defer mcl.mu.Unlock()
	mcl.collections = make(map[int32]*Collection)
}

// Size returns the number of collections
func (mcl *MasterCollectionList) Size() int {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()
	return len(mcl.collections)
}

// NeedsItem checks if any collection needs a specific item
func (mcl *MasterCollectionList) NeedsItem(itemID int32) bool {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	for _, collection := range mcl.collections {
		if collection.NeedsItem(itemID) {
			return true
		}
	}

	return false
}

// GetCollectionsByCategory returns collections in a specific category
func (mcl *MasterCollectionList) GetCollectionsByCategory(category string) []*Collection {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	var result []*Collection
	for _, collection := range mcl.collections {
		if collection.GetCategory() == category {
			result = append(result, collection)
		}
	}

	return result
}

// GetCollectionsByLevel returns collections for a specific level range
func (mcl *MasterCollectionList) GetCollectionsByLevel(minLevel, maxLevel int8) []*Collection {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	var result []*Collection
	for _, collection := range mcl.collections {
		level := collection.GetLevel()
		if level >= minLevel && level <= maxLevel {
			result = append(result, collection)
		}
	}

	return result
}

// GetAllCollections returns all collections
func (mcl *MasterCollectionList) GetAllCollections() []*Collection {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	result := make([]*Collection, 0, len(mcl.collections))
	for _, collection := range mcl.collections {
		result = append(result, collection)
	}

	return result
}

// GetCollectionIDs returns all collection IDs
func (mcl *MasterCollectionList) GetCollectionIDs() []int32 {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	ids := make([]int32, 0, len(mcl.collections))
	for id := range mcl.collections {
		ids = append(ids, id)
	}

	sort.Slice(ids, func(i, j int) bool {
		return ids[i] < ids[j]
	})

	return ids
}

// GetCategories returns all unique categories
func (mcl *MasterCollectionList) GetCategories() []string {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	categoryMap := make(map[string]bool)
	for _, collection := range mcl.collections {
		category := collection.GetCategory()
		if category != "" {
			categoryMap[category] = true
		}
	}

	categories := make([]string, 0, len(categoryMap))
	for category := range categoryMap {
		categories = append(categories, category)
	}

	sort.Strings(categories)
	return categories
}

// GetCollectionsRequiringItem returns collections that need a specific item
func (mcl *MasterCollectionList) GetCollectionsRequiringItem(itemID int32) []*Collection {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	var result []*Collection
	for _, collection := range mcl.collections {
		if collection.NeedsItem(itemID) {
			result = append(result, collection)
		}
	}

	return result
}

// GetStatistics returns master collection list statistics
func (mcl *MasterCollectionList) GetStatistics() CollectionStatistics {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	stats := CollectionStatistics{
		TotalCollections: len(mcl.collections),
	}

	for _, collection := range mcl.collections {
		if collection.GetCompleted() {
			stats.CompletedCollections++
		}
		if collection.GetTotalItemsCount() > 0 {
			stats.ActiveCollections++
		}
		stats.TotalItems += collection.GetTotalItemsCount()
		stats.FoundItems += collection.GetFoundItemsCount()
		stats.TotalRewards += len(collection.GetRewardItems()) + len(collection.GetSelectableRewardItems())
		if collection.GetRewardCoin() > 0 {
			stats.TotalRewards++
		}
		if collection.GetRewardXP() > 0 {
			stats.TotalRewards++
		}
	}

	return stats
}

// ValidateIntegrity checks the integrity of all collections
func (mcl *MasterCollectionList) ValidateIntegrity(itemLookup ItemLookup) []error {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	var errors []error

	for _, collection := range mcl.collections {
		if err := collection.Validate(); err != nil {
			errors = append(errors, fmt.Errorf("collection %d (%s): %w",
				collection.GetID(), collection.GetName(), err))
		}

		// Check if all required items exist
		if itemLookup != nil {
			for _, item := range collection.GetCollectionItems() {
				if !itemLookup.ItemExists(item.ItemID) {
					errors = append(errors, fmt.Errorf("collection %d (%s) references non-existent item %d",
						collection.GetID(), collection.GetName(), item.ItemID))
				}
			}

			// Check reward items
			for _, item := range collection.GetRewardItems() {
				if !itemLookup.ItemExists(item.ItemID) {
					errors = append(errors, fmt.Errorf("collection %d (%s) has non-existent reward item %d",
						collection.GetID(), collection.GetName(), item.ItemID))
				}
			}

			for _, item := range collection.GetSelectableRewardItems() {
				if !itemLookup.ItemExists(item.ItemID) {
					errors = append(errors, fmt.Errorf("collection %d (%s) has non-existent selectable reward item %d",
						collection.GetID(), collection.GetName(), item.ItemID))
				}
			}
		}
	}

	return errors
}

// FindCollectionsByName searches for collections by name (case-insensitive)
func (mcl *MasterCollectionList) FindCollectionsByName(searchTerm string) []*Collection {
	mcl.mu.RLock()
	defer mcl.mu.RUnlock()

	var result []*Collection
	searchLower := strings.ToLower(searchTerm)

	for _, collection := range mcl.collections {
		if strings.Contains(strings.ToLower(collection.GetName()), searchLower) {
			result = append(result, collection)
		}
	}

	// Sort by name
	sort.Slice(result, func(i, j int) bool {
		return result[i].GetName() < result[j].GetName()
	})

	return result
}
