package recipes

import (
	"sync"
)

// MasterRecipeBookList manages all recipe books in the system
// Converted from C++ MasterRecipeBookList class
type MasterRecipeBookList struct {
	recipeBooks map[int32]*Recipe // Book ID -> Recipe (represents recipe book)
	mutex       sync.RWMutex
	stats       *Statistics
}

// NewMasterRecipeBookList creates a new master recipe book list
// Converted from C++ MasterRecipeBookList::MasterRecipeBookList constructor
func NewMasterRecipeBookList() *MasterRecipeBookList {
	return &MasterRecipeBookList{
		recipeBooks: make(map[int32]*Recipe),
		stats:       NewStatistics(),
	}
}

// AddRecipeBook adds a recipe book to the master list
// Converted from C++ MasterRecipeBookList::AddRecipeBook
func (mrbl *MasterRecipeBookList) AddRecipeBook(recipe *Recipe) bool {
	if recipe == nil || recipe.BookID <= 0 {
		return false
	}

	mrbl.mutex.Lock()
	defer mrbl.mutex.Unlock()

	// Check for duplicate book ID
	if _, exists := mrbl.recipeBooks[recipe.BookID]; exists {
		return false
	}

	mrbl.recipeBooks[recipe.BookID] = recipe
	mrbl.stats.TotalRecipeBooks++

	return true
}

// GetRecipeBook retrieves a recipe book by book ID
// Converted from C++ MasterRecipeBookList::GetRecipeBooks
func (mrbl *MasterRecipeBookList) GetRecipeBook(bookID int32) *Recipe {
	mrbl.mutex.RLock()
	defer mrbl.mutex.RUnlock()

	mrbl.stats.IncrementRecipeBookLookups()

	if recipe, exists := mrbl.recipeBooks[bookID]; exists {
		return recipe
	}

	return nil
}

// ClearRecipeBooks removes all recipe books from the master list
// Converted from C++ MasterRecipeBookList::ClearRecipeBooks
func (mrbl *MasterRecipeBookList) ClearRecipeBooks() {
	mrbl.mutex.Lock()
	defer mrbl.mutex.Unlock()

	mrbl.recipeBooks = make(map[int32]*Recipe)
	mrbl.stats.TotalRecipeBooks = 0
}

// Size returns the total number of recipe books
// Converted from C++ MasterRecipeBookList::Size
func (mrbl *MasterRecipeBookList) Size() int32 {
	mrbl.mutex.RLock()
	defer mrbl.mutex.RUnlock()

	return int32(len(mrbl.recipeBooks))
}

// GetAllRecipeBooks returns all recipe books
func (mrbl *MasterRecipeBookList) GetAllRecipeBooks() map[int32]*Recipe {
	mrbl.mutex.RLock()
	defer mrbl.mutex.RUnlock()

	// Return a copy to prevent external modification
	result := make(map[int32]*Recipe)
	for id, recipe := range mrbl.recipeBooks {
		result[id] = recipe
	}

	return result
}

// GetStatistics returns a snapshot of the current statistics
func (mrbl *MasterRecipeBookList) GetStatistics() Statistics {
	return mrbl.stats.GetSnapshot()
}

// PlayerRecipeList manages recipes for a specific player
// Converted from C++ PlayerRecipeList class
type PlayerRecipeList struct {
	recipes map[int32]*Recipe // Recipe ID -> Recipe
	mutex   sync.RWMutex
}

// NewPlayerRecipeList creates a new player recipe list
// Converted from C++ PlayerRecipeList::PlayerRecipeList constructor
func NewPlayerRecipeList() *PlayerRecipeList {
	return &PlayerRecipeList{
		recipes: make(map[int32]*Recipe),
	}
}

// AddRecipe adds a recipe to the player's list
// Converted from C++ PlayerRecipeList::AddRecipe
func (prl *PlayerRecipeList) AddRecipe(recipe *Recipe) bool {
	if recipe == nil || !recipe.IsValid() {
		return false
	}

	prl.mutex.Lock()
	defer prl.mutex.Unlock()

	// Check for duplicate ID
	if _, exists := prl.recipes[recipe.ID]; exists {
		return false
	}

	prl.recipes[recipe.ID] = recipe
	return true
}

// GetRecipe retrieves a recipe by ID from the player's list
// Converted from C++ PlayerRecipeList::GetRecipe
func (prl *PlayerRecipeList) GetRecipe(recipeID int32) *Recipe {
	prl.mutex.RLock()
	defer prl.mutex.RUnlock()

	if recipe, exists := prl.recipes[recipeID]; exists {
		return recipe
	}

	return nil
}

// RemoveRecipe removes a recipe from the player's list
// Converted from C++ PlayerRecipeList::RemoveRecipe
func (prl *PlayerRecipeList) RemoveRecipe(recipeID int32) bool {
	prl.mutex.Lock()
	defer prl.mutex.Unlock()

	if _, exists := prl.recipes[recipeID]; exists {
		delete(prl.recipes, recipeID)
		return true
	}

	return false
}

// ClearRecipes removes all recipes from the player's list
// Converted from C++ PlayerRecipeList::ClearRecipes
func (prl *PlayerRecipeList) ClearRecipes() {
	prl.mutex.Lock()
	defer prl.mutex.Unlock()

	prl.recipes = make(map[int32]*Recipe)
}

// Size returns the total number of recipes for this player
// Converted from C++ PlayerRecipeList::Size
func (prl *PlayerRecipeList) Size() int32 {
	prl.mutex.RLock()
	defer prl.mutex.RUnlock()

	return int32(len(prl.recipes))
}

// GetRecipes returns all recipes for this player
// Converted from C++ PlayerRecipeList::GetRecipes
func (prl *PlayerRecipeList) GetRecipes() map[int32]*Recipe {
	prl.mutex.RLock()
	defer prl.mutex.RUnlock()

	// Return a copy to prevent external modification
	result := make(map[int32]*Recipe)
	for id, recipe := range prl.recipes {
		result[id] = recipe
	}

	return result
}

// GetRecipesBySkill returns recipes for a specific skill
func (prl *PlayerRecipeList) GetRecipesBySkill(skillID int32) []*Recipe {
	prl.mutex.RLock()
	defer prl.mutex.RUnlock()

	var result []*Recipe
	for _, recipe := range prl.recipes {
		if recipe.Skill == skillID {
			result = append(result, recipe)
		}
	}

	return result
}

// GetRecipesByTier returns recipes for a specific tier
func (prl *PlayerRecipeList) GetRecipesByTier(tier int8) []*Recipe {
	prl.mutex.RLock()
	defer prl.mutex.RUnlock()

	var result []*Recipe
	for _, recipe := range prl.recipes {
		if recipe.Tier == tier {
			result = append(result, recipe)
		}
	}

	return result
}

// PlayerRecipeBookList manages recipe books for a specific player
// Converted from C++ PlayerRecipeBookList class
type PlayerRecipeBookList struct {
	recipeBooks map[int32]*Recipe // Book ID -> Recipe
	mutex       sync.RWMutex
}

// NewPlayerRecipeBookList creates a new player recipe book list
// Converted from C++ PlayerRecipeBookList::PlayerRecipeBookList constructor
func NewPlayerRecipeBookList() *PlayerRecipeBookList {
	return &PlayerRecipeBookList{
		recipeBooks: make(map[int32]*Recipe),
	}
}

// AddRecipeBook adds a recipe book to the player's list
// Converted from C++ PlayerRecipeBookList::AddRecipeBook
func (prbl *PlayerRecipeBookList) AddRecipeBook(recipe *Recipe) bool {
	if recipe == nil || recipe.BookID <= 0 {
		return false
	}

	prbl.mutex.Lock()
	defer prbl.mutex.Unlock()

	// Check for duplicate book ID
	if _, exists := prbl.recipeBooks[recipe.BookID]; exists {
		return false
	}

	prbl.recipeBooks[recipe.BookID] = recipe
	return true
}

// GetRecipeBook retrieves a recipe book by book ID from the player's list
// Converted from C++ PlayerRecipeBookList::GetRecipeBook
func (prbl *PlayerRecipeBookList) GetRecipeBook(bookID int32) *Recipe {
	prbl.mutex.RLock()
	defer prbl.mutex.RUnlock()

	if recipe, exists := prbl.recipeBooks[bookID]; exists {
		return recipe
	}

	return nil
}

// HasRecipeBook checks if the player has a specific recipe book
// Converted from C++ PlayerRecipeBookList::HasRecipeBook
func (prbl *PlayerRecipeBookList) HasRecipeBook(bookID int32) bool {
	prbl.mutex.RLock()
	defer prbl.mutex.RUnlock()

	_, exists := prbl.recipeBooks[bookID]
	return exists
}

// ClearRecipeBooks removes all recipe books from the player's list
// Converted from C++ PlayerRecipeBookList::ClearRecipeBooks
func (prbl *PlayerRecipeBookList) ClearRecipeBooks() {
	prbl.mutex.Lock()
	defer prbl.mutex.Unlock()

	prbl.recipeBooks = make(map[int32]*Recipe)
}

// Size returns the total number of recipe books for this player
func (prbl *PlayerRecipeBookList) Size() int32 {
	prbl.mutex.RLock()
	defer prbl.mutex.RUnlock()

	return int32(len(prbl.recipeBooks))
}

// GetRecipeBooks returns all recipe books for this player
// Converted from C++ PlayerRecipeBookList::GetRecipeBooks
func (prbl *PlayerRecipeBookList) GetRecipeBooks() map[int32]*Recipe {
	prbl.mutex.RLock()
	defer prbl.mutex.RUnlock()

	// Return a copy to prevent external modification
	result := make(map[int32]*Recipe)
	for id, recipe := range prbl.recipeBooks {
		result[id] = recipe
	}

	return result
}
