package recipes

import (
	"fmt"
	"strings"
)

// NewRecipe creates a new recipe with default values
// Converted from C++ Recipe::Recipe constructor
func NewRecipe() *Recipe {
	return &Recipe{
		Components: make(map[int8][]int32),
		Products:   make(map[int8]*RecipeProducts),
	}
}

// NewRecipeFromRecipe creates a copy of another recipe
// Converted from C++ Recipe::Recipe(Recipe *in) copy constructor
func NewRecipeFromRecipe(source *Recipe) *Recipe {
	if source == nil {
		return NewRecipe()
	}

	source.mutex.RLock()
	defer source.mutex.RUnlock()

	recipe := &Recipe{
		// Core data
		ID:          source.ID,
		SoeID:       source.SoeID,
		BookID:      source.BookID,
		Name:        source.Name,
		Description: source.Description,
		BookName:    source.BookName,
		Book:        source.Book,
		Device:      source.Device,

		// Properties
		Level:         source.Level,
		Tier:          source.Tier,
		Icon:          source.Icon,
		Skill:         source.Skill,
		Technique:     source.Technique,
		Knowledge:     source.Knowledge,
		Classes:       source.Classes,
		DeviceSubType: source.DeviceSubType,

		// Unknown fields
		Unknown1: source.Unknown1,
		Unknown2: source.Unknown2,
		Unknown3: source.Unknown3,
		Unknown4: source.Unknown4,

		// Product information
		ProductItemID: source.ProductItemID,
		ProductName:   source.ProductName,
		ProductQty:    source.ProductQty,

		// Component titles
		PrimaryBuildCompTitle: source.PrimaryBuildCompTitle,
		Build1CompTitle:       source.Build1CompTitle,
		Build2CompTitle:       source.Build2CompTitle,
		Build3CompTitle:       source.Build3CompTitle,
		Build4CompTitle:       source.Build4CompTitle,
		FuelCompTitle:         source.FuelCompTitle,

		// Component quantities
		Build1CompQty:  source.Build1CompQty,
		Build2CompQty:  source.Build2CompQty,
		Build3CompQty:  source.Build3CompQty,
		Build4CompQty:  source.Build4CompQty,
		FuelCompQty:    source.FuelCompQty,
		PrimaryCompQty: source.PrimaryCompQty,

		// Stage information
		HighestStage: source.HighestStage,

		// Initialize maps
		Components: make(map[int8][]int32),
		Products:   make(map[int8]*RecipeProducts),
	}

	// Deep copy components
	for slot, components := range source.Components {
		recipe.Components[slot] = make([]int32, len(components))
		copy(recipe.Components[slot], components)
	}

	// Deep copy products
	for stage, products := range source.Products {
		if products != nil {
			recipe.Products[stage] = &RecipeProducts{
				ProductID:    products.ProductID,
				ByproductID:  products.ByproductID,
				ProductQty:   products.ProductQty,
				ByproductQty: products.ByproductQty,
			}
		}
	}

	return recipe
}

// AddBuildComponent adds a component to a specific slot for this recipe
// Converted from C++ Recipe::AddBuildComp
func (r *Recipe) AddBuildComponent(itemID int32, slot int8, preferred bool) {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	if slot < 0 || slot >= MaxSlots {
		return
	}

	// Initialize the slot if it doesn't exist
	if r.Components[slot] == nil {
		r.Components[slot] = make([]int32, 0)
	}

	// Check if the item is already in this slot
	for _, existingID := range r.Components[slot] {
		if existingID == itemID {
			return // Already exists
		}
	}

	// Add the component
	if preferred {
		// Add at the beginning for preferred components
		r.Components[slot] = append([]int32{itemID}, r.Components[slot]...)
	} else {
		r.Components[slot] = append(r.Components[slot], itemID)
	}
}

// GetTotalBuildComponents returns the total number of component slots used
// Converted from C++ Recipe::GetTotalBuildComponents
func (r *Recipe) GetTotalBuildComponents() int8 {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	count := int8(0)
	for slot := SlotBuild1; slot <= SlotBuild4; slot++ {
		if len(r.Components[slot]) > 0 {
			count++
		}
	}
	return count
}

// GetItemRequiredQuantity returns the required quantity for a specific item
// Converted from C++ Recipe::GetItemRequiredQuantity
func (r *Recipe) GetItemRequiredQuantity(itemID int32) int8 {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	// Check each slot for the item
	for slot, components := range r.Components {
		for _, componentID := range components {
			if componentID == itemID {
				// Return the quantity based on the slot
				switch slot {
				case SlotPrimary:
					return int8(r.PrimaryCompQty)
				case SlotBuild1:
					return int8(r.Build1CompQty)
				case SlotBuild2:
					return int8(r.Build2CompQty)
				case SlotBuild3:
					return int8(r.Build3CompQty)
				case SlotBuild4:
					return int8(r.Build4CompQty)
				case SlotFuel:
					return int8(r.FuelCompQty)
				}
			}
		}
	}

	return 0 // Not found
}

// CanUseRecipeByClass checks if a tradeskill class can use this recipe
// Converted from C++ Recipe::CanUseRecipeByClass (simplified)
func (r *Recipe) CanUseRecipeByClass(classID int8) bool {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	// Any can use: bit combination of 1+2 (adornments + artisan)
	if r.Classes < 4 {
		return true
	}

	// Check if the class bit is set
	return (1<<classID)&r.Classes != 0
}

// IsValid checks if the recipe has valid data
func (r *Recipe) IsValid() bool {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	if r.ID < MinRecipeID || r.ID > MaxRecipeID {
		return false
	}

	if strings.TrimSpace(r.Name) == "" {
		return false
	}

	if r.Level < MinRecipeLevel || r.Level > MaxRecipeLevel {
		return false
	}

	if r.Tier < MinTier || r.Tier > MaxTier {
		return false
	}

	return true
}

// GetComponentsBySlot returns the component items for a specific slot
func (r *Recipe) GetComponentsBySlot(slot int8) []int32 {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	if components, exists := r.Components[slot]; exists {
		// Return a copy to prevent external modification
		result := make([]int32, len(components))
		copy(result, components)
		return result
	}

	return nil
}

// GetProductsForStage returns the products for a specific crafting stage
func (r *Recipe) GetProductsForStage(stage int8) *RecipeProducts {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	if products, exists := r.Products[stage]; exists {
		// Return a copy to prevent external modification
		return &RecipeProducts{
			ProductID:    products.ProductID,
			ByproductID:  products.ByproductID,
			ProductQty:   products.ProductQty,
			ByproductQty: products.ByproductQty,
		}
	}

	return nil
}

// SetProductsForStage sets the products for a specific crafting stage
func (r *Recipe) SetProductsForStage(stage int8, products *RecipeProducts) {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	if stage < Stage0 || stage > Stage4 {
		return
	}

	if products == nil {
		delete(r.Products, stage)
		return
	}

	r.Products[stage] = &RecipeProducts{
		ProductID:    products.ProductID,
		ByproductID:  products.ByproductID,
		ProductQty:   products.ProductQty,
		ByproductQty: products.ByproductQty,
	}
}

// GetComponentTitleForSlot returns the component title for a specific slot
func (r *Recipe) GetComponentTitleForSlot(slot int8) string {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	switch slot {
	case SlotPrimary:
		return r.PrimaryBuildCompTitle
	case SlotBuild1:
		return r.Build1CompTitle
	case SlotBuild2:
		return r.Build2CompTitle
	case SlotBuild3:
		return r.Build3CompTitle
	case SlotBuild4:
		return r.Build4CompTitle
	case SlotFuel:
		return r.FuelCompTitle
	default:
		return ""
	}
}

// GetComponentQuantityForSlot returns the component quantity for a specific slot
func (r *Recipe) GetComponentQuantityForSlot(slot int8) int16 {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	switch slot {
	case SlotPrimary:
		return r.PrimaryCompQty
	case SlotBuild1:
		return r.Build1CompQty
	case SlotBuild2:
		return r.Build2CompQty
	case SlotBuild3:
		return r.Build3CompQty
	case SlotBuild4:
		return r.Build4CompQty
	case SlotFuel:
		return r.FuelCompQty
	default:
		return 0
	}
}

// GetInfo returns comprehensive information about the recipe
func (r *Recipe) GetInfo() map[string]interface{} {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	info := make(map[string]interface{})

	info["id"] = r.ID
	info["soe_id"] = r.SoeID
	info["book_id"] = r.BookID
	info["name"] = r.Name
	info["description"] = r.Description
	info["book_name"] = r.BookName
	info["book"] = r.Book
	info["device"] = r.Device
	info["level"] = r.Level
	info["tier"] = r.Tier
	info["icon"] = r.Icon
	info["skill"] = r.Skill
	info["technique"] = r.Technique
	info["knowledge"] = r.Knowledge
	info["classes"] = r.Classes
	info["product_id"] = r.ProductItemID
	info["product_name"] = r.ProductName
	info["product_qty"] = r.ProductQty
	info["highest_stage"] = r.HighestStage
	info["total_components"] = r.GetTotalBuildComponents()
	info["valid"] = r.IsValid()

	return info
}

// String returns a string representation of the recipe
func (r *Recipe) String() string {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	return fmt.Sprintf("Recipe{ID: %d, Name: %s, Level: %d, Tier: %d, Skill: %d}",
		r.ID, r.Name, r.Level, r.Tier, r.Skill)
}
