package achievements

import (
	"fmt"
	"time"
)

// PlayerList manages achievements for a specific player
type PlayerList struct {
	achievements map[uint32]*Achievement
}

// PlayerUpdateList manages achievement updates/progress for a specific player
type PlayerUpdateList struct {
	updates map[uint32]*Update
}

// NewPlayerList creates a new player achievement list
func NewPlayerList() *PlayerList {
	return &PlayerList{
		achievements: make(map[uint32]*Achievement),
	}
}

// NewPlayerUpdateList creates a new player achievement update list
func NewPlayerUpdateList() *PlayerUpdateList {
	return &PlayerUpdateList{
		updates: make(map[uint32]*Update),
	}
}

// AddAchievement adds an achievement to the player's list
// Returns false if achievement with same ID already exists
func (p *PlayerList) AddAchievement(achievement *Achievement) bool {
	if achievement == nil {
		return false
	}

	if _, exists := p.achievements[achievement.ID]; exists {
		return false
	}

	p.achievements[achievement.ID] = achievement
	return true
}

// GetAchievement retrieves an achievement by ID
// Returns nil if not found
func (p *PlayerList) GetAchievement(id uint32) *Achievement {
	return p.achievements[id]
}

// GetAllAchievements returns all player achievements
func (p *PlayerList) GetAllAchievements() map[uint32]*Achievement {
	result := make(map[uint32]*Achievement, len(p.achievements))
	for id, achievement := range p.achievements {
		result[id] = achievement
	}
	return result
}

// RemoveAchievement removes an achievement from the player's list
// Returns true if achievement was found and removed
func (p *PlayerList) RemoveAchievement(id uint32) bool {
	if _, exists := p.achievements[id]; !exists {
		return false
	}

	delete(p.achievements, id)
	return true
}

// HasAchievement checks if player has a specific achievement
func (p *PlayerList) HasAchievement(id uint32) bool {
	_, exists := p.achievements[id]
	return exists
}

// Clear removes all achievements from the player's list
func (p *PlayerList) Clear() {
	p.achievements = make(map[uint32]*Achievement)
}

// Size returns the number of achievements in the player's list
func (p *PlayerList) Size() int {
	return len(p.achievements)
}

// GetAchievementsByCategory returns player achievements filtered by category
func (p *PlayerList) GetAchievementsByCategory(category string) []*Achievement {
	var result []*Achievement
	for _, achievement := range p.achievements {
		if achievement.Category == category {
			result = append(result, achievement)
		}
	}
	return result
}

// AddUpdate adds an achievement update to the player's list
// Returns false if update with same ID already exists
func (p *PlayerUpdateList) AddUpdate(update *Update) bool {
	if update == nil {
		return false
	}

	if _, exists := p.updates[update.ID]; exists {
		return false
	}

	p.updates[update.ID] = update
	return true
}

// GetUpdate retrieves an achievement update by ID
// Returns nil if not found
func (p *PlayerUpdateList) GetUpdate(id uint32) *Update {
	return p.updates[id]
}

// GetAllUpdates returns all player achievement updates
func (p *PlayerUpdateList) GetAllUpdates() map[uint32]*Update {
	result := make(map[uint32]*Update, len(p.updates))
	for id, update := range p.updates {
		result[id] = update
	}
	return result
}

// UpdateProgress updates or creates achievement progress
func (p *PlayerUpdateList) UpdateProgress(achievementID uint32, itemUpdate uint32) {
	update := p.updates[achievementID]
	if update == nil {
		update = NewUpdate()
		update.ID = achievementID
		p.updates[achievementID] = update
	}

	// Add or update the progress item
	found := false
	for i := range update.UpdateItems {
		if update.UpdateItems[i].AchievementID == achievementID {
			update.UpdateItems[i].ItemUpdate = itemUpdate
			found = true
			break
		}
	}

	if !found {
		update.AddUpdateItem(UpdateItem{
			AchievementID: achievementID,
			ItemUpdate:    itemUpdate,
		})
	}
}

// CompleteAchievement marks an achievement as completed
func (p *PlayerUpdateList) CompleteAchievement(achievementID uint32) {
	update := p.updates[achievementID]
	if update == nil {
		update = NewUpdate()
		update.ID = achievementID
		p.updates[achievementID] = update
	}
	update.CompletedDate = time.Now()
}

// IsCompleted checks if an achievement is completed
func (p *PlayerUpdateList) IsCompleted(achievementID uint32) bool {
	update := p.updates[achievementID]
	return update != nil && !update.CompletedDate.IsZero()
}

// GetCompletedDate returns the completion date for an achievement
// Returns zero time if not completed
func (p *PlayerUpdateList) GetCompletedDate(achievementID uint32) time.Time {
	update := p.updates[achievementID]
	if update == nil {
		return time.Time{}
	}
	return update.CompletedDate
}

// GetProgress returns the current progress for an achievement
// Returns 0 if no progress found
func (p *PlayerUpdateList) GetProgress(achievementID uint32) uint32 {
	update := p.updates[achievementID]
	if update == nil || len(update.UpdateItems) == 0 {
		return 0
	}

	// Return the first matching update item's progress
	for _, item := range update.UpdateItems {
		if item.AchievementID == achievementID {
			return item.ItemUpdate
		}
	}
	return 0
}

// RemoveUpdate removes an achievement update from the player's list
// Returns true if update was found and removed
func (p *PlayerUpdateList) RemoveUpdate(id uint32) bool {
	if _, exists := p.updates[id]; !exists {
		return false
	}

	delete(p.updates, id)
	return true
}

// Clear removes all updates from the player's list
func (p *PlayerUpdateList) Clear() {
	p.updates = make(map[uint32]*Update)
}

// Size returns the number of updates in the player's list
func (p *PlayerUpdateList) Size() int {
	return len(p.updates)
}

// GetCompletedAchievements returns all completed achievement IDs
func (p *PlayerUpdateList) GetCompletedAchievements() []uint32 {
	var completed []uint32
	for id, update := range p.updates {
		if !update.CompletedDate.IsZero() {
			completed = append(completed, id)
		}
	}
	return completed
}

// GetInProgressAchievements returns all in-progress achievement IDs
func (p *PlayerUpdateList) GetInProgressAchievements() []uint32 {
	var inProgress []uint32
	for id, update := range p.updates {
		if update.CompletedDate.IsZero() && len(update.UpdateItems) > 0 {
			inProgress = append(inProgress, id)
		}
	}
	return inProgress
}

// PlayerManager combines achievement list and update list for a player
type PlayerManager struct {
	Achievements *PlayerList
	Updates      *PlayerUpdateList
}

// NewPlayerManager creates a new player manager
func NewPlayerManager() *PlayerManager {
	return &PlayerManager{
		Achievements: NewPlayerList(),
		Updates:      NewPlayerUpdateList(),
	}
}

// CheckRequirements validates if player meets achievement requirements
// This is a basic implementation - extend as needed for specific game logic
func (pm *PlayerManager) CheckRequirements(achievement *Achievement) (bool, error) {
	if achievement == nil {
		return false, fmt.Errorf("achievement cannot be nil")
	}

	// Basic implementation - check if we have progress >= required quantity
	progress := pm.Updates.GetProgress(achievement.ID)
	return progress >= achievement.QtyRequired, nil
}

// GetCompletionStatus returns completion percentage for an achievement
func (pm *PlayerManager) GetCompletionStatus(achievement *Achievement) float64 {
	if achievement == nil || achievement.QtyRequired == 0 {
		return 0.0
	}

	progress := pm.Updates.GetProgress(achievement.ID)
	if progress >= achievement.QtyRequired {
		return 100.0
	}

	return (float64(progress) / float64(achievement.QtyRequired)) * 100.0
}
