package com.pixelmintdrop.model

import android.content.Context
import android.content.SharedPreferences
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.math.min

/**
 * Manages player progression, experience points, and unlockable rewards
 */
class PlayerProgressionManager(context: Context) {
    private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
    
    // Player level and XP
    private var playerLevel: Int = 1
    private var playerXP: Long = 0
    private var totalXPEarned: Long = 0
    
    // Track unlocked rewards
    private val unlockedThemes = mutableSetOf<String>()
    private val unlockedBlocks = mutableSetOf<String>()
    private val unlockedBadges = mutableSetOf<String>()
    
    // XP gained in the current session
    private var sessionXPGained: Long = 0
    
    init {
        loadProgress()
    }
    
    /**
     * Load player progression data from shared preferences
     */
    private fun loadProgress() {
        playerLevel = prefs.getInt(KEY_PLAYER_LEVEL, 1)
        playerXP = prefs.getLong(KEY_PLAYER_XP, 0)
        totalXPEarned = prefs.getLong(KEY_TOTAL_XP_EARNED, 0)
        
        // Load unlocked rewards
        val themesSet = prefs.getStringSet(KEY_UNLOCKED_THEMES, setOf()) ?: setOf()
        val blocksSet = prefs.getStringSet(KEY_UNLOCKED_BLOCKS, setOf()) ?: setOf()
        val badgesSet = prefs.getStringSet(KEY_UNLOCKED_BADGES, setOf()) ?: setOf()
        
        unlockedThemes.addAll(themesSet)
        unlockedBlocks.addAll(blocksSet)
        unlockedBadges.addAll(badgesSet)
        
        // Add default theme if nothing is unlocked
        if (unlockedThemes.isEmpty()) {
            unlockedThemes.add(THEME_CLASSIC)
        }
        
        // Always ensure default block skin is present (Level 1)
        unlockedBlocks.add("block_skin_1")
        
        // Explicitly check and save all unlocks for the current level on load
        checkAllUnlocksForCurrentLevel()
    }
    
    /**
     * Save player progression data to shared preferences
     */
    private fun saveProgress() {
        prefs.edit()
            .putInt(KEY_PLAYER_LEVEL, playerLevel)
            .putLong(KEY_PLAYER_XP, playerXP)
            .putLong(KEY_TOTAL_XP_EARNED, totalXPEarned)
            .putStringSet(KEY_UNLOCKED_THEMES, unlockedThemes)
            .putStringSet(KEY_UNLOCKED_BLOCKS, unlockedBlocks)
            .putStringSet(KEY_UNLOCKED_BADGES, unlockedBadges)
            .apply()
    }
    
    /**
     * Calculate XP required to reach a specific level
     */
    fun calculateXPForLevel(level: Int): Long {
        return (BASE_XP * level.toDouble().pow(XP_CURVE_FACTOR)).roundToInt().toLong()
    }
    
    /**
     * Calculate total XP required to reach a certain level from level 1
     */
    fun calculateTotalXPForLevel(level: Int): Long {
        var totalXP = 0L
        for (lvl in 1 until level) {
            totalXP += calculateXPForLevel(lvl)
        }
        return totalXP
    }
    
    /**
     * Calculate XP from a game session based on score, lines, level, etc.
     */
    fun calculateGameXP(score: Int, linesCleared: Int, level: Int, gameTime: Long, 
                        tSpins: Int, combos: Int, quadCount: Int, perfectClearCount: Int): Long {
        // Base XP from score with level multiplier (capped at level 10)
        val cappedLevel = min(level, 10)
        val scoreXP = (score * (1 + LEVEL_MULTIPLIER * cappedLevel)).toLong()
        
        // XP from lines cleared (reduced for higher levels)
        val linesXP = linesCleared * XP_PER_LINE * (1 - (level - 1) * 0.05).coerceAtLeast(0.5)
        
        // XP from special moves (reduced for higher levels)
        val tSpinXP = tSpins * T_SPIN_XP_BONUS * (1 - (level - 1) * 0.05).coerceAtLeast(0.5)
        val combosXP = combos * COMBO_XP_BONUS
        val quadBonus = quadCount * QUAD_XP_BONUS * (1 - (level - 1) * 0.05).coerceAtLeast(0.5)
        val perfectClearBonus = perfectClearCount * PERFECT_CLEAR_XP_BONUS
        
        // Time bonus (reduced for longer games)
        val timeBonus = (gameTime / 60000) * TIME_XP_PER_MINUTE * (1 - (gameTime / 3600000) * 0.1).coerceAtLeast(0.5)
        
        // Calculate total XP
        return (scoreXP + linesXP + tSpinXP + combosXP + quadBonus + perfectClearBonus + timeBonus).toLong()
    }
    
    /**
     * Add XP to the player and handle level-ups
     * Returns a list of newly unlocked rewards
     */
    fun addXP(xpAmount: Long): List<String> {
        sessionXPGained = xpAmount
        playerXP += xpAmount
        totalXPEarned += xpAmount
        
        val newRewards = mutableListOf<String>()
        val oldLevel = playerLevel
        
        // Check for level ups
        var xpForNextLevel = calculateXPForLevel(playerLevel)
        while (playerXP >= xpForNextLevel) {
            playerXP -= xpForNextLevel
            playerLevel++
            
            // Check for new rewards at this level
            val levelRewards = checkLevelRewards(playerLevel)
            newRewards.addAll(levelRewards)
            
            // Calculate XP needed for the next level
            xpForNextLevel = calculateXPForLevel(playerLevel)
        }
        
        // Save progress if there were any changes
        if (oldLevel != playerLevel || newRewards.isNotEmpty()) {
            saveProgress()
        }
        
        return newRewards
    }
    
    /**
     * Check if the player unlocked new rewards at the current level
     */
    private fun checkLevelRewards(level: Int): List<String> {
        val newRewards = mutableListOf<String>()
        
        // Check for theme unlocks
        when (level) {
            5 -> {
                if (unlockedThemes.add(THEME_NEON)) {
                    newRewards.add("Unlocked Neon Theme!")
                }
            }
            10 -> {
                if (unlockedThemes.add(THEME_MONOCHROME)) {
                    newRewards.add("Unlocked Monochrome Theme!")
                }
            }
            15 -> {
                if (unlockedThemes.add(THEME_RETRO)) {
                    newRewards.add("Unlocked Retro Arcade Theme!")
                }
            }
            20 -> {
                if (unlockedThemes.add(THEME_MINIMALIST)) {
                    newRewards.add("Unlocked Minimalist Theme!")
                }
            }
            25 -> {
                if (unlockedThemes.add(THEME_GALAXY)) {
                    newRewards.add("Unlocked Galaxy Theme!")
                }
            }
        }
        
        // Check for block skin unlocks (start from skin 2 at level 7)
        when (level) {
            7 -> {
                if (unlockedBlocks.add("block_skin_2")) {
                    newRewards.add("Unlocked Neon Block Skin!")
                }
            }
            14 -> {
                if (unlockedBlocks.add("block_skin_3")) {
                    newRewards.add("Unlocked Retro Block Skin!")
                }
            }
            21 -> {
                if (unlockedBlocks.add("block_skin_4")) {
                    newRewards.add("Unlocked Minimalist Block Skin!")
                }
            }
            28 -> {
                if (unlockedBlocks.add("block_skin_5")) {
                    newRewards.add("Unlocked Galaxy Block Skin!")
                }
            }
        }
        
        return newRewards
    }
    
    /**
     * Check and unlock any rewards the player should have based on their current level
     * This ensures players don't miss unlocks if they level up multiple times at once
     */
    private fun checkAllUnlocksForCurrentLevel() {
        // Check theme unlocks
        if (playerLevel >= 5) unlockedThemes.add(THEME_NEON)
        if (playerLevel >= 10) unlockedThemes.add(THEME_MONOCHROME)
        if (playerLevel >= 15) unlockedThemes.add(THEME_RETRO)
        if (playerLevel >= 20) unlockedThemes.add(THEME_MINIMALIST)
        if (playerLevel >= 25) unlockedThemes.add(THEME_GALAXY)
        
        // Check block skin unlocks (start from skin 2 at level 7)
        // Skin 1 is default (added in loadProgress)
        if (playerLevel >= 7) unlockedBlocks.add("block_skin_2")
        if (playerLevel >= 14) unlockedBlocks.add("block_skin_3")
        if (playerLevel >= 21) unlockedBlocks.add("block_skin_4")
        if (playerLevel >= 28) unlockedBlocks.add("block_skin_5")
        
        // Save any newly unlocked items
        saveProgress()
    }
    
    /**
     * Start a new progression session
     */
    fun startNewSession() {
        sessionXPGained = 0
        
        // Ensure all appropriate unlocks are available
        checkAllUnlocksForCurrentLevel()
    }
    
    // Getters
    fun getPlayerLevel(): Int = playerLevel
    fun getCurrentXP(): Long = playerXP
    fun getXPForNextLevel(): Long = calculateXPForLevel(playerLevel)
    fun getSessionXPGained(): Long = sessionXPGained
    fun getUnlockedThemes(): Set<String> = unlockedThemes.toSet()
    fun getUnlockedBlocks(): Set<String> = unlockedBlocks.toSet()
    fun getUnlockedBadges(): Set<String> = unlockedBadges.toSet()
    
    /**
     * Check if a specific theme is unlocked
     */
    fun isThemeUnlocked(themeId: String): Boolean {
        return unlockedThemes.contains(themeId)
    }
    
    /**
     * Award a badge to the player
     */
    fun awardBadge(badgeId: String): Boolean {
        val newlyAwarded = unlockedBadges.add(badgeId)
        if (newlyAwarded) {
            saveProgress()
        }
        return newlyAwarded
    }
    
    /**
     * Reset all player progression data
     */
    fun resetProgress() {
        playerLevel = 1
        playerXP = 0
        totalXPEarned = 0
        
        unlockedThemes.clear()
        unlockedBlocks.clear()
        unlockedBadges.clear()
        
        // Add default theme
        unlockedThemes.add(THEME_CLASSIC)
        
        // Add default block skin (Level 1)
        unlockedBlocks.add("block_skin_1")
        
        saveProgress()
    }
    
    companion object {
        private const val PREFS_NAME = "pixelmintdrop_progression"
        private const val KEY_PLAYER_LEVEL = "player_level"
        private const val KEY_PLAYER_XP = "player_xp"
        private const val KEY_TOTAL_XP_EARNED = "total_xp_earned"
        private const val KEY_UNLOCKED_THEMES = "unlocked_themes"
        private const val KEY_UNLOCKED_BLOCKS = "unlocked_blocks"
        private const val KEY_UNLOCKED_BADGES = "unlocked_badges"
        private const val KEY_SELECTED_THEME = "selected_theme"
        private const val KEY_SELECTED_BLOCK_SKIN = "selected_block_skin"
        
        // XP constants
        private const val BASE_XP = 3000L
        private const val XP_CURVE_FACTOR = 2.0
        private const val LEVEL_MULTIPLIER = 0.03
        private const val XP_PER_LINE = 40L
        private const val COMBO_XP_BONUS = 5L
        private const val PERFECT_CLEAR_XP_BONUS = 200L
        private const val T_SPIN_XP_BONUS = 80L
        private const val QUAD_XP_BONUS = 150L
        private const val TIME_XP_PER_MINUTE = 20L
        
        // Theme constants
        const val THEME_CLASSIC = "theme_classic"
        const val THEME_NEON = "theme_neon"
        const val THEME_MONOCHROME = "theme_monochrome"
        const val THEME_RETRO = "theme_retro"
        const val THEME_MINIMALIST = "theme_minimalist"
        const val THEME_GALAXY = "theme_galaxy"
        
        // Map of themes to required levels
        val THEME_REQUIRED_LEVELS = mapOf(
            THEME_CLASSIC to 1,
            THEME_NEON to 5,
            THEME_MONOCHROME to 10,
            THEME_RETRO to 15,
            THEME_MINIMALIST to 20,
            THEME_GALAXY to 25
        )
    }
    
    /**
     * Get the required level for a specific theme
     */
    fun getRequiredLevelForTheme(themeId: String): Int {
        return THEME_REQUIRED_LEVELS[themeId] ?: 1
    }
    
    /**
     * Set the selected block skin
     */
    fun setSelectedBlockSkin(skinId: String) {
        if (unlockedBlocks.contains(skinId)) {
            prefs.edit().putString(KEY_SELECTED_BLOCK_SKIN, skinId).commit()
        }
    }
    
    /**
     * Get the selected block skin
     */
    fun getSelectedBlockSkin(): String {
        return prefs.getString(KEY_SELECTED_BLOCK_SKIN, "block_skin_1") ?: "block_skin_1"
    }
    
    /**
     * Set the selected theme
     */
    fun setSelectedTheme(themeId: String) {
        if (unlockedThemes.contains(themeId)) {
            prefs.edit().putString(KEY_SELECTED_THEME, themeId).apply()
        }
    }
    
    /**
     * Get the selected theme
     */
    fun getSelectedTheme(): String {
        return prefs.getString(KEY_SELECTED_THEME, THEME_CLASSIC) ?: THEME_CLASSIC
    }
} 