package quests

import "fmt"

// Reward management methods for Quest

// AddRewardCoins adds coin rewards
func (q *Quest) AddRewardCoins(copper, silver, gold, platinum int32) {
	q.RewardCoins = int64(copper) + int64(silver)*100 + int64(gold)*10000 + int64(platinum)*1000000
	q.SetSaveNeeded(true)
}

// AddRewardCoinsMax sets the maximum coin reward
func (q *Quest) AddRewardCoinsMax(coins int64) {
	q.RewardCoinsMax = coins
	q.SetSaveNeeded(true)
}

// AddRewardFaction adds a faction reward
func (q *Quest) AddRewardFaction(factionID int32, amount int32) {
	q.RewardFactions[factionID] = amount
	q.SetSaveNeeded(true)
}

// RemoveRewardFaction removes a faction reward
func (q *Quest) RemoveRewardFaction(factionID int32) bool {
	if _, exists := q.RewardFactions[factionID]; exists {
		delete(q.RewardFactions, factionID)
		q.SetSaveNeeded(true)
		return true
	}
	return false
}

// SetRewardStatus sets the status point reward
func (q *Quest) SetRewardStatus(amount int32) {
	q.RewardStatus = amount
	q.SetSaveNeeded(true)
}

// SetRewardComment sets the reward comment text
func (q *Quest) SetRewardComment(comment string) {
	q.RewardComment = comment
	q.SetSaveNeeded(true)
}

// SetRewardXP sets the experience point reward
func (q *Quest) SetRewardXP(xp int32) {
	q.RewardExp = xp
	q.SetSaveNeeded(true)
}

// SetRewardTSXP sets the tradeskill experience point reward
func (q *Quest) SetRewardTSXP(xp int32) {
	q.RewardTSExp = xp
	q.SetSaveNeeded(true)
}

// SetGeneratedCoin sets the generated coin amount
func (q *Quest) SetGeneratedCoin(coin int64) {
	q.GeneratedCoin = coin
	q.SetSaveNeeded(true)
}

// GetCoinsReward returns the base coin reward
func (q *Quest) GetCoinsReward() int64 {
	return q.RewardCoins
}

// GetCoinsRewardMax returns the maximum coin reward
func (q *Quest) GetCoinsRewardMax() int64 {
	return q.RewardCoinsMax
}

// GetGeneratedCoin returns the generated coin amount
func (q *Quest) GetGeneratedCoin() int64 {
	return q.GeneratedCoin
}

// GetExpReward returns the experience point reward
func (q *Quest) GetExpReward() int32 {
	return q.RewardExp
}

// GetTSExpReward returns the tradeskill experience point reward
func (q *Quest) GetTSExpReward() int32 {
	return q.RewardTSExp
}

// GetStatusPoints returns the status point reward
func (q *Quest) GetStatusPoints() int32 {
	return q.RewardStatus
}

// GetRewardFactions returns the faction rewards map
func (q *Quest) GetRewardFactions() map[int32]int32 {
	return q.RewardFactions
}

// GetRewardFaction returns the faction reward amount for a specific faction
func (q *Quest) GetRewardFaction(factionID int32) (int32, bool) {
	amount, exists := q.RewardFactions[factionID]
	return amount, exists
}

// HasRewardFaction checks if a faction has a reward
func (q *Quest) HasRewardFaction(factionID int32) bool {
	_, exists := q.RewardFactions[factionID]
	return exists
}

// ClearAllRewards removes all rewards
func (q *Quest) ClearAllRewards() {
	q.RewardCoins = 0
	q.RewardCoinsMax = 0
	q.RewardStatus = 0
	q.RewardComment = ""
	q.RewardExp = 0
	q.RewardTSExp = 0
	q.GeneratedCoin = 0
	q.RewardFactions = make(map[int32]int32)
	q.SetSaveNeeded(true)
}

// Temporary reward methods

// SetStatusTmpReward sets the temporary status reward
func (q *Quest) SetStatusTmpReward(status int32) {
	q.TmpRewardStatus = status
	q.SetSaveNeeded(true)
}

// GetStatusTmpReward returns the temporary status reward
func (q *Quest) GetStatusTmpReward() int32 {
	return q.TmpRewardStatus
}

// SetCoinTmpReward sets the temporary coin reward
func (q *Quest) SetCoinTmpReward(coins int64) {
	q.TmpRewardCoins = coins
	q.SetSaveNeeded(true)
}

// GetCoinTmpReward returns the temporary coin reward
func (q *Quest) GetCoinTmpReward() int64 {
	return q.TmpRewardCoins
}

// ClearTmpRewards clears temporary rewards
func (q *Quest) ClearTmpRewards() {
	q.TmpRewardStatus = 0
	q.TmpRewardCoins = 0
	q.SetSaveNeeded(true)
}

// Status earning methods

// SetStatusToEarnMin sets the minimum status to earn
func (q *Quest) SetStatusToEarnMin(value int32) {
	q.StatusToEarnMin = value
	q.SetSaveNeeded(true)
}

// GetStatusToEarnMin returns the minimum status to earn
func (q *Quest) GetStatusToEarnMin() int32 {
	return q.StatusToEarnMin
}

// SetStatusToEarnMax sets the maximum status to earn
func (q *Quest) SetStatusToEarnMax(value int32) {
	q.StatusToEarnMax = value
	q.SetSaveNeeded(true)
}

// GetStatusToEarnMax returns the maximum status to earn
func (q *Quest) GetStatusToEarnMax() int32 {
	return q.StatusToEarnMax
}

// SetStatusEarned sets the quest status earned
func (q *Quest) SetStatusEarned(status int32) {
	q.Status = status
	q.SetSaveNeeded(true)
}

// GetStatusEarned returns the quest status earned
func (q *Quest) GetStatusEarned() int32 {
	return q.Status
}

// Reward calculation methods

// CalculateCoinsReward calculates the actual coin reward based on level and other factors
func (q *Quest) CalculateCoinsReward(playerLevel int8) int64 {
	baseReward := q.RewardCoins
	if baseReward <= 0 {
		return 0
	}

	// Use generated coin if set
	if q.GeneratedCoin > 0 {
		return q.GeneratedCoin
	}

	// Level-based coin scaling (simplified version)
	levelMultiplier := float64(playerLevel) / float64(q.Level)
	if levelMultiplier > 1.5 {
		levelMultiplier = 1.5 // Cap the multiplier
	} else if levelMultiplier < 0.5 {
		levelMultiplier = 0.5 // Floor the multiplier
	}

	calculatedReward := int64(float64(baseReward) * levelMultiplier)

	// Apply max reward cap if set
	if q.RewardCoinsMax > 0 && calculatedReward > q.RewardCoinsMax {
		calculatedReward = q.RewardCoinsMax
	}

	return calculatedReward
}

// CalculateExpReward calculates the actual experience reward based on level and other factors
func (q *Quest) CalculateExpReward(playerLevel int8) int32 {
	baseReward := q.RewardExp
	if baseReward <= 0 {
		return 0
	}

	// Level-based experience scaling
	if playerLevel > q.Level {
		// Reduced XP for overleveled players
		levelDiff := playerLevel - q.Level
		reduction := float64(levelDiff) * 0.1
		if reduction > 0.8 {
			reduction = 0.8 // Max 80% reduction
		}
		return int32(float64(baseReward) * (1.0 - reduction))
	} else if playerLevel < q.Level {
		// Bonus XP for underleveled players (small bonus)
		levelDiff := q.Level - playerLevel
		bonus := float64(levelDiff) * 0.05
		if bonus > 0.25 {
			bonus = 0.25 // Max 25% bonus
		}
		return int32(float64(baseReward) * (1.0 + bonus))
	}

	return baseReward
}

// CalculateTSExpReward calculates the actual tradeskill experience reward
func (q *Quest) CalculateTSExpReward(playerTSLevel int8) int32 {
	baseReward := q.RewardTSExp
	if baseReward <= 0 {
		return 0
	}

	// Similar scaling as regular XP but for tradeskill level
	if playerTSLevel > q.Level {
		levelDiff := playerTSLevel - q.Level
		reduction := float64(levelDiff) * 0.1
		if reduction > 0.8 {
			reduction = 0.8
		}
		return int32(float64(baseReward) * (1.0 - reduction))
	} else if playerTSLevel < q.Level {
		levelDiff := q.Level - playerTSLevel
		bonus := float64(levelDiff) * 0.05
		if bonus > 0.25 {
			bonus = 0.25
		}
		return int32(float64(baseReward) * (1.0 + bonus))
	}

	return baseReward
}

// CalculateStatusReward calculates the actual status reward
func (q *Quest) CalculateStatusReward() int32 {
	// Use status earned if set, otherwise use base reward status
	if q.Status > 0 {
		return q.Status
	}
	return q.RewardStatus
}

// Reward validation methods

// ValidateRewards validates all quest rewards
func (q *Quest) ValidateRewards() error {
	// Validate coin rewards
	if q.RewardCoins < 0 {
		return fmt.Errorf("reward coins cannot be negative")
	}

	if q.RewardCoinsMax < 0 {
		return fmt.Errorf("reward coins max cannot be negative")
	}

	if q.RewardCoinsMax > 0 && q.RewardCoinsMax < q.RewardCoins {
		return fmt.Errorf("reward coins max cannot be less than base reward coins")
	}

	// Validate experience rewards
	if q.RewardExp < 0 {
		return fmt.Errorf("reward experience cannot be negative")
	}

	if q.RewardTSExp < 0 {
		return fmt.Errorf("reward tradeskill experience cannot be negative")
	}

	// Validate status rewards
	if q.RewardStatus < 0 {
		return fmt.Errorf("reward status cannot be negative")
	}

	if q.Status < 0 {
		return fmt.Errorf("status earned cannot be negative")
	}

	// Validate temporary rewards
	if q.TmpRewardCoins < 0 {
		return fmt.Errorf("temporary reward coins cannot be negative")
	}

	if q.TmpRewardStatus < 0 {
		return fmt.Errorf("temporary reward status cannot be negative")
	}

	// Validate status to earn ranges
	if q.StatusToEarnMin < 0 {
		return fmt.Errorf("status to earn min cannot be negative")
	}

	if q.StatusToEarnMax < 0 {
		return fmt.Errorf("status to earn max cannot be negative")
	}

	if q.StatusToEarnMax > 0 && q.StatusToEarnMax < q.StatusToEarnMin {
		return fmt.Errorf("status to earn max cannot be less than min")
	}

	// Validate faction rewards
	for factionID, amount := range q.RewardFactions {
		if factionID <= 0 {
			return fmt.Errorf("invalid faction ID in rewards: %d", factionID)
		}

		// Faction amounts can be negative (reputation loss)
		if amount < -50000 || amount > 50000 {
			return fmt.Errorf("faction reward amount out of range for faction %d: %d", factionID, amount)
		}
	}

	// Validate reward comment length
	if len(q.RewardComment) > MaxCompleteActionLength {
		return fmt.Errorf("reward comment too long (max %d)", MaxCompleteActionLength)
	}

	return nil
}

// Helper methods for coin conversion

// ConvertCoinsToComponents breaks down coins into copper, silver, gold, platinum
func ConvertCoinsToComponents(totalCoins int64) (copper, silver, gold, platinum int32) {
	platinum = int32(totalCoins / 1000000)
	remaining := totalCoins % 1000000

	gold = int32(remaining / 10000)
	remaining = remaining % 10000

	silver = int32(remaining / 100)
	copper = int32(remaining % 100)

	return copper, silver, gold, platinum
}

// ConvertComponentsToCoins converts individual coin components to total coins
func ConvertComponentsToCoins(copper, silver, gold, platinum int32) int64 {
	return int64(copper) + int64(silver)*100 + int64(gold)*10000 + int64(platinum)*1000000
}

// FormatCoinsString formats coins as a readable string
func FormatCoinsString(totalCoins int64) string {
	if totalCoins == 0 {
		return "0 copper"
	}

	copper, silver, gold, platinum := ConvertCoinsToComponents(totalCoins)

	var parts []string
	if platinum > 0 {
		parts = append(parts, fmt.Sprintf("%d platinum", platinum))
	}
	if gold > 0 {
		parts = append(parts, fmt.Sprintf("%d gold", gold))
	}
	if silver > 0 {
		parts = append(parts, fmt.Sprintf("%d silver", silver))
	}
	if copper > 0 {
		parts = append(parts, fmt.Sprintf("%d copper", copper))
	}

	if len(parts) == 0 {
		return "0 copper"
	}

	if len(parts) == 1 {
		return parts[0]
	}

	// Join with commas and "and" for the last item
	result := ""
	for i, part := range parts {
		if i == 0 {
			result = part
		} else if i == len(parts)-1 {
			result += " and " + part
		} else {
			result += ", " + part
		}
	}

	return result
}
