package quests

import (
	"sync"
	"time"
)

// Location represents a 3D location in a zone for quest steps
type Location struct {
	ID     int32   `json:"id"`
	X      float32 `json:"x"`
	Y      float32 `json:"y"`
	Z      float32 `json:"z"`
	ZoneID int32   `json:"zone_id"`
}

// NewLocation creates a new location
func NewLocation(id int32, x, y, z float32, zoneID int32) *Location {
	return &Location{
		ID:     id,
		X:      x,
		Y:      y,
		Z:      z,
		ZoneID: zoneID,
	}
}

// QuestFactionPrereq represents faction requirements for a quest
type QuestFactionPrereq struct {
	FactionID int32 `json:"faction_id"`
	Min       int32 `json:"min"`
	Max       int32 `json:"max"`
}

// NewQuestFactionPrereq creates a new faction prerequisite
func NewQuestFactionPrereq(factionID, min, max int32) *QuestFactionPrereq {
	return &QuestFactionPrereq{
		FactionID: factionID,
		Min:       min,
		Max:       max,
	}
}

// QuestStep represents a single step in a quest
type QuestStep struct {
	// Basic step data
	ID           int32   `json:"step_id"`
	Type         int8    `json:"type"`
	Description  string  `json:"description"`
	TaskGroup    string  `json:"task_group"`
	Quantity     int32   `json:"quantity"`
	StepProgress int32   `json:"step_progress"`
	Icon         int16   `json:"icon"`
	MaxVariation float32 `json:"max_variation"`
	Percentage   float32 `json:"percentage"`
	UsableItemID int32   `json:"usable_item_id"`

	// Tracking data
	UpdateName       string `json:"update_name"`
	UpdateTargetName string `json:"update_target_name"`
	Updated          bool   `json:"updated"`

	// Step data (one of these will be populated based on type)
	IDs       map[int32]bool `json:"ids,omitempty"`       // For kill, chat, obtain item, etc.
	Locations []*Location    `json:"locations,omitempty"` // For location steps

	// Thread safety
	mutex sync.RWMutex
}

// NewQuestStep creates a new quest step
func NewQuestStep(id int32, stepType int8, description string, ids []int32, quantity int32, taskGroup string, locations []*Location, maxVariation, percentage float32, usableItemID int32) *QuestStep {
	step := &QuestStep{
		ID:           id,
		Type:         stepType,
		Description:  description,
		TaskGroup:    taskGroup,
		Quantity:     quantity,
		StepProgress: 0,
		Icon:         DefaultIcon,
		MaxVariation: maxVariation,
		Percentage:   percentage,
		UsableItemID: usableItemID,
		Updated:      false,
	}

	// Initialize IDs map for non-location steps
	if stepType != StepTypeLocation && len(ids) > 0 {
		step.IDs = make(map[int32]bool)
		for _, id := range ids {
			step.IDs[id] = true
		}
	}

	// Initialize locations for location steps
	if stepType == StepTypeLocation && len(locations) > 0 {
		step.Locations = make([]*Location, len(locations))
		copy(step.Locations, locations)
	}

	return step
}

// Copy creates a copy of a quest step
func (qs *QuestStep) Copy() *QuestStep {
	qs.mutex.RLock()
	defer qs.mutex.RUnlock()

	newStep := &QuestStep{
		ID:               qs.ID,
		Type:             qs.Type,
		Description:      qs.Description,
		TaskGroup:        qs.TaskGroup,
		Quantity:         qs.Quantity,
		StepProgress:     0, // Reset progress for new quest copy
		Icon:             qs.Icon,
		MaxVariation:     qs.MaxVariation,
		Percentage:       qs.Percentage,
		UsableItemID:     qs.UsableItemID,
		UpdateName:       qs.UpdateName,
		UpdateTargetName: qs.UpdateTargetName,
		Updated:          false,
	}

	// Copy IDs map
	if qs.IDs != nil {
		newStep.IDs = make(map[int32]bool)
		for id, value := range qs.IDs {
			newStep.IDs[id] = value
		}
	}

	// Copy locations
	if qs.Locations != nil {
		newStep.Locations = make([]*Location, len(qs.Locations))
		for i, loc := range qs.Locations {
			newStep.Locations[i] = &Location{
				ID:     loc.ID,
				X:      loc.X,
				Y:      loc.Y,
				Z:      loc.Z,
				ZoneID: loc.ZoneID,
			}
		}
	}

	return newStep
}

// Complete checks if the step is complete
func (qs *QuestStep) Complete() bool {
	qs.mutex.RLock()
	defer qs.mutex.RUnlock()
	return qs.StepProgress >= qs.Quantity
}

// SetComplete marks the step as complete
func (qs *QuestStep) SetComplete() {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()
	qs.StepProgress = qs.Quantity
	qs.Updated = true
}

// AddStepProgress adds progress to the step and returns the actual amount added
func (qs *QuestStep) AddStepProgress(val int32) int32 {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()

	qs.Updated = true
	remaining := qs.Quantity - qs.StepProgress
	if val > remaining {
		qs.StepProgress = qs.Quantity
		return remaining
	}

	qs.StepProgress += val
	return val
}

// SetStepProgress sets the progress directly
func (qs *QuestStep) SetStepProgress(val int32) {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()
	qs.StepProgress = val
}

// GetStepProgress returns current progress
func (qs *QuestStep) GetStepProgress() int32 {
	qs.mutex.RLock()
	defer qs.mutex.RUnlock()
	return qs.StepProgress
}

// CheckStepReferencedID checks if an ID is referenced by this step
func (qs *QuestStep) CheckStepReferencedID(id int32) bool {
	qs.mutex.RLock()
	defer qs.mutex.RUnlock()

	if qs.IDs != nil {
		_, exists := qs.IDs[id]
		return exists
	}
	return false
}

// CheckStepLocationUpdate checks if character location matches step requirements
func (qs *QuestStep) CheckStepLocationUpdate(charX, charY, charZ float32, zoneID int32) bool {
	qs.mutex.RLock()
	defer qs.mutex.RUnlock()

	if qs.Locations == nil {
		return false
	}

	for _, loc := range qs.Locations {
		if loc.ZoneID > 0 && loc.ZoneID != zoneID {
			continue
		}

		// Calculate distance within max variation
		diffX := loc.X - charX
		if diffX < 0 {
			diffX = -diffX
		}
		if diffX <= qs.MaxVariation {
			diffZ := loc.Z - charZ
			if diffZ < 0 {
				diffZ = -diffZ
			}
			if diffZ <= qs.MaxVariation {
				totalDiff := diffX + diffZ
				if totalDiff <= qs.MaxVariation {
					diffY := loc.Y - charY
					if diffY < 0 {
						diffY = -diffY
					}
					if diffY <= qs.MaxVariation {
						totalDiff += diffY
						if totalDiff <= qs.MaxVariation {
							return true
						}
					}
				}
			}
		}
	}

	return false
}

// WasUpdated returns if step was updated
func (qs *QuestStep) WasUpdated() bool {
	qs.mutex.RLock()
	defer qs.mutex.RUnlock()
	return qs.Updated
}

// SetWasUpdated sets the updated flag
func (qs *QuestStep) SetWasUpdated(val bool) {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()
	qs.Updated = val
}

// GetCurrentQuantity returns current progress as int16
func (qs *QuestStep) GetCurrentQuantity() int16 {
	qs.mutex.RLock()
	defer qs.mutex.RUnlock()
	return int16(qs.StepProgress)
}

// GetNeededQuantity returns required quantity as int16
func (qs *QuestStep) GetNeededQuantity() int16 {
	qs.mutex.RLock()
	defer qs.mutex.RUnlock()
	return int16(qs.Quantity)
}

// ResetTaskGroup clears the task group
func (qs *QuestStep) ResetTaskGroup() {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()
	qs.TaskGroup = ""
}

// SetTaskGroup sets the task group
func (qs *QuestStep) SetTaskGroup(taskGroup string) {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()
	qs.TaskGroup = taskGroup
}

// SetDescription sets the step description
func (qs *QuestStep) SetDescription(description string) {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()
	qs.Description = description
}

// SetUpdateName sets the update name
func (qs *QuestStep) SetUpdateName(name string) {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()
	qs.UpdateName = name
}

// SetUpdateTargetName sets the update target name
func (qs *QuestStep) SetUpdateTargetName(name string) {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()
	qs.UpdateTargetName = name
}

// SetIcon sets the step icon
func (qs *QuestStep) SetIcon(icon int16) {
	qs.mutex.Lock()
	defer qs.mutex.Unlock()
	qs.Icon = icon
}

// Quest represents a complete quest with all its steps and requirements
type Quest struct {
	// Basic quest information
	ID             int32  `json:"quest_id"`
	Name           string `json:"name"`
	Type           string `json:"type"`
	Zone           string `json:"zone"`
	Level          int8   `json:"level"`
	EncounterLevel int8   `json:"encounter_level"`
	Description    string `json:"description"`
	CompletedDesc  string `json:"completed_description"`

	// Quest giver and return NPC
	QuestGiver int32 `json:"quest_giver"`
	ReturnID   int32 `json:"return_id"`

	// Prerequisites
	PrereqLevel      int8                  `json:"prereq_level"`
	PrereqTSLevel    int8                  `json:"prereq_ts_level"`
	PrereqMaxLevel   int8                  `json:"prereq_max_level"`
	PrereqMaxTSLevel int8                  `json:"prereq_max_ts_level"`
	PrereqFactions   []*QuestFactionPrereq `json:"prereq_factions"`
	PrereqRaces      []int8                `json:"prereq_races"`
	PrereqModelTypes []int16               `json:"prereq_model_types"`
	PrereqClasses    []int8                `json:"prereq_classes"`
	PrereqTSClasses  []int8                `json:"prereq_ts_classes"`
	PrereqQuests     []int32               `json:"prereq_quests"`

	// Rewards
	RewardCoins    int64           `json:"reward_coins"`
	RewardCoinsMax int64           `json:"reward_coins_max"`
	RewardFactions map[int32]int32 `json:"reward_factions"`
	RewardStatus   int32           `json:"reward_status"`
	RewardComment  string          `json:"reward_comment"`
	RewardExp      int32           `json:"reward_exp"`
	RewardTSExp    int32           `json:"reward_ts_exp"`
	GeneratedCoin  int64           `json:"generated_coin"`

	// Temporary rewards
	TmpRewardStatus int32 `json:"tmp_reward_status"`
	TmpRewardCoins  int64 `json:"tmp_reward_coins"`

	// Steps and task groups
	QuestSteps          []*QuestStep            `json:"quest_steps"`
	QuestStepMap        map[int32]*QuestStep    `json:"-"` // For quick lookup
	QuestStepReverseMap map[*QuestStep]int32    `json:"-"` // Reverse lookup
	StepUpdates         []*QuestStep            `json:"-"` // Steps that were updated
	StepFailures        []*QuestStep            `json:"-"` // Steps that failed
	TaskGroupOrder      map[int16]string        `json:"task_group_order"`
	TaskGroup           map[string][]*QuestStep `json:"-"` // Grouped steps
	TaskGroupNum        int16                   `json:"task_group_num"`

	// Actions
	CompleteActions map[int32]string `json:"complete_actions"`
	ProgressActions map[int32]string `json:"progress_actions"`
	FailedActions   map[int32]string `json:"failed_actions"`
	CompleteAction  string           `json:"complete_action"`

	// State tracking
	Deleted           bool `json:"deleted"`
	TurnedIn          bool `json:"turned_in"`
	UpdateNeeded      bool `json:"update_needed"`
	HasSentLastUpdate bool `json:"has_sent_last_update"`
	NeedsSave         bool `json:"needs_save"`
	Visible           int8 `json:"visible"`

	// Date tracking
	Day   int8 `json:"day"`
	Month int8 `json:"month"`
	Year  int8 `json:"year"`

	// Quest flags and settings
	FeatherColor  int8  `json:"feather_color"`
	Repeatable    bool  `json:"repeatable"`
	Tracked       bool  `json:"tracked"`
	CompletedFlag bool  `json:"completed_flag"`
	YellowName    bool  `json:"yellow_name"`
	QuestFlags    int32 `json:"quest_flags"`
	Hidden        bool  `json:"hidden"`
	Status        int32 `json:"status"`

	// Timer and completion tracking
	Timestamp     int32 `json:"timestamp"`
	TimerStep     int32 `json:"timer_step"`
	CompleteCount int16 `json:"complete_count"`

	// Temporary state
	QuestStateTemporary  bool   `json:"quest_state_temporary"`
	QuestTempDescription string `json:"quest_temp_description"`
	QuestShareableFlag   int32  `json:"quest_shareable_flag"`
	CanDeleteQuest       bool   `json:"can_delete_quest"`
	StatusToEarnMin      int32  `json:"status_to_earn_min"`
	StatusToEarnMax      int32  `json:"status_to_earn_max"`
	HideReward           bool   `json:"hide_reward"`

	// Thread safety
	stepsMutex           sync.RWMutex
	completeActionsMutex sync.RWMutex
	progressActionsMutex sync.RWMutex
	failedActionsMutex   sync.RWMutex
}

// NewQuest creates a new quest with the given ID
func NewQuest(id int32) *Quest {
	now := time.Now()

	quest := &Quest{
		ID:                   id,
		PrereqLevel:          DefaultPrereqLevel,
		PrereqTSLevel:        0,
		PrereqMaxLevel:       0,
		PrereqMaxTSLevel:     0,
		RewardCoins:          0,
		RewardCoinsMax:       0,
		CompletedFlag:        false,
		HasSentLastUpdate:    false,
		EncounterLevel:       0,
		RewardExp:            0,
		RewardTSExp:          0,
		FeatherColor:         0,
		Repeatable:           false,
		YellowName:           false,
		Hidden:               false,
		GeneratedCoin:        0,
		QuestFlags:           0,
		Timestamp:            0,
		CompleteCount:        0,
		QuestStateTemporary:  false,
		TmpRewardStatus:      0,
		TmpRewardCoins:       0,
		CompletedDesc:        "",
		QuestTempDescription: "",
		QuestShareableFlag:   0,
		CanDeleteQuest:       false,
		Status:               0,
		StatusToEarnMin:      0,
		StatusToEarnMax:      0,
		HideReward:           false,
		Deleted:              false,
		TurnedIn:             false,
		UpdateNeeded:         true,
		NeedsSave:            false,
		TaskGroupNum:         DefaultTaskGroupNum,
		Visible:              DefaultVisible,
		Day:                  int8(now.Day()),
		Month:                int8(now.Month()),
		Year:                 int8(now.Year() - 2000), // EQ2 uses 2-digit years

		// Initialize maps and slices
		QuestStepMap:        make(map[int32]*QuestStep),
		QuestStepReverseMap: make(map[*QuestStep]int32),
		TaskGroupOrder:      make(map[int16]string),
		TaskGroup:           make(map[string][]*QuestStep),
		CompleteActions:     make(map[int32]string),
		ProgressActions:     make(map[int32]string),
		FailedActions:       make(map[int32]string),
		RewardFactions:      make(map[int32]int32),
	}

	return quest
}

// Copy creates a complete copy of a quest (used when giving quest to player)
func (q *Quest) Copy() *Quest {
	q.stepsMutex.RLock()
	defer q.stepsMutex.RUnlock()

	newQuest := NewQuest(q.ID)

	// Copy basic information
	newQuest.Name = q.Name
	newQuest.Type = q.Type
	newQuest.Zone = q.Zone
	newQuest.Level = q.Level
	newQuest.EncounterLevel = q.EncounterLevel
	newQuest.Description = q.Description
	newQuest.CompletedDesc = q.CompletedDesc
	newQuest.QuestGiver = q.QuestGiver
	newQuest.ReturnID = q.ReturnID

	// Copy prerequisites
	newQuest.PrereqLevel = q.PrereqLevel
	newQuest.PrereqTSLevel = q.PrereqTSLevel
	newQuest.PrereqMaxLevel = q.PrereqMaxLevel
	newQuest.PrereqMaxTSLevel = q.PrereqMaxTSLevel

	// Copy prerequisite slices - create new slices
	newQuest.PrereqRaces = make([]int8, len(q.PrereqRaces))
	copy(newQuest.PrereqRaces, q.PrereqRaces)

	newQuest.PrereqModelTypes = make([]int16, len(q.PrereqModelTypes))
	copy(newQuest.PrereqModelTypes, q.PrereqModelTypes)

	newQuest.PrereqClasses = make([]int8, len(q.PrereqClasses))
	copy(newQuest.PrereqClasses, q.PrereqClasses)

	newQuest.PrereqTSClasses = make([]int8, len(q.PrereqTSClasses))
	copy(newQuest.PrereqTSClasses, q.PrereqTSClasses)

	newQuest.PrereqQuests = make([]int32, len(q.PrereqQuests))
	copy(newQuest.PrereqQuests, q.PrereqQuests)

	// Copy faction prerequisites
	newQuest.PrereqFactions = make([]*QuestFactionPrereq, len(q.PrereqFactions))
	for i, faction := range q.PrereqFactions {
		newQuest.PrereqFactions[i] = &QuestFactionPrereq{
			FactionID: faction.FactionID,
			Min:       faction.Min,
			Max:       faction.Max,
		}
	}

	// Copy rewards
	newQuest.RewardCoins = q.RewardCoins
	newQuest.RewardCoinsMax = q.RewardCoinsMax
	newQuest.RewardStatus = q.RewardStatus
	newQuest.RewardComment = q.RewardComment
	newQuest.RewardExp = q.RewardExp
	newQuest.RewardTSExp = q.RewardTSExp
	newQuest.GeneratedCoin = q.GeneratedCoin

	// Copy reward factions map
	for factionID, amount := range q.RewardFactions {
		newQuest.RewardFactions[factionID] = amount
	}

	// Copy quest steps
	for _, step := range q.QuestSteps {
		newQuest.AddQuestStep(step.Copy())
	}

	// Copy actions maps
	q.completeActionsMutex.RLock()
	for stepID, action := range q.CompleteActions {
		newQuest.CompleteActions[stepID] = action
	}
	q.completeActionsMutex.RUnlock()

	q.progressActionsMutex.RLock()
	for stepID, action := range q.ProgressActions {
		newQuest.ProgressActions[stepID] = action
	}
	q.progressActionsMutex.RUnlock()

	q.failedActionsMutex.RLock()
	for stepID, action := range q.FailedActions {
		newQuest.FailedActions[stepID] = action
	}
	q.failedActionsMutex.RUnlock()

	// Copy other properties
	newQuest.CompleteAction = q.CompleteAction
	newQuest.FeatherColor = q.FeatherColor
	newQuest.Repeatable = q.Repeatable
	newQuest.Hidden = q.Hidden
	newQuest.QuestFlags = q.QuestFlags
	newQuest.Status = q.Status
	newQuest.CompleteCount = q.CompleteCount
	newQuest.QuestShareableFlag = q.QuestShareableFlag
	newQuest.CanDeleteQuest = q.CanDeleteQuest
	newQuest.StatusToEarnMin = q.StatusToEarnMin
	newQuest.StatusToEarnMax = q.StatusToEarnMax
	newQuest.HideReward = q.HideReward
	newQuest.CompletedFlag = q.CompletedFlag
	newQuest.HasSentLastUpdate = q.HasSentLastUpdate
	newQuest.YellowName = q.YellowName

	// Reset state for new quest copy
	newQuest.StepUpdates = make([]*QuestStep, 0)
	newQuest.StepFailures = make([]*QuestStep, 0)
	newQuest.Deleted = false
	newQuest.UpdateNeeded = true
	newQuest.TurnedIn = false
	newQuest.QuestStateTemporary = false
	newQuest.TmpRewardStatus = 0
	newQuest.TmpRewardCoins = 0
	newQuest.QuestTempDescription = ""

	return newQuest
}
