package collections

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

// NewPlayerCollectionList creates a new player collection list
func NewPlayerCollectionList(characterID int32, database CollectionDatabase) *PlayerCollectionList {
	return &PlayerCollectionList{
		characterID: characterID,
		collections: make(map[int32]*Collection),
		database:    database,
	}
}

// Initialize loads player's collection progress from database
func (pcl *PlayerCollectionList) Initialize(ctx context.Context, masterList *MasterCollectionList) error {
	pcl.mu.Lock()
	defer pcl.mu.Unlock()

	// Load player collection data
	playerCollections, err := pcl.database.LoadPlayerCollections(ctx, pcl.characterID)
	if err != nil {
		return fmt.Errorf("failed to load player collections: %w", err)
	}

	for _, playerCollection := range playerCollections {
		// Get the master collection template
		masterCollection := masterList.GetCollection(playerCollection.CollectionID)
		if masterCollection == nil {
			continue // Skip collections that no longer exist
		}

		// Create a copy for the player
		collection := NewCollectionFromData(masterCollection)
		if collection == nil {
			continue
		}

		collection.SetCompleted(playerCollection.Completed)

		// Load player's found items
		foundItems, err := pcl.database.LoadPlayerCollectionItems(ctx, pcl.characterID, playerCollection.CollectionID)
		if err != nil {
			return fmt.Errorf("failed to load player collection items for collection %d: %w", playerCollection.CollectionID, err)
		}

		// Mark found items
		for _, itemID := range foundItems {
			collection.MarkItemFound(itemID)
		}

		// Reset save needed flag after loading
		collection.SetSaveNeeded(false)

		pcl.collections[playerCollection.CollectionID] = collection
	}

	return nil
}

// AddCollection adds a collection to the player's list
func (pcl *PlayerCollectionList) AddCollection(collection *Collection) bool {
	pcl.mu.Lock()
	defer pcl.mu.Unlock()

	if collection == nil {
		return false
	}

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

	pcl.collections[id] = collection
	return true
}

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

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

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

// NeedsItem checks if any player collection or potential collection needs an item
func (pcl *PlayerCollectionList) NeedsItem(itemID int32, masterList *MasterCollectionList) bool {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

	// Check player's active collections first
	for _, collection := range pcl.collections {
		if collection.NeedsItem(itemID) {
			return true
		}
	}

	// Check if any master collection the player doesn't have needs this item
	if masterList != nil {
		for _, masterCollection := range masterList.GetAllCollections() {
			if masterCollection.NeedsItem(itemID) {
				// Player doesn't have this collection yet
				if _, hasCollection := pcl.collections[masterCollection.GetID()]; !hasCollection {
					return true
				}
			}
		}
	}

	return false
}

// HasCollectionsToHandIn checks if any collections are ready to turn in
func (pcl *PlayerCollectionList) HasCollectionsToHandIn() bool {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

	for _, collection := range pcl.collections {
		if collection.GetIsReadyToTurnIn() {
			return true
		}
	}

	return false
}

// GetCollectionsReadyToTurnIn returns collections that are ready to complete
func (pcl *PlayerCollectionList) GetCollectionsReadyToTurnIn() []*Collection {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

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

	return result
}

// GetCompletedCollections returns all completed collections
func (pcl *PlayerCollectionList) GetCompletedCollections() []*Collection {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

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

	return result
}

// GetActiveCollections returns all active (incomplete) collections
func (pcl *PlayerCollectionList) GetActiveCollections() []*Collection {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

	var result []*Collection
	for _, collection := range pcl.collections {
		if !collection.GetCompleted() {
			result = append(result, collection)
		}
	}

	return result
}

// GetAllCollections returns all player collections
func (pcl *PlayerCollectionList) GetAllCollections() []*Collection {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

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

	return result
}

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

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

	return result
}

// ProcessItemFound processes when a player finds an item that may belong to collections
func (pcl *PlayerCollectionList) ProcessItemFound(itemID int32, masterList *MasterCollectionList) ([]*Collection, error) {
	pcl.mu.Lock()
	defer pcl.mu.Unlock()

	var updatedCollections []*Collection

	// Check existing player collections
	for _, collection := range pcl.collections {
		if collection.NeedsItem(itemID) {
			if collection.MarkItemFound(itemID) {
				updatedCollections = append(updatedCollections, collection)
			}
		}
	}

	// Check if player should start new collections
	if masterList != nil {
		for _, masterCollection := range masterList.GetAllCollections() {
			// Skip if player already has this collection
			if _, hasCollection := pcl.collections[masterCollection.GetID()]; hasCollection {
				continue
			}

			// Check if master collection needs this item
			if masterCollection.NeedsItem(itemID) {
				// Create new collection for player
				newCollection := NewCollectionFromData(masterCollection)
				if newCollection != nil {
					newCollection.MarkItemFound(itemID)
					pcl.collections[masterCollection.GetID()] = newCollection
					updatedCollections = append(updatedCollections, newCollection)
				}
			}
		}
	}

	return updatedCollections, nil
}

// CompleteCollection marks a collection as completed
func (pcl *PlayerCollectionList) CompleteCollection(collectionID int32) error {
	pcl.mu.Lock()
	defer pcl.mu.Unlock()

	collection, exists := pcl.collections[collectionID]
	if !exists {
		return fmt.Errorf("collection %d not found", collectionID)
	}

	if collection.GetCompleted() {
		return fmt.Errorf("collection %d is already completed", collectionID)
	}

	if !collection.GetIsReadyToTurnIn() {
		return fmt.Errorf("collection %d is not ready to complete", collectionID)
	}

	collection.SetCompleted(true)
	collection.SetSaveNeeded(true)

	return nil
}

// GetCollectionsNeedingSave returns collections that need to be saved
func (pcl *PlayerCollectionList) GetCollectionsNeedingSave() []*Collection {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

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

	return result
}

// SaveCollections saves all collections that need saving
func (pcl *PlayerCollectionList) SaveCollections(ctx context.Context) error {
	collectionsToSave := pcl.GetCollectionsNeedingSave()
	if len(collectionsToSave) == 0 {
		return nil
	}

	if err := pcl.database.SavePlayerCollections(ctx, pcl.characterID, collectionsToSave); err != nil {
		return fmt.Errorf("failed to save player collections: %w", err)
	}

	// Mark collections as saved
	for _, collection := range collectionsToSave {
		collection.SetSaveNeeded(false)
	}

	return nil
}

// GetStatistics returns player collection statistics
func (pcl *PlayerCollectionList) GetStatistics() CollectionStatistics {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

	stats := CollectionStatistics{
		TotalCollections:       len(pcl.collections),
		PlayersWithCollections: 1, // This player
	}

	for _, collection := range pcl.collections {
		if collection.GetCompleted() {
			stats.CompletedCollections++
		}
		if !collection.GetCompleted() && collection.GetFoundItemsCount() > 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
}

// GetCollectionProgress returns detailed progress for all collections
func (pcl *PlayerCollectionList) GetCollectionProgress() []CollectionProgress {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

	progress := make([]CollectionProgress, 0, len(pcl.collections))
	for _, collection := range pcl.collections {
		progress = append(progress, collection.GetCollectionProgress())
	}

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

	return progress
}

// GetCharacterID returns the character ID for this collection list
func (pcl *PlayerCollectionList) GetCharacterID() int32 {
	return pcl.characterID
}

// RemoveCollection removes a collection from the player's list
func (pcl *PlayerCollectionList) RemoveCollection(collectionID int32) bool {
	pcl.mu.Lock()
	defer pcl.mu.Unlock()

	if _, exists := pcl.collections[collectionID]; exists {
		delete(pcl.collections, collectionID)
		return true
	}

	return false
}

// GetCollectionIDs returns all collection IDs the player has
func (pcl *PlayerCollectionList) GetCollectionIDs() []int32 {
	pcl.mu.RLock()
	defer pcl.mu.RUnlock()

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

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

	return ids
}
