package ground_spawn

import (
	"fmt"
	"math/rand"
	"strings"

	"eq2emu/internal/spawn"
)

// NewGroundSpawn creates a new ground spawn instance
func NewGroundSpawn(config GroundSpawnConfig) *GroundSpawn {
	baseSpawn := spawn.NewSpawn()

	gs := &GroundSpawn{
		Spawn:                 baseSpawn,
		numberHarvests:        config.NumberHarvests,
		numAttemptsPerHarvest: config.AttemptsPerHarvest,
		groundspawnID:         config.GroundSpawnID,
		collectionSkill:       config.CollectionSkill,
		randomizeHeading:      config.RandomizeHeading,
	}

	// Configure base spawn properties
	gs.SetName(config.Name)
	gs.SetSpawnType(DefaultSpawnType)
	gs.SetDifficulty(DefaultDifficulty)
	gs.SetState(DefaultState)

	// Set position
	gs.SetX(config.Location.X)
	gs.SetY(config.Location.Y)
	gs.SetZ(config.Location.Z)

	if config.RandomizeHeading {
		gs.SetHeading(rand.Float32() * 360.0)
	} else {
		gs.SetHeading(config.Location.Heading)
	}

	return gs
}

// Copy creates a deep copy of the ground spawn
func (gs *GroundSpawn) Copy() *GroundSpawn {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	newSpawn := &GroundSpawn{
		Spawn:                 gs.Spawn.Copy().(*spawn.Spawn),
		numberHarvests:        gs.numberHarvests,
		numAttemptsPerHarvest: gs.numAttemptsPerHarvest,
		groundspawnID:         gs.groundspawnID,
		collectionSkill:       gs.collectionSkill,
		randomizeHeading:      gs.randomizeHeading,
	}

	return newSpawn
}

// IsGroundSpawn returns true (implements spawn interface)
func (gs *GroundSpawn) IsGroundSpawn() bool {
	return true
}

// GetNumberHarvests returns the number of harvests remaining
func (gs *GroundSpawn) GetNumberHarvests() int8 {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	return gs.numberHarvests
}

// SetNumberHarvests sets the number of harvests remaining
func (gs *GroundSpawn) SetNumberHarvests(val int8) {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	gs.numberHarvests = val
}

// GetAttemptsPerHarvest returns attempts per harvest session
func (gs *GroundSpawn) GetAttemptsPerHarvest() int8 {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	return gs.numAttemptsPerHarvest
}

// SetAttemptsPerHarvest sets attempts per harvest session
func (gs *GroundSpawn) SetAttemptsPerHarvest(val int8) {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	gs.numAttemptsPerHarvest = val
}

// GetGroundSpawnEntryID returns the database entry ID
func (gs *GroundSpawn) GetGroundSpawnEntryID() int32 {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	return gs.groundspawnID
}

// SetGroundSpawnEntryID sets the database entry ID
func (gs *GroundSpawn) SetGroundSpawnEntryID(val int32) {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	gs.groundspawnID = val
}

// GetCollectionSkill returns the required harvesting skill
func (gs *GroundSpawn) GetCollectionSkill() string {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	return gs.collectionSkill
}

// SetCollectionSkill sets the required harvesting skill
func (gs *GroundSpawn) SetCollectionSkill(skill string) {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	gs.collectionSkill = skill
}

// GetRandomizeHeading returns whether heading should be randomized
func (gs *GroundSpawn) GetRandomizeHeading() bool {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	return gs.randomizeHeading
}

// SetRandomizeHeading sets whether heading should be randomized
func (gs *GroundSpawn) SetRandomizeHeading(val bool) {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	gs.randomizeHeading = val
}

// IsDepleted returns true if the ground spawn has no harvests remaining
func (gs *GroundSpawn) IsDepleted() bool {
	return gs.GetNumberHarvests() <= 0
}

// IsAvailable returns true if the ground spawn can be harvested
func (gs *GroundSpawn) IsAvailable() bool {
	return gs.GetNumberHarvests() > 0 && gs.IsAlive()
}

// GetHarvestMessageName returns the appropriate harvest verb based on skill
func (gs *GroundSpawn) GetHarvestMessageName(presentTense bool, failure bool) string {
	skill := strings.ToLower(gs.GetCollectionSkill())

	switch skill {
	case "gathering", "collecting":
		if presentTense {
			return "gather"
		}
		return "gathered"

	case "mining":
		if presentTense {
			return "mine"
		}
		return "mined"

	case "fishing":
		if presentTense {
			return "fish"
		}
		return "fished"

	case "trapping":
		if failure {
			return "trap"
		}
		if presentTense {
			return "acquire"
		}
		return "acquired"

	case "foresting":
		if presentTense {
			return "forest"
		}
		return "forested"

	default:
		if presentTense {
			return "collect"
		}
		return "collected"
	}
}

// GetHarvestSpellType returns the spell type for harvesting
func (gs *GroundSpawn) GetHarvestSpellType() string {
	skill := strings.ToLower(gs.GetCollectionSkill())

	switch skill {
	case "gathering", "collecting":
		return SpellTypeGather
	case "mining":
		return SpellTypeMine
	case "trapping":
		return SpellTypeTrap
	case "foresting":
		return SpellTypeChop
	case "fishing":
		return SpellTypeFish
	default:
		return SpellTypeGather
	}
}

// GetHarvestSpellName returns the spell name for harvesting
func (gs *GroundSpawn) GetHarvestSpellName() string {
	skill := gs.GetCollectionSkill()

	if skill == SkillCollecting {
		return SkillGathering
	}

	return skill
}

// ProcessHarvest handles the complex harvesting logic
func (gs *GroundSpawn) ProcessHarvest(context *HarvestContext) (*HarvestResult, error) {
	if context == nil {
		return nil, fmt.Errorf("harvest context cannot be nil")
	}

	if context.Player == nil {
		return nil, fmt.Errorf("player cannot be nil")
	}

	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	// Check if ground spawn is depleted
	if gs.numberHarvests <= 0 {
		return &HarvestResult{
			Success:     false,
			MessageText: "This spawn has nothing more to harvest!",
		}, nil
	}

	// Validate harvest data
	if context.GroundSpawnEntries == nil || len(context.GroundSpawnEntries) == 0 {
		return &HarvestResult{
			Success:     false,
			MessageText: fmt.Sprintf("Error: No groundspawn entries assigned to groundspawn id: %d", gs.groundspawnID),
		}, nil
	}

	if context.GroundSpawnItems == nil || len(context.GroundSpawnItems) == 0 {
		return &HarvestResult{
			Success:     false,
			MessageText: fmt.Sprintf("Error: No groundspawn items assigned to groundspawn id: %d", gs.groundspawnID),
		}, nil
	}

	// Validate player skill
	if context.PlayerSkill == nil {
		return &HarvestResult{
			Success:     false,
			MessageText: fmt.Sprintf("Error: You do not have the '%s' skill!", gs.collectionSkill),
		}, nil
	}

	result := &HarvestResult{
		Success:      true,
		ItemsAwarded: make([]*HarvestedItem, 0),
	}

	// Process each harvest attempt
	for attempt := int8(0); attempt < gs.numAttemptsPerHarvest; attempt++ {
		attemptResult := gs.processHarvestAttempt(context)
		if attemptResult != nil {
			result.ItemsAwarded = append(result.ItemsAwarded, attemptResult.ItemsAwarded...)
			if attemptResult.SkillGained {
				result.SkillGained = true
			}
		}
	}

	// Decrement harvest count
	gs.numberHarvests--

	return result, nil
}

// processHarvestAttempt handles a single harvest attempt
func (gs *GroundSpawn) processHarvestAttempt(context *HarvestContext) *HarvestResult {
	// Filter available harvest tables based on player skill and level
	availableTables := gs.filterHarvestTables(context)
	if len(availableTables) == 0 {
		return &HarvestResult{
			Success:     false,
			MessageText: "You lack the skills to harvest this node!",
		}
	}

	// Select harvest table based on skill roll
	selectedTable := gs.selectHarvestTable(availableTables, context.TotalSkill)
	if selectedTable == nil {
		return &HarvestResult{
			Success:     false,
			MessageText: "Failed to determine harvest table",
		}
	}

	// Determine harvest type based on table percentages
	harvestType := gs.determineHarvestType(selectedTable, context.IsCollection)
	if harvestType == HarvestTypeNone {
		return &HarvestResult{
			Success: false,
			MessageText: fmt.Sprintf("You failed to %s anything from %s.",
				gs.GetHarvestMessageName(true, true), gs.GetName()),
		}
	}

	// Award items based on harvest type
	items := gs.awardHarvestItems(harvestType, context.GroundSpawnItems, context.Player)

	// Handle skill progression
	skillGained := gs.handleSkillProgression(context, selectedTable)

	return &HarvestResult{
		Success:      len(items) > 0,
		HarvestType:  harvestType,
		ItemsAwarded: items,
		SkillGained:  skillGained,
	}
}

// filterHarvestTables filters tables based on player capabilities
func (gs *GroundSpawn) filterHarvestTables(context *HarvestContext) []*GroundSpawnEntry {
	var filtered []*GroundSpawnEntry

	for _, entry := range context.GroundSpawnEntries {
		// Check skill requirement
		if entry.MinSkillLevel > context.TotalSkill {
			continue
		}

		// Check level requirement for bonus tables
		if entry.BonusTable && context.Player.GetLevel() < entry.MinAdventureLevel {
			continue
		}

		filtered = append(filtered, entry)
	}

	return filtered
}

// selectHarvestTable selects a harvest table based on skill level
func (gs *GroundSpawn) selectHarvestTable(tables []*GroundSpawnEntry, totalSkill int16) *GroundSpawnEntry {
	if len(tables) == 0 {
		return nil
	}

	// Find lowest skill requirement
	lowestSkill := int16(32767)
	for _, table := range tables {
		if table.MinSkillLevel < lowestSkill {
			lowestSkill = table.MinSkillLevel
		}
	}

	// Roll for table selection
	tableChoice := int16(rand.Intn(int(totalSkill-lowestSkill+1))) + lowestSkill

	// Find best matching table
	var bestTable *GroundSpawnEntry
	bestScore := int16(0)

	for _, table := range tables {
		if tableChoice >= table.MinSkillLevel && table.MinSkillLevel > bestScore {
			bestTable = table
			bestScore = table.MinSkillLevel
		}
	}

	// If multiple tables match, pick randomly
	var matches []*GroundSpawnEntry
	for _, table := range tables {
		if table.MinSkillLevel == bestScore {
			matches = append(matches, table)
		}
	}

	if len(matches) > 1 {
		return matches[rand.Intn(len(matches))]
	}

	return bestTable
}

// determineHarvestType determines what type of harvest occurs
func (gs *GroundSpawn) determineHarvestType(table *GroundSpawnEntry, isCollection bool) int8 {
	chance := rand.Float32() * 100.0

	// Collection items always get 1 item
	if isCollection {
		return HarvestType1Item
	}

	// Check harvest types in order of rarity (most rare first)
	if chance <= table.Harvest10 {
		return HarvestType10AndRare
	}
	if chance <= table.HarvestRare {
		return HarvestTypeRare
	}
	if chance <= table.HarvestImbue {
		return HarvestTypeImbue
	}
	if chance <= table.Harvest5 {
		return HarvestType5Items
	}
	if chance <= table.Harvest3 {
		return HarvestType3Items
	}
	if chance <= table.Harvest1 {
		return HarvestType1Item
	}

	return HarvestTypeNone
}

// awardHarvestItems awards items based on harvest type
func (gs *GroundSpawn) awardHarvestItems(harvestType int8, availableItems []*GroundSpawnEntryItem, player *Player) []*HarvestedItem {
	var items []*HarvestedItem

	// Filter items based on harvest type and player location
	normalItems := gs.filterItems(availableItems, ItemRarityNormal, player.GetLocation())
	rareItems := gs.filterItems(availableItems, ItemRarityRare, player.GetLocation())
	imbueItems := gs.filterItems(availableItems, ItemRarityImbue, player.GetLocation())

	switch harvestType {
	case HarvestType1Item:
		items = gs.selectRandomItems(normalItems, 1)
	case HarvestType3Items:
		items = gs.selectRandomItems(normalItems, 3)
	case HarvestType5Items:
		items = gs.selectRandomItems(normalItems, 5)
	case HarvestTypeImbue:
		items = gs.selectRandomItems(imbueItems, 1)
	case HarvestTypeRare:
		items = gs.selectRandomItems(rareItems, 1)
	case HarvestType10AndRare:
		normal := gs.selectRandomItems(normalItems, 10)
		rare := gs.selectRandomItems(rareItems, 1)
		items = append(normal, rare...)
	}

	return items
}

// filterItems filters items by rarity and grid restriction
func (gs *GroundSpawn) filterItems(items []*GroundSpawnEntryItem, rarity int8, playerGrid int32) []*GroundSpawnEntryItem {
	var filtered []*GroundSpawnEntryItem

	for _, item := range items {
		if item.IsRare != rarity {
			continue
		}

		// Check grid restriction
		if item.GridID != 0 && item.GridID != playerGrid {
			continue
		}

		filtered = append(filtered, item)
	}

	return filtered
}

// selectRandomItems randomly selects items from available list
func (gs *GroundSpawn) selectRandomItems(items []*GroundSpawnEntryItem, quantity int16) []*HarvestedItem {
	if len(items) == 0 {
		return nil
	}

	var result []*HarvestedItem

	for i := int16(0); i < quantity; i++ {
		selectedItem := items[rand.Intn(len(items))]

		harvestedItem := &HarvestedItem{
			ItemID:   selectedItem.ItemID,
			Quantity: selectedItem.Quantity,
			IsRare:   selectedItem.IsRare == ItemRarityRare,
			Name:     fmt.Sprintf("Item_%d", selectedItem.ItemID), // Placeholder
		}

		result = append(result, harvestedItem)
	}

	return result
}

// handleSkillProgression manages skill increases from harvesting
func (gs *GroundSpawn) handleSkillProgression(context *HarvestContext, table *GroundSpawnEntry) bool {
	if context.IsCollection {
		return false // Collections don't give skill
	}

	if context.PlayerSkill == nil {
		return false
	}

	// Check if player skill is already at max for this node
	maxSkillAllowed := int16(float32(context.MaxSkillRequired) * 1.0) // TODO: Use skill multiplier rule

	if context.PlayerSkill.GetCurrentValue() >= maxSkillAllowed {
		return false
	}

	// Award skill increase (placeholder implementation)
	// TODO: Integrate with actual skill system when available
	return true
}

// HandleUse processes player interaction with the ground spawn
func (gs *GroundSpawn) HandleUse(client Client, useType string) error {
	if client == nil {
		return fmt.Errorf("client cannot be nil")
	}

	gs.harvestUseMutex.Lock()
	defer gs.harvestUseMutex.Unlock()

	// Check spawn access requirements
	if !gs.MeetsSpawnAccessRequirements(client.GetPlayer()) {
		return nil // Silently ignore if requirements not met
	}

	// Normalize use type
	useType = strings.ToLower(strings.TrimSpace(useType))

	// Handle older clients that don't send use type
	if client.GetVersion() <= 561 && useType == "" {
		useType = gs.GetHarvestSpellType()
	}

	// Check if this is a harvest action
	expectedSpellType := gs.GetHarvestSpellType()
	if useType == expectedSpellType {
		return gs.handleHarvestUse(client)
	}

	// Handle other command interactions
	if gs.HasCommandIcon() {
		return gs.handleCommandUse(client, useType)
	}

	return nil
}

// handleHarvestUse processes harvest-specific use
func (gs *GroundSpawn) handleHarvestUse(client Client) error {
	spellName := gs.GetHarvestSpellName()

	// TODO: Integrate with spell system when available
	// spell := masterSpellList.GetSpellByName(spellName)
	// if spell != nil {
	//     zone.ProcessSpell(spell, player, target, true, true)
	// }

	if client.GetLogger() != nil {
		client.GetLogger().LogDebug("Player %s attempting to harvest %s using spell %s",
			client.GetPlayer().GetName(), gs.GetName(), spellName)
	}

	return nil
}

// handleCommandUse processes command-specific use
func (gs *GroundSpawn) handleCommandUse(client Client, command string) error {
	// TODO: Integrate with entity command system when available
	// entityCommand := gs.FindEntityCommand(command)
	// if entityCommand != nil {
	//     zone.ProcessEntityCommand(entityCommand, player, target)
	// }

	if client.GetLogger() != nil {
		client.GetLogger().LogDebug("Player %s using command %s on %s",
			client.GetPlayer().GetName(), command, gs.GetName())
	}

	return nil
}

// Serialize creates a packet representation of the ground spawn
func (gs *GroundSpawn) Serialize(player *Player, version int16) ([]byte, error) {
	// Use base spawn serialization
	return gs.Spawn.Serialize(player, version)
}

// Respawn resets the ground spawn to harvestable state
func (gs *GroundSpawn) Respawn() {
	gs.harvestMutex.Lock()
	defer gs.harvestMutex.Unlock()

	// Reset harvest count to default
	gs.numberHarvests = DefaultNumberHarvests

	// Randomize heading if configured
	if gs.randomizeHeading {
		gs.SetHeading(rand.Float32() * 360.0)
	}

	// Mark as alive
	gs.SetAlive(true)
}
