package quests

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

// RegisterQuest sets the basic quest information
func (q *Quest) RegisterQuest(name, questType, zone string, level int8, description string) {
	q.Name = name
	q.Type = questType
	q.Zone = zone
	q.Level = level
	q.Description = description
	q.SetSaveNeeded(true)
}

// AddQuestStep adds a step to the quest
func (q *Quest) AddQuestStep(step *QuestStep) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	// Check if step ID already exists
	if _, exists := q.QuestStepMap[step.ID]; exists {
		return false
	}

	// Add to all tracking structures
	q.QuestSteps = append(q.QuestSteps, step)
	q.QuestStepMap[step.ID] = step
	q.QuestStepReverseMap[step] = step.ID

	// Handle task groups
	taskGroup := step.TaskGroup
	if taskGroup == "" {
		taskGroup = step.Description
	}

	if taskGroup != "" {
		// Add to task group order if new
		if _, exists := q.getTaskGroupByName(taskGroup); !exists {
			q.TaskGroupOrder[q.TaskGroupNum] = taskGroup
			q.TaskGroupNum++
		}

		// Add step to task group
		q.TaskGroup[taskGroup] = append(q.TaskGroup[taskGroup], step)
	}

	q.SetSaveNeeded(true)
	return true
}

// CreateQuestStep creates and adds a new quest step
func (q *Quest) CreateQuestStep(id int32, stepType int8, description string, ids []int32, quantity int32, taskGroup string, locations []*Location, maxVariation, percentage float32, usableItemID int32) *QuestStep {
	step := NewQuestStep(id, stepType, description, ids, quantity, taskGroup, locations, maxVariation, percentage, usableItemID)
	if q.AddQuestStep(step) {
		return step
	}
	return nil
}

// RemoveQuestStep removes a step from the quest
func (q *Quest) RemoveQuestStep(stepID int32) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	step, exists := q.QuestStepMap[stepID]
	if !exists {
		return false
	}

	// Remove from maps
	delete(q.QuestStepMap, stepID)
	delete(q.QuestStepReverseMap, step)

	// Remove from slice
	for i, questStep := range q.QuestSteps {
		if questStep == step {
			q.QuestSteps = append(q.QuestSteps[:i], q.QuestSteps[i+1:]...)
			break
		}
	}

	// Remove from task groups
	taskGroup := step.TaskGroup
	if taskGroup != "" {
		if steps, exists := q.TaskGroup[taskGroup]; exists {
			for i, taskStep := range steps {
				if taskStep == step {
					q.TaskGroup[taskGroup] = append(steps[:i], steps[i+1:]...)
					break
				}
			}

			// If task group is now empty, remove it
			if len(q.TaskGroup[taskGroup]) == 0 {
				delete(q.TaskGroup, taskGroup)
				// Find and remove from task group order
				for orderNum, groupName := range q.TaskGroupOrder {
					if groupName == taskGroup {
						delete(q.TaskGroupOrder, orderNum)
						q.TaskGroupNum--
						break
					}
				}
			}
		}
	}

	// Remove from actions
	q.completeActionsMutex.Lock()
	delete(q.CompleteActions, stepID)
	q.completeActionsMutex.Unlock()

	q.progressActionsMutex.Lock()
	delete(q.ProgressActions, stepID)
	q.progressActionsMutex.Unlock()

	q.failedActionsMutex.Lock()
	delete(q.FailedActions, stepID)
	q.failedActionsMutex.Unlock()

	q.SetSaveNeeded(true)
	return true
}

// GetQuestStep returns a quest step by ID
func (q *Quest) GetQuestStep(stepID int32) *QuestStep {
	q.stepsMutex.RLock()
	defer q.stepsMutex.RUnlock()
	return q.QuestStepMap[stepID]
}

// SetStepComplete marks a step as complete
func (q *Quest) SetStepComplete(stepID int32) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	step, exists := q.QuestStepMap[stepID]
	if !exists || step.Complete() {
		return false
	}

	step.SetComplete()
	q.StepUpdates = append(q.StepUpdates, step)
	q.SetSaveNeeded(true)
	return true
}

// AddStepProgress adds progress to a step
func (q *Quest) AddStepProgress(stepID int32, progress int32) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	step, exists := q.QuestStepMap[stepID]
	if !exists {
		return false
	}

	// Check percentage chance for success
	if step.Percentage < MaxPercentage && step.Percentage > 0 {
		if step.Percentage <= rand.Float32()*MaxPercentage {
			q.StepFailures = append(q.StepFailures, step)
			return false
		}
	}

	actualProgress := step.AddStepProgress(progress)
	if actualProgress > 0 {
		q.StepUpdates = append(q.StepUpdates, step)
		q.SetSaveNeeded(true)

		// TODO: Call progress action if exists
		q.progressActionsMutex.RLock()
		if action, exists := q.ProgressActions[stepID]; exists && action != "" {
			// TODO: Execute Lua script with action
			_ = action // Placeholder for Lua execution
		}
		q.progressActionsMutex.RUnlock()

		return true
	}

	return false
}

// GetStepProgress returns the current progress of a step
func (q *Quest) GetStepProgress(stepID int32) int32 {
	step := q.GetQuestStep(stepID)
	if step != nil {
		return step.GetStepProgress()
	}
	return 0
}

// GetQuestStepCompleted checks if a step is completed
func (q *Quest) GetQuestStepCompleted(stepID int32) bool {
	step := q.GetQuestStep(stepID)
	return step != nil && step.Complete()
}

// GetQuestStep returns the first incomplete step ID
func (q *Quest) GetCurrentQuestStep() int16 {
	q.stepsMutex.RLock()
	defer q.stepsMutex.RUnlock()

	for _, step := range q.QuestSteps {
		if !step.Complete() {
			return int16(step.ID)
		}
	}
	return 0
}

// QuestStepIsActive checks if a step is active (not completed)
func (q *Quest) QuestStepIsActive(stepID int16) bool {
	step := q.GetQuestStep(int32(stepID))
	return step != nil && !step.Complete()
}

// GetTaskGroupStep returns the current task group step
func (q *Quest) GetTaskGroupStep() int16 {
	q.stepsMutex.RLock()
	defer q.stepsMutex.RUnlock()

	ret := int16(len(q.TaskGroupOrder))

	for orderNum, taskGroupName := range q.TaskGroupOrder {
		if steps, exists := q.TaskGroup[taskGroupName]; exists {
			complete := true
			for _, step := range steps {
				if !step.Complete() {
					complete = false
					break
				}
			}
			if !complete && orderNum < ret {
				ret = orderNum
			}
		}
	}

	return ret
}

// CheckQuestReferencedSpawns checks if a spawn is referenced by any quest step
func (q *Quest) CheckQuestReferencedSpawns(spawnID int32) bool {
	q.stepsMutex.RLock()
	defer q.stepsMutex.RUnlock()

	for _, step := range q.QuestSteps {
		if step.Complete() {
			continue
		}

		switch step.Type {
		case StepTypeKill, StepTypeNormal:
			if step.CheckStepReferencedID(spawnID) {
				return true
			}
		case StepTypeKillRaceReq:
			// TODO: Implement race requirement checking
			// This would require spawn race information
		}
	}

	return false
}

// CheckQuestKillUpdate checks and updates kill quest steps
func (q *Quest) CheckQuestKillUpdate(spawnID int32, update bool) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	hasUpdate := false

	for _, step := range q.QuestSteps {
		if step.Complete() {
			continue
		}

		shouldUpdate := false
		switch step.Type {
		case StepTypeKill:
			shouldUpdate = step.CheckStepReferencedID(spawnID)
		case StepTypeKillRaceReq:
			// TODO: Implement race requirement checking
			// shouldUpdate = step.CheckStepKillRaceReqUpdate(spawn)
		}

		if shouldUpdate {
			if update {
				// Check percentage chance
				passed := true
				if step.Percentage < MaxPercentage {
					passed = step.Percentage > rand.Float32()*MaxPercentage
				}

				if passed {
					actualProgress := step.AddStepProgress(1)
					if actualProgress > 0 {
						q.StepUpdates = append(q.StepUpdates, step)

						// TODO: Call progress action
						q.progressActionsMutex.RLock()
						if action, exists := q.ProgressActions[step.ID]; exists && action != "" {
							// TODO: Execute Lua script
							_ = action
						}
						q.progressActionsMutex.RUnlock()

						hasUpdate = true
					}
				} else {
					q.StepFailures = append(q.StepFailures, step)
				}
			} else {
				hasUpdate = true
			}
		}
	}

	if hasUpdate && update {
		q.SetSaveNeeded(true)
	}

	return hasUpdate
}

// CheckQuestChatUpdate checks and updates chat quest steps
func (q *Quest) CheckQuestChatUpdate(npcID int32, update bool) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	hasUpdate := false

	for _, step := range q.QuestSteps {
		if step.Complete() || step.Type != StepTypeChat {
			continue
		}

		if step.CheckStepReferencedID(npcID) {
			if update {
				actualProgress := step.AddStepProgress(1)
				if actualProgress > 0 {
					q.StepUpdates = append(q.StepUpdates, step)

					// TODO: Call progress action
					q.progressActionsMutex.RLock()
					if action, exists := q.ProgressActions[step.ID]; exists && action != "" {
						// TODO: Execute Lua script
						_ = action
					}
					q.progressActionsMutex.RUnlock()
				}
			}
			hasUpdate = true
		}
	}

	if hasUpdate && update {
		q.SetSaveNeeded(true)
	}

	return hasUpdate
}

// CheckQuestItemUpdate checks and updates item quest steps
func (q *Quest) CheckQuestItemUpdate(itemID int32, quantity int8) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	hasUpdate := false

	for _, step := range q.QuestSteps {
		if step.Complete() || step.Type != StepTypeObtainItem {
			continue
		}

		if step.CheckStepReferencedID(itemID) {
			// Check percentage chance
			passed := true
			if step.Percentage < MaxPercentage {
				passed = step.Percentage > rand.Float32()*MaxPercentage
			}

			if passed {
				actualProgress := step.AddStepProgress(int32(quantity))
				if actualProgress > 0 {
					q.StepUpdates = append(q.StepUpdates, step)

					// TODO: Call progress action
					q.progressActionsMutex.RLock()
					if action, exists := q.ProgressActions[step.ID]; exists && action != "" {
						// TODO: Execute Lua script
						_ = action
					}
					q.progressActionsMutex.RUnlock()
				}
				hasUpdate = true
			} else {
				q.StepFailures = append(q.StepFailures, step)
			}
		}
	}

	if hasUpdate {
		q.SetSaveNeeded(true)
	}

	return hasUpdate
}

// CheckQuestLocationUpdate checks and updates location quest steps
func (q *Quest) CheckQuestLocationUpdate(charX, charY, charZ float32, zoneID int32) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	hasUpdate := false

	for _, step := range q.QuestSteps {
		if step.Complete() || step.Type != StepTypeLocation {
			continue
		}

		if step.CheckStepLocationUpdate(charX, charY, charZ, zoneID) {
			actualProgress := step.AddStepProgress(1)
			if actualProgress > 0 {
				q.StepUpdates = append(q.StepUpdates, step)

				// TODO: Call progress action
				q.progressActionsMutex.RLock()
				if action, exists := q.ProgressActions[step.ID]; exists && action != "" {
					// TODO: Execute Lua script
					_ = action
				}
				q.progressActionsMutex.RUnlock()
			}
			hasUpdate = true
		}
	}

	if hasUpdate {
		q.SetSaveNeeded(true)
	}

	return hasUpdate
}

// CheckQuestSpellUpdate checks and updates spell quest steps
func (q *Quest) CheckQuestSpellUpdate(spellID int32) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	hasUpdate := false

	for _, step := range q.QuestSteps {
		if step.Complete() || step.Type != StepTypeSpell {
			continue
		}

		if step.CheckStepReferencedID(spellID) {
			// Check percentage chance
			passed := true
			if step.Percentage < MaxPercentage {
				passed = step.Percentage > rand.Float32()*MaxPercentage
			}

			if passed {
				actualProgress := step.AddStepProgress(1)
				if actualProgress > 0 {
					q.StepUpdates = append(q.StepUpdates, step)

					// TODO: Call progress action
					q.progressActionsMutex.RLock()
					if action, exists := q.ProgressActions[step.ID]; exists && action != "" {
						// TODO: Execute Lua script
						_ = action
					}
					q.progressActionsMutex.RUnlock()
				}
				hasUpdate = true
			} else {
				q.StepFailures = append(q.StepFailures, step)
			}
		}
	}

	if hasUpdate {
		q.SetSaveNeeded(true)
	}

	return hasUpdate
}

// CheckQuestRefIDUpdate checks and updates reference ID quest steps (craft/harvest)
func (q *Quest) CheckQuestRefIDUpdate(refID int32, quantity int32) bool {
	q.stepsMutex.Lock()
	defer q.stepsMutex.Unlock()

	hasUpdate := false

	for _, step := range q.QuestSteps {
		if step.Complete() {
			continue
		}

		if step.Type == StepTypeHarvest || step.Type == StepTypeCraft {
			if step.CheckStepReferencedID(refID) {
				// Check percentage chance
				passed := true
				if step.Percentage < MaxPercentage {
					passed = step.Percentage > rand.Float32()*MaxPercentage
				}

				if passed {
					actualProgress := step.AddStepProgress(quantity)
					if actualProgress > 0 {
						q.StepUpdates = append(q.StepUpdates, step)

						// TODO: Call progress action
						q.progressActionsMutex.RLock()
						if action, exists := q.ProgressActions[step.ID]; exists && action != "" {
							// TODO: Execute Lua script
							_ = action
						}
						q.progressActionsMutex.RUnlock()
					}
					hasUpdate = true
				} else {
					q.StepFailures = append(q.StepFailures, step)
				}
			}
		}
	}

	if hasUpdate {
		q.SetSaveNeeded(true)
	}

	return hasUpdate
}

// GetCompleted checks if the quest is complete
func (q *Quest) GetCompleted() bool {
	q.stepsMutex.RLock()
	defer q.stepsMutex.RUnlock()

	for _, step := range q.QuestSteps {
		if !step.Complete() {
			return false
		}
	}
	return true
}

// CheckCategoryYellow checks if the quest category should be displayed in yellow
func (q *Quest) CheckCategoryYellow() bool {
	category := q.Type
	yellowCategories := []string{
		"Signature", "Heritage", "Hallmark", "Deity", "Miscellaneous",
		"Language", "Lore and Legend", "World Event", "Tradeskill",
	}

	for _, yellowCat := range yellowCategories {
		if strings.EqualFold(category, yellowCat) {
			return true
		}
	}
	return false
}

// SetStepTimer sets a timer for a quest step
func (q *Quest) SetStepTimer(duration int32) {
	if duration == 0 {
		q.Timestamp = 0
	} else {
		q.Timestamp = int32(time.Now().Unix()) + duration
	}
	q.SetSaveNeeded(true)
}

// StepFailed handles when a step fails
func (q *Quest) StepFailed(stepID int32) {
	q.failedActionsMutex.RLock()
	action, exists := q.FailedActions[stepID]
	q.failedActionsMutex.RUnlock()

	if exists && action != "" {
		// TODO: Execute Lua script for failed action
		_ = action
	}
}

// Helper methods

// getTaskGroupByName finds a task group by name
func (q *Quest) getTaskGroupByName(name string) ([]*QuestStep, bool) {
	steps, exists := q.TaskGroup[name]
	return steps, exists
}

// SetSaveNeeded sets the save needed flag
func (q *Quest) SetSaveNeeded(needed bool) {
	q.NeedsSave = needed
}

// SetQuestTemporaryState sets the quest temporary state
func (q *Quest) SetQuestTemporaryState(tempState bool, customDescription string) {
	if !tempState {
		q.TmpRewardCoins = 0
		q.TmpRewardStatus = 0
		// TODO: Clear temporary reward items
	}

	q.QuestStateTemporary = tempState
	q.QuestTempDescription = customDescription
	q.SetSaveNeeded(true)
}

// CanShareQuestCriteria checks if the quest meets sharing criteria
func (q *Quest) CanShareQuestCriteria(hasQuest, hasCompleted bool, currentStep int16) bool {
	shareableFlag := q.QuestShareableFlag

	// Check if quest can be shared at all
	if shareableFlag == ShareableNone {
		return false
	}

	// Check completed sharing
	if (shareableFlag&ShareableCompleted) == 0 && hasCompleted {
		return false
	}

	// Check if can only share when completed
	if shareableFlag == ShareableCompleted && !hasCompleted {
		return false
	}

	// Check during quest sharing
	if (shareableFlag&ShareableDuring) == 0 && hasQuest && currentStep > 1 {
		return false
	}

	// Check active quest sharing
	if (shareableFlag&ShareableActive) == 0 && hasQuest {
		return false
	}

	// Check if has quest for sharing
	if (shareableFlag&ShareableCompleted) == 0 && !hasQuest {
		return false
	}

	return true
}

// Validation methods

// ValidateQuest performs basic quest validation
func (q *Quest) ValidateQuest() error {
	if q.ID <= 0 {
		return fmt.Errorf("quest ID must be positive")
	}

	if q.Name == "" {
		return fmt.Errorf("quest name cannot be empty")
	}

	if len(q.Name) > MaxQuestNameLength {
		return fmt.Errorf("quest name too long (max %d)", MaxQuestNameLength)
	}

	if len(q.Description) > MaxQuestDescriptionLength {
		return fmt.Errorf("quest description too long (max %d)", MaxQuestDescriptionLength)
	}

	if q.Level < 1 || q.Level > 100 {
		return fmt.Errorf("quest level must be between 1 and 100")
	}

	if len(q.QuestSteps) == 0 {
		return fmt.Errorf("quest must have at least one step")
	}

	// Validate steps
	for _, step := range q.QuestSteps {
		if err := q.validateStep(step); err != nil {
			return fmt.Errorf("step %d validation failed: %w", step.ID, err)
		}
	}

	return nil
}

// validateStep validates a single quest step
func (q *Quest) validateStep(step *QuestStep) error {
	if step.ID <= 0 {
		return fmt.Errorf("step ID must be positive")
	}

	if step.Type < StepTypeKill || step.Type > StepTypeKillRaceReq {
		return fmt.Errorf("invalid step type: %d", step.Type)
	}

	if len(step.Description) > MaxStepDescriptionLength {
		return fmt.Errorf("step description too long (max %d)", MaxStepDescriptionLength)
	}

	if step.Quantity <= 0 {
		return fmt.Errorf("step quantity must be positive")
	}

	if step.Percentage < MinPercentage || step.Percentage > MaxPercentage {
		return fmt.Errorf("step percentage must be between %.1f and %.1f", MinPercentage, MaxPercentage)
	}

	// Type-specific validation
	switch step.Type {
	case StepTypeLocation:
		if len(step.Locations) == 0 {
			return fmt.Errorf("location step must have at least one location")
		}
		if step.MaxVariation < MinLocationVariation || step.MaxVariation > MaxLocationVariation {
			return fmt.Errorf("location max variation must be between %.1f and %.1f", MinLocationVariation, MaxLocationVariation)
		}
	default:
		if len(step.IDs) == 0 {
			return fmt.Errorf("non-location step must have at least one referenced ID")
		}
	}

	return nil
}
