package quests

import (
	"fmt"
	"sync"
)

// MasterQuestList manages all quests in the system
type MasterQuestList struct {
	quests map[int32]*Quest
	mutex  sync.RWMutex
}

// NewMasterQuestList creates a new master quest list
func NewMasterQuestList() *MasterQuestList {
	return &MasterQuestList{
		quests: make(map[int32]*Quest),
	}
}

// AddQuest adds a quest to the master list
func (mql *MasterQuestList) AddQuest(questID int32, quest *Quest) error {
	if quest == nil {
		return fmt.Errorf("quest cannot be nil")
	}

	if questID != quest.ID {
		return fmt.Errorf("quest ID mismatch: provided %d, quest has %d", questID, quest.ID)
	}

	mql.mutex.Lock()
	defer mql.mutex.Unlock()

	if _, exists := mql.quests[questID]; exists {
		return fmt.Errorf("quest %d already exists", questID)
	}

	mql.quests[questID] = quest
	return nil
}

// GetQuest returns a quest by ID, optionally creating a copy
func (mql *MasterQuestList) GetQuest(questID int32, copyQuest bool) *Quest {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	quest, exists := mql.quests[questID]
	if !exists {
		return nil
	}

	if copyQuest {
		return quest.Copy()
	}

	return quest
}

// RemoveQuest removes a quest from the master list
func (mql *MasterQuestList) RemoveQuest(questID int32) bool {
	mql.mutex.Lock()
	defer mql.mutex.Unlock()

	if _, exists := mql.quests[questID]; exists {
		delete(mql.quests, questID)
		return true
	}

	return false
}

// HasQuest checks if a quest exists
func (mql *MasterQuestList) HasQuest(questID int32) bool {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	_, exists := mql.quests[questID]
	return exists
}

// GetAllQuests returns a copy of all quests map
func (mql *MasterQuestList) GetAllQuests() map[int32]*Quest {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	quests := make(map[int32]*Quest)
	for id, quest := range mql.quests {
		quests[id] = quest
	}

	return quests
}

// GetQuestsByLevel returns quests within a level range
func (mql *MasterQuestList) GetQuestsByLevel(minLevel, maxLevel int8) []*Quest {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	var quests []*Quest
	for _, quest := range mql.quests {
		if quest.Level >= minLevel && quest.Level <= maxLevel {
			quests = append(quests, quest)
		}
	}

	return quests
}

// GetQuestsByType returns quests of a specific type
func (mql *MasterQuestList) GetQuestsByType(questType string) []*Quest {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	var quests []*Quest
	for _, quest := range mql.quests {
		if quest.Type == questType {
			quests = append(quests, quest)
		}
	}

	return quests
}

// GetQuestsByZone returns quests in a specific zone
func (mql *MasterQuestList) GetQuestsByZone(zone string) []*Quest {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	var quests []*Quest
	for _, quest := range mql.quests {
		if quest.Zone == zone {
			quests = append(quests, quest)
		}
	}

	return quests
}

// GetQuestsByGiver returns quests given by a specific NPC
func (mql *MasterQuestList) GetQuestsByGiver(giverID int32) []*Quest {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	var quests []*Quest
	for _, quest := range mql.quests {
		if quest.QuestGiver == giverID {
			quests = append(quests, quest)
		}
	}

	return quests
}

// GetRepeatableQuests returns all repeatable quests
func (mql *MasterQuestList) GetRepeatableQuests() []*Quest {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	var quests []*Quest
	for _, quest := range mql.quests {
		if quest.Repeatable {
			quests = append(quests, quest)
		}
	}

	return quests
}

// GetQuestCount returns the total number of quests
func (mql *MasterQuestList) GetQuestCount() int {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	return len(mql.quests)
}

// Clear removes all quests
func (mql *MasterQuestList) Clear() {
	mql.mutex.Lock()
	defer mql.mutex.Unlock()

	mql.quests = make(map[int32]*Quest)
}

// Reload clears and reloads all quests
func (mql *MasterQuestList) Reload() {
	mql.Clear()

	// TODO: Implement quest reloading from database or files
	// This would typically involve:
	// 1. Loading quest data from database
	// 2. Creating Quest objects
	// 3. Adding them to the master list

	// For now, this is a placeholder
	fmt.Println("Quest reload requested - implementation pending")
}

// ValidateAllQuests validates all quests in the master list
func (mql *MasterQuestList) ValidateAllQuests() []error {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	var errors []error

	for questID, quest := range mql.quests {
		if err := quest.ValidateQuest(); err != nil {
			errors = append(errors, fmt.Errorf("quest %d validation failed: %w", questID, err))
		}
	}

	return errors
}

// GetQuestStatistics returns basic statistics about the quest system
func (mql *MasterQuestList) GetQuestStatistics() *QuestStatistics {
	mql.mutex.RLock()
	defer mql.mutex.RUnlock()

	stats := &QuestStatistics{
		TotalQuests:     len(mql.quests),
		QuestsByType:    make(map[string]int),
		QuestsByLevel:   make(map[int8]int),
		RepeatableCount: 0,
		HiddenCount:     0,
	}

	for _, quest := range mql.quests {
		// Count by type
		stats.QuestsByType[quest.Type]++

		// Count by level
		stats.QuestsByLevel[quest.Level]++

		// Count special flags
		if quest.Repeatable {
			stats.RepeatableCount++
		}
		if quest.Hidden {
			stats.HiddenCount++
		}
	}

	return stats
}

// QuestStatistics contains statistical information about quests
type QuestStatistics struct {
	TotalQuests     int            `json:"total_quests"`
	QuestsByType    map[string]int `json:"quests_by_type"`
	QuestsByLevel   map[int8]int   `json:"quests_by_level"`
	RepeatableCount int            `json:"repeatable_count"`
	HiddenCount     int            `json:"hidden_count"`
}

// QuestManager provides high-level quest management functionality
type QuestManager struct {
	masterList   *MasterQuestList
	playerQuests map[int32]map[int32]*Quest // playerID -> questID -> quest
	mutex        sync.RWMutex
}

// NewQuestManager creates a new quest manager
func NewQuestManager() *QuestManager {
	return &QuestManager{
		masterList:   NewMasterQuestList(),
		playerQuests: make(map[int32]map[int32]*Quest),
	}
}

// GetMasterList returns the master quest list
func (qm *QuestManager) GetMasterList() *MasterQuestList {
	return qm.masterList
}

// GiveQuestToPlayer assigns a quest to a player
func (qm *QuestManager) GiveQuestToPlayer(playerID, questID int32) (*Quest, error) {
	// Get quest from master list (copy it)
	quest := qm.masterList.GetQuest(questID, true)
	if quest == nil {
		return nil, fmt.Errorf("quest %d not found", questID)
	}

	qm.mutex.Lock()
	defer qm.mutex.Unlock()

	// Initialize player quest map if needed
	if qm.playerQuests[playerID] == nil {
		qm.playerQuests[playerID] = make(map[int32]*Quest)
	}

	// Check if player already has this quest
	if _, exists := qm.playerQuests[playerID][questID]; exists {
		return nil, fmt.Errorf("player %d already has quest %d", playerID, questID)
	}

	// Assign quest to player
	qm.playerQuests[playerID][questID] = quest

	return quest, nil
}

// RemoveQuestFromPlayer removes a quest from a player
func (qm *QuestManager) RemoveQuestFromPlayer(playerID, questID int32) bool {
	qm.mutex.Lock()
	defer qm.mutex.Unlock()

	if playerQuests, exists := qm.playerQuests[playerID]; exists {
		if _, questExists := playerQuests[questID]; questExists {
			delete(playerQuests, questID)

			// Clean up empty player quest map
			if len(playerQuests) == 0 {
				delete(qm.playerQuests, playerID)
			}

			return true
		}
	}

	return false
}

// GetPlayerQuest returns a specific quest for a player
func (qm *QuestManager) GetPlayerQuest(playerID, questID int32) *Quest {
	qm.mutex.RLock()
	defer qm.mutex.RUnlock()

	if playerQuests, exists := qm.playerQuests[playerID]; exists {
		return playerQuests[questID]
	}

	return nil
}

// GetPlayerQuests returns all quests for a player
func (qm *QuestManager) GetPlayerQuests(playerID int32) map[int32]*Quest {
	qm.mutex.RLock()
	defer qm.mutex.RUnlock()

	if playerQuests, exists := qm.playerQuests[playerID]; exists {
		// Return a copy
		quests := make(map[int32]*Quest)
		for questID, quest := range playerQuests {
			quests[questID] = quest
		}
		return quests
	}

	return make(map[int32]*Quest)
}

// PlayerHasQuest checks if a player has a specific quest
func (qm *QuestManager) PlayerHasQuest(playerID, questID int32) bool {
	return qm.GetPlayerQuest(playerID, questID) != nil
}

// GetPlayerQuestCount returns the number of quests a player has
func (qm *QuestManager) GetPlayerQuestCount(playerID int32) int {
	qm.mutex.RLock()
	defer qm.mutex.RUnlock()

	if playerQuests, exists := qm.playerQuests[playerID]; exists {
		return len(playerQuests)
	}

	return 0
}

// ClearPlayerQuests removes all quests from a player
func (qm *QuestManager) ClearPlayerQuests(playerID int32) {
	qm.mutex.Lock()
	defer qm.mutex.Unlock()

	delete(qm.playerQuests, playerID)
}

// GetAllPlayerIDs returns all player IDs that have quests
func (qm *QuestManager) GetAllPlayerIDs() []int32 {
	qm.mutex.RLock()
	defer qm.mutex.RUnlock()

	var playerIDs []int32
	for playerID := range qm.playerQuests {
		playerIDs = append(playerIDs, playerID)
	}

	return playerIDs
}

// UpdatePlayerQuestProgress updates progress for a player's quest step
func (qm *QuestManager) UpdatePlayerQuestProgress(playerID, questID, stepID, progress int32) bool {
	quest := qm.GetPlayerQuest(playerID, questID)
	if quest == nil {
		return false
	}

	return quest.AddStepProgress(stepID, progress)
}

// CompletePlayerQuestStep marks a quest step as complete for a player
func (qm *QuestManager) CompletePlayerQuestStep(playerID, questID, stepID int32) bool {
	quest := qm.GetPlayerQuest(playerID, questID)
	if quest == nil {
		return false
	}

	return quest.SetStepComplete(stepID)
}

// IsPlayerQuestComplete checks if a player's quest is complete
func (qm *QuestManager) IsPlayerQuestComplete(playerID, questID int32) bool {
	quest := qm.GetPlayerQuest(playerID, questID)
	if quest == nil {
		return false
	}

	return quest.GetCompleted()
}

// GetPlayerQuestStatistics returns statistics for a player's quests
func (qm *QuestManager) GetPlayerQuestStatistics(playerID int32) *PlayerQuestStatistics {
	quests := qm.GetPlayerQuests(playerID)

	stats := &PlayerQuestStatistics{
		TotalQuests:     len(quests),
		CompletedQuests: 0,
		QuestsByType:    make(map[string]int),
		QuestsByLevel:   make(map[int8]int),
	}

	for _, quest := range quests {
		if quest.GetCompleted() {
			stats.CompletedQuests++
		}

		stats.QuestsByType[quest.Type]++
		stats.QuestsByLevel[quest.Level]++
	}

	return stats
}

// PlayerQuestStatistics contains statistical information about a player's quests
type PlayerQuestStatistics struct {
	TotalQuests     int            `json:"total_quests"`
	CompletedQuests int            `json:"completed_quests"`
	QuestsByType    map[string]int `json:"quests_by_type"`
	QuestsByLevel   map[int8]int   `json:"quests_by_level"`
}
