package tradeskills

import (
	"fmt"
	"log"
	"math/rand"
	"time"
)

// NewTradeskillManager creates a new tradeskill manager with default configuration.
func NewTradeskillManager() *TradeskillManager {
	return &TradeskillManager{
		tradeskillList:    make(map[uint32]*Tradeskill),
		critFailChance:    DefaultCritFailChance,
		critSuccessChance: DefaultCritSuccessChance,
		failChance:        DefaultFailChance,
		successChance:     DefaultSuccessChance,
		eventChance:       DefaultEventChance,
		stats: TradeskillManagerStats{
			LastUpdate: time.Now(),
		},
	}
}

// Process handles periodic updates for all active tradeskill sessions.
// This should be called regularly (typically every 50ms) by the server.
func (tm *TradeskillManager) Process() {
	tm.mutex.Lock()
	defer tm.mutex.Unlock()

	currentTime := time.Now()

	// Process each active tradeskill session
	for playerID, tradeskill := range tm.tradeskillList {
		if tradeskill == nil {
			continue
		}

		// Check if this tradeskill needs an update
		if !tradeskill.NeedsUpdate() {
			continue
		}

		outcome := tm.processCraftingUpdate(tradeskill)

		// TODO: Send update packets to client
		// This would need integration with the packet system
		log.Printf("Crafting update for player %d: Progress=%d, Durability=%d, Success=%v",
			playerID, tradeskill.CurrentProgress, tradeskill.CurrentDurability, outcome.Success)

		// Check if crafting is complete or failed
		if outcome.Completed {
			tm.completeCrafting(playerID, tradeskill, true)
		} else if outcome.Failed {
			tm.completeCrafting(playerID, tradeskill, false)
		} else {
			// Schedule next update
			tradeskill.NextUpdateTime = currentTime.Add(CraftingUpdateInterval)
			tradeskill.LastUpdate = currentTime
		}
	}

	// Update statistics
	tm.stats.LastUpdate = currentTime
}

// processCraftingUpdate calculates the outcome of a single crafting update.
func (tm *TradeskillManager) processCraftingUpdate(ts *Tradeskill) CraftingOutcome {
	outcome := CraftingOutcome{}

	// Roll for outcome type based on configured chances
	roll := rand.Float32() * 100.0

	var progressChange, durabilityChange int32

	// Determine base outcome
	if roll <= tm.critFailChance {
		// Critical failure
		progressChange = -50
		durabilityChange = -100
		outcome.CriticalFailure = true
		log.Printf("Critical failure for crafting session")
	} else if roll <= tm.critFailChance+tm.critSuccessChance {
		// Critical success
		progressChange = 100
		durabilityChange = 10
		outcome.CriticalSuccess = true
		outcome.Success = true
		log.Printf("Critical success for crafting session")
	} else if roll <= tm.critFailChance+tm.critSuccessChance+tm.failChance {
		// Regular failure
		progressChange = 0
		durabilityChange = -50
		outcome.Success = false
	} else {
		// Regular success
		progressChange = 50
		durabilityChange = -10
		outcome.Success = true
	}

	// Apply event effects if there's an active event
	if ts.CurrentEvent != nil {
		if ts.EventCountered {
			progressChange += int32(ts.CurrentEvent.SuccessProgress)
			durabilityChange += int32(ts.CurrentEvent.SuccessDurability)
		} else {
			progressChange += int32(ts.CurrentEvent.FailProgress)
			durabilityChange += int32(ts.CurrentEvent.FailDurability)
		}
	}

	// Apply changes
	ts.CurrentProgress += progressChange
	ts.CurrentDurability += durabilityChange

	// Clamp values to valid ranges
	if ts.CurrentProgress < MinProgress {
		ts.CurrentProgress = MinProgress
	} else if ts.CurrentProgress > MaxProgress {
		ts.CurrentProgress = MaxProgress
	}

	if ts.CurrentDurability < MinDurability {
		ts.CurrentDurability = MinDurability
	} else if ts.CurrentDurability > MaxDurability {
		ts.CurrentDurability = MaxDurability
	}

	outcome.ProgressChange = progressChange
	outcome.DurabilityChange = durabilityChange
	outcome.Completed = ts.IsComplete()
	outcome.Failed = ts.IsFailed()

	// Reset event state
	ts.CurrentEvent = nil
	ts.EventChecked = false
	ts.EventCountered = false

	// Roll for new event
	eventRoll := rand.Float32() * 100.0
	if eventRoll <= tm.eventChance {
		// TODO: Select random event from master list based on technique
		// This would need integration with the master events list
		tm.stats.TotalEventsTriggered++
	}

	return outcome
}

// BeginCrafting starts a new crafting session for a player.
func (tm *TradeskillManager) BeginCrafting(request CraftingRequest) error {
	tm.mutex.Lock()
	defer tm.mutex.Unlock()

	// Check if player is already crafting
	if _, exists := tm.tradeskillList[request.PlayerID]; exists {
		return fmt.Errorf("player %d is already crafting", request.PlayerID)
	}

	// Validate request
	if request.RecipeID == 0 {
		return fmt.Errorf("invalid recipe ID")
	}

	if len(request.Components) == 0 {
		return fmt.Errorf("no components provided")
	}

	// TODO: Validate recipe exists and player has it
	// TODO: Validate components are available in player inventory
	// TODO: Validate crafting table is correct for recipe

	// Create new tradeskill session
	now := time.Now()
	tradeskill := &Tradeskill{
		PlayerID:          request.PlayerID,
		TableSpawnID:      request.TableSpawnID,
		RecipeID:          request.RecipeID,
		CurrentProgress:   MinProgress,
		CurrentDurability: MaxDurability,
		NextUpdateTime:    now.Add(500 * time.Millisecond), // Initial delay before first update
		UsedComponents:    request.Components,
		StartTime:         now,
		LastUpdate:        now,
	}

	// Add to active sessions
	tm.tradeskillList[request.PlayerID] = tradeskill
	tm.stats.ActiveSessions++
	tm.stats.TotalSessionsStarted++

	// TODO: Send crafting UI packet to client
	// TODO: Lock inventory items being used
	// TODO: Unlock tradeskill spells

	log.Printf("Started crafting session for player %d with recipe %d", request.PlayerID, request.RecipeID)
	return nil
}

// StopCrafting ends a crafting session for a player.
func (tm *TradeskillManager) StopCrafting(playerID uint32) error {
	tm.mutex.Lock()
	defer tm.mutex.Unlock()

	tradeskill, exists := tm.tradeskillList[playerID]
	if !exists {
		return fmt.Errorf("player %d is not crafting", playerID)
	}

	// Determine completion status
	completed := tradeskill.IsComplete()

	return tm.completeCrafting(playerID, tradeskill, completed)
}

// completeCrafting handles the completion of a crafting session.
func (tm *TradeskillManager) completeCrafting(playerID uint32, ts *Tradeskill, success bool) error {
	// TODO: Calculate rewards based on progress/durability
	// TODO: Give items to player based on completion stage
	// TODO: Award tradeskill experience
	// TODO: Unlock inventory items
	// TODO: Lock tradeskill spells
	// TODO: Send stop crafting packet

	log.Printf("Completed crafting session for player %d: success=%v, progress=%d, durability=%d",
		playerID, success, ts.CurrentProgress, ts.CurrentDurability)

	// Remove from active sessions
	delete(tm.tradeskillList, playerID)
	tm.stats.ActiveSessions--

	if success {
		tm.stats.TotalSessionsCompleted++
	} else {
		tm.stats.TotalSessionsCancelled++
	}

	return nil
}

// IsClientCrafting checks if a player is currently crafting.
func (tm *TradeskillManager) IsClientCrafting(playerID uint32) bool {
	tm.mutex.RLock()
	defer tm.mutex.RUnlock()

	_, exists := tm.tradeskillList[playerID]
	return exists
}

// GetTradeskill returns the tradeskill session for a player.
func (tm *TradeskillManager) GetTradeskill(playerID uint32) *Tradeskill {
	tm.mutex.RLock()
	defer tm.mutex.RUnlock()

	return tm.tradeskillList[playerID]
}

// CheckTradeskillEvent processes a player's attempt to counter a tradeskill event.
func (tm *TradeskillManager) CheckTradeskillEvent(request EventCounterRequest) error {
	tm.mutex.Lock()
	defer tm.mutex.Unlock()

	tradeskill, exists := tm.tradeskillList[request.PlayerID]
	if !exists {
		return fmt.Errorf("player %d is not crafting", request.PlayerID)
	}

	// Check if there's an active event that hasn't been checked yet
	if tradeskill.CurrentEvent == nil || tradeskill.EventChecked {
		return fmt.Errorf("no active event to counter")
	}

	// Mark event as checked
	tradeskill.EventChecked = true

	// Check if the counter was successful (icon matches)
	countered := request.SpellIcon == tradeskill.CurrentEvent.Icon
	tradeskill.EventCountered = countered

	if countered {
		tm.stats.TotalEventsCountered++
	}

	// TODO: Send counter reaction packet to client

	log.Printf("Player %d %s event %s", request.PlayerID,
		map[bool]string{true: "countered", false: "failed to counter"}[countered],
		tradeskill.CurrentEvent.Name)

	return nil
}

// GetStats returns current statistics for the tradeskill manager.
func (tm *TradeskillManager) GetStats() TradeskillStats {
	tm.mutex.RLock()
	defer tm.mutex.RUnlock()

	return TradeskillStats{
		ActiveSessions:     tm.stats.ActiveSessions,
		RecentCompletions:  tm.stats.TotalSessionsCompleted, // TODO: Track hourly completions
		AverageSessionTime: time.Minute * 5,                 // TODO: Calculate actual average
	}
}

// GetTechniqueSuccessAnim returns the success animation for a technique and client version.
func (tm *TradeskillManager) GetTechniqueSuccessAnim(clientVersion int16, technique uint32) uint32 {
	switch technique {
	case TechniqueSkillTransmuting: // Sculpting
		if clientVersion <= 561 {
			return 3007 // leatherworking_success
		}
		return 11785

	case TechniqueSkillArtistry:
		if clientVersion <= 561 {
			return 2319 // cooking_success
		}
		return 11245

	case TechniqueSkillFletching:
		if clientVersion <= 561 {
			return 2356 // woodworking_success
		}
		return 13309

	case TechniqueSkillMetalworking, TechniqueSkillMetalshaping:
		if clientVersion <= 561 {
			return 2442 // metalworking_success
		}
		return 11813

	case TechniqueSkillTailoring:
		if clientVersion <= 561 {
			return 2352 // tailoring_success
		}
		return 13040

	case TechniqueSkillAlchemy:
		if clientVersion <= 561 {
			return 2298 // alchemy_success
		}
		return 10749

	case TechniqueSkillJewelcrafting:
		if clientVersion <= 561 {
			return 2304 // artificing_success
		}
		return 10767

	case TechniqueSkillScribing:
		// No known animations for scribing
		return 0
	}

	return 0
}

// GetTechniqueFailureAnim returns the failure animation for a technique and client version.
func (tm *TradeskillManager) GetTechniqueFailureAnim(clientVersion int16, technique uint32) uint32 {
	switch technique {
	case TechniqueSkillTransmuting: // Sculpting
		if clientVersion <= 561 {
			return 3005 // leatherworking_failure
		}
		return 11783

	case TechniqueSkillArtistry:
		if clientVersion <= 561 {
			return 2317 // cooking_failure
		}
		return 11243

	case TechniqueSkillFletching:
		if clientVersion <= 561 {
			return 2354 // woodworking_failure
		}
		return 13307

	case TechniqueSkillMetalworking, TechniqueSkillMetalshaping:
		if clientVersion <= 561 {
			return 2441 // metalworking_failure
		}
		return 11811

	case TechniqueSkillTailoring:
		if clientVersion <= 561 {
			return 2350 // tailoring_failure
		}
		return 13038

	case TechniqueSkillAlchemy:
		if clientVersion <= 561 {
			return 2298 // alchemy_failure (same as success in C++ - typo?)
		}
		return 10749

	case TechniqueSkillJewelcrafting:
		if clientVersion <= 561 {
			return 2302 // artificing_failure
		}
		return 10765

	case TechniqueSkillScribing:
		// No known animations for scribing
		return 0
	}

	return 0
}

// GetTechniqueIdleAnim returns the idle animation for a technique and client version.
func (tm *TradeskillManager) GetTechniqueIdleAnim(clientVersion int16, technique uint32) uint32 {
	switch technique {
	case TechniqueSkillTransmuting: // Sculpting
		if clientVersion <= 561 {
			return 3006 // leatherworking_idle
		}
		return 11784

	case TechniqueSkillArtistry:
		if clientVersion <= 561 {
			return 2318 // cooking_idle
		}
		return 11244

	case TechniqueSkillFletching:
		if clientVersion <= 561 {
			return 2355 // woodworking_idle
		}
		return 13308

	case TechniqueSkillMetalworking, TechniqueSkillMetalshaping:
		if clientVersion <= 561 {
			return 1810 // metalworking_idle
		}
		return 11812

	case TechniqueSkillTailoring:
		if clientVersion <= 561 {
			return 2351 // tailoring_idle
		}
		return 13039

	case TechniqueSkillAlchemy:
		if clientVersion <= 561 {
			return 2297 // alchemy_idle
		}
		return 10748

	case TechniqueSkillJewelcrafting:
		if clientVersion <= 561 {
			return 2303 // artificing_idle
		}
		return 10766

	case TechniqueSkillScribing:
		if clientVersion <= 561 {
			return 3131 // scribing_idle
		}
		return 12193
	}

	return 0
}

// GetMissTargetAnim returns the miss target animation for client version.
func (tm *TradeskillManager) GetMissTargetAnim(clientVersion int16) uint32 {
	if clientVersion <= 561 {
		return 1144
	}
	return 11814
}

// GetKillMissTargetAnim returns the kill miss target animation for client version.
func (tm *TradeskillManager) GetKillMissTargetAnim(clientVersion int16) uint32 {
	if clientVersion <= 561 {
		return 33912
	}
	return 44582
}

// UpdateConfiguration updates the manager's configuration from rules.
func (tm *TradeskillManager) UpdateConfiguration(critFail, critSuccess, fail, success, eventChance float32) error {
	tm.mutex.Lock()
	defer tm.mutex.Unlock()

	// Validate that chances add up to 100% (excluding event chance)
	total := critFail + critSuccess + fail + success
	if total != 100.0 {
		log.Printf("Warning: Tradeskill chances don't add up to 100%% (got %.1f%%), using defaults", total)
		tm.critFailChance = DefaultCritFailChance
		tm.critSuccessChance = DefaultCritSuccessChance
		tm.failChance = DefaultFailChance
		tm.successChance = DefaultSuccessChance
	} else {
		tm.critFailChance = critFail / 100.0 // Convert to 0-1 range
		tm.critSuccessChance = critSuccess / 100.0
		tm.failChance = fail / 100.0
		tm.successChance = success / 100.0
	}

	tm.eventChance = eventChance

	return nil
}

// NewMasterTradeskillEventsList creates a new master events list.
func NewMasterTradeskillEventsList() *MasterTradeskillEventsList {
	return &MasterTradeskillEventsList{
		eventList:   make(map[uint32][]*TradeskillEvent),
		totalEvents: 0,
	}
}

// AddEvent adds a tradeskill event to the master list.
func (mtel *MasterTradeskillEventsList) AddEvent(event *TradeskillEvent) {
	if event == nil {
		return
	}

	mtel.mutex.Lock()
	defer mtel.mutex.Unlock()

	mtel.eventList[event.Technique] = append(mtel.eventList[event.Technique], event)
	mtel.totalEvents++
}

// GetEventByTechnique returns all events for a given technique.
func (mtel *MasterTradeskillEventsList) GetEventByTechnique(technique uint32) []*TradeskillEvent {
	mtel.mutex.RLock()
	defer mtel.mutex.RUnlock()

	events, exists := mtel.eventList[technique]
	if !exists {
		return nil
	}

	// Return a copy to avoid race conditions
	result := make([]*TradeskillEvent, len(events))
	copy(result, events)
	return result
}

// Size returns the total number of events in the master list.
func (mtel *MasterTradeskillEventsList) Size() int32 {
	mtel.mutex.RLock()
	defer mtel.mutex.RUnlock()

	return mtel.totalEvents
}

// GetStats returns statistics about the events list.
func (mtel *MasterTradeskillEventsList) GetStats() TradeskillStats {
	mtel.mutex.RLock()
	defer mtel.mutex.RUnlock()

	eventsByTechnique := make(map[uint32]int32)
	for technique, events := range mtel.eventList {
		eventsByTechnique[technique] = int32(len(events))
	}

	return TradeskillStats{
		TotalEvents:       mtel.totalEvents,
		EventsByTechnique: eventsByTechnique,
	}
}

// Clear removes all events from the master list.
func (mtel *MasterTradeskillEventsList) Clear() {
	mtel.mutex.Lock()
	defer mtel.mutex.Unlock()

	mtel.eventList = make(map[uint32][]*TradeskillEvent)
	mtel.totalEvents = 0
}
