package app.chompass.models

import app.chompass.R

import kotlinx.serialization.Serializable
import kotlin.math.roundToInt

enum class HomeTopNutrient(
    val storageKey: String,
    val displayName: String,
    val unit: String,
    val displayNameRes: Int,
    val unitRes: Int
) {
    PROTEIN("protein", "Protein", "g", R.string.nutrition_label_protein, R.string.unit_g),
    CARBS("carbs", "Carbs", "g", R.string.nutrition_label_carbs, R.string.unit_g),
    FAT("fat", "Fat", "g", R.string.nutrition_label_fat, R.string.unit_g),
    FIBER("fiber", "Fiber", "g", R.string.nutrition_label_fiber, R.string.unit_g),
    SUGAR("sugar", "Sugar", "g", R.string.nutrition_label_sugar, R.string.unit_g),
    ADDED_SUGAR("addedSugar", "Added Sugar", "g", R.string.nutrition_label_added_sugar, R.string.unit_g),
    SATURATED_FAT("saturatedFat", "Sat Fat", "g", R.string.nutrient_short_sat_fat, R.string.unit_g),
    CHOLESTEROL("cholesterol", "Cholesterol", "mg", R.string.nutrition_label_cholesterol, R.string.unit_mg),
    SODIUM("sodium", "Sodium", "mg", R.string.nutrition_label_sodium, R.string.unit_mg),
    POTASSIUM("potassium", "Potassium", "mg", R.string.nutrition_label_potassium, R.string.unit_mg),
    TRANS_FAT("transFat", "Trans Fat", "g", R.string.nutrition_label_trans_fat, R.string.unit_g),
    CALCIUM("calcium", "Calcium", "mg", R.string.nutrition_label_calcium, R.string.unit_mg),
    IRON("iron", "Iron", "mg", R.string.nutrition_label_iron, R.string.unit_mg),
    MAGNESIUM("magnesium", "Magnesium", "mg", R.string.nutrition_label_magnesium, R.string.unit_mg),
    ZINC("zinc", "Zinc", "mg", R.string.nutrition_label_zinc, R.string.unit_mg),
    VITAMIN_A("vitaminA", "Vit A", "mcg", R.string.nutrient_short_vit_a, R.string.unit_mcg),
    VITAMIN_C("vitaminC", "Vit C", "mg", R.string.nutrient_short_vit_c, R.string.unit_mg),
    VITAMIN_D("vitaminD", "Vit D", "mcg", R.string.nutrient_short_vit_d, R.string.unit_mcg),
    VITAMIN_B12("vitaminB12", "B12", "mcg", R.string.nutrient_short_b12, R.string.unit_mcg),
    VITAMIN_E("vitaminE", "Vit E", "mg", R.string.nutrient_short_vit_e, R.string.unit_mg),
    VITAMIN_K("vitaminK", "Vit K", "mcg", R.string.nutrient_short_vit_k, R.string.unit_mcg),
    FOLATE("folate", "Folate", "mcg", R.string.nutrition_label_folate, R.string.unit_mcg),
    OMEGA3("omega3", "Omega-3", "g", R.string.nutrient_short_omega, R.string.unit_g),
    CAFFEINE("caffeine", "Caffeine", "mg", R.string.nutrition_label_caffeine, R.string.unit_mg);

    fun current(entries: List<FoodEntry>): Double = when (this) {
        PROTEIN -> entries.sumOf { it.protein }
        CARBS -> entries.sumOf { it.carbs }
        FAT -> entries.sumOf { it.fat }
        FIBER -> entries.sumOf { it.fiber ?: 0.0 }
        SUGAR -> entries.sumOf { it.sugar ?: 0.0 }
        ADDED_SUGAR -> entries.sumOf { it.addedSugar ?: 0.0 }
        SATURATED_FAT -> entries.sumOf { it.saturatedFat ?: 0.0 }
        CHOLESTEROL -> entries.sumOf { it.cholesterol ?: 0.0 }
        SODIUM -> entries.sumOf { it.sodium ?: 0.0 }
        POTASSIUM -> entries.sumOf { it.potassium ?: 0.0 }
        TRANS_FAT -> entries.sumOf { it.transFat ?: 0.0 }
        CALCIUM -> entries.sumOf { it.calcium ?: 0.0 }
        IRON -> entries.sumOf { it.iron ?: 0.0 }
        MAGNESIUM -> entries.sumOf { it.magnesium ?: 0.0 }
        ZINC -> entries.sumOf { it.zinc ?: 0.0 }
        VITAMIN_A -> entries.sumOf { it.vitaminA ?: 0.0 }
        VITAMIN_C -> entries.sumOf { it.vitaminC ?: 0.0 }
        VITAMIN_D -> entries.sumOf { it.vitaminD ?: 0.0 }
        VITAMIN_B12 -> entries.sumOf { it.vitaminB12 ?: 0.0 }
        VITAMIN_E -> entries.sumOf { it.vitaminE ?: 0.0 }
        VITAMIN_K -> entries.sumOf { it.vitaminK ?: 0.0 }
        FOLATE -> entries.sumOf { it.folate ?: 0.0 }
        OMEGA3 -> entries.sumOf { it.omega3 ?: 0.0 }
        CAFFEINE -> entries.sumOf { it.caffeine ?: 0.0 }
    }

    /**
     * P/C/F goals scale with the hero ring's projected day when ADD_ACTIVE
     * pushes the displayed calorie target above the stored base (#38); the
     * optional micronutrient goals are fixed daily targets and never scale.
     */
    fun goal(profile: UserProfile?, optionalGoals: OptionalNutrientGoals, macroScale: Float = 1f): Int = when (this) {
        PROTEIN -> scaleMacro(profile?.effectiveProtein ?: 150, macroScale)
        CARBS -> scaleMacro(profile?.effectiveCarbs ?: 220, macroScale)
        FAT -> scaleMacro(profile?.effectiveFat ?: 70, macroScale)
        FIBER -> optionalGoals.fiber
        SUGAR -> optionalGoals.sugar
        ADDED_SUGAR -> optionalGoals.addedSugar
        SATURATED_FAT -> optionalGoals.saturatedFat
        CHOLESTEROL -> optionalGoals.cholesterol
        SODIUM -> optionalGoals.sodium
        POTASSIUM -> optionalGoals.potassium
        TRANS_FAT -> optionalGoals.transFat
        CALCIUM -> optionalGoals.calcium
        IRON -> optionalGoals.iron
        MAGNESIUM -> optionalGoals.magnesium
        ZINC -> optionalGoals.zinc
        VITAMIN_A -> optionalGoals.vitaminA
        VITAMIN_C -> optionalGoals.vitaminC
        VITAMIN_D -> optionalGoals.vitaminD
        VITAMIN_B12 -> optionalGoals.vitaminB12
        VITAMIN_E -> optionalGoals.vitaminE
        VITAMIN_K -> optionalGoals.vitaminK
        FOLATE -> optionalGoals.folate
        OMEGA3 -> optionalGoals.omega3
        CAFFEINE -> optionalGoals.caffeine
    }

    /**
     * Macro day types (#60 phase 1): P/C/F goals come from the day-type plan's
     * resolved targets for the viewed day ([MacroPlanResolver] output); a null
     * [resolved] or a base fallback keeps the profile set. Optional
     * micronutrient goals are unchanged fixed globals.
     */
    fun goal(
        resolved: ResolvedDayTargets?,
        profile: UserProfile?,
        optionalGoals: OptionalNutrientGoals,
        macroScale: Float = 1f,
    ): Int = when (this) {
        PROTEIN -> scaleMacro(resolved?.targets?.proteinG ?: profile?.effectiveProtein ?: 150, macroScale)
        CARBS -> scaleMacro(resolved?.targets?.carbsG ?: profile?.effectiveCarbs ?: 220, macroScale)
        FAT -> scaleMacro(resolved?.targets?.fatG ?: profile?.effectiveFat ?: 70, macroScale)
        else -> goal(profile, optionalGoals, macroScale)
    }

    private fun scaleMacro(grams: Int, scale: Float): Int =
        if (scale > 1f && grams > 0) (grams * scale).roundToInt() else grams

    companion object {
        /** #75: P/C/F — never part of the Progress averages selection (the macro card covers them). */
        val Macros = setOf(PROTEIN, CARBS, FAT)

        /**
         * #75: nutrients the Progress averages card can show, in canonical
         * declaration order — every goal-bearing non-macro nutrient.
         */
        val AveragesCandidates: List<HomeTopNutrient> = entries.filterNot { it in Macros }

        /** The original trio: pref unset renders exactly the pre-#75 rows. */
        val DefaultAverages: Set<HomeTopNutrient> = setOf(FIBER, SUGAR, SODIUM)
        val DefaultAveragesStorage: Set<String> = DefaultAverages.map { it.storageKey }.toSet()

        /**
         * Normalizes a stored/raw key set for the averages selection: known
         * non-macro storage keys (or enum names) only. The empty set is kept —
         * master toggle on with nothing selected hides the card.
         */
        fun normalizeAveragesSelectionStorage(raw: Iterable<String>): Set<String> =
            raw.mapNotNull { key ->
                values().firstOrNull { it.storageKey == key || it.name == key }
            }
                .filter { it !in Macros }
                .map { it.storageKey }
                .toSet()

        /** Raw set → canonical-order non-macro selection. */
        fun averagesSelectionFromStorage(raw: Collection<String>): List<HomeTopNutrient> {
            val keys = normalizeAveragesSelectionStorage(raw)
            return AveragesCandidates.filter { it.storageKey in keys }
        }

        val DefaultSelection = listOf(PROTEIN, CARBS, FAT, FIBER)
        val DefaultStorageValue = DefaultSelection.joinToString(",") { it.storageKey }

        fun fromStorage(raw: String?, cardCount: Int = HomeDisplayPreferences.DEFAULT_NUTRIENT_CARD_COUNT): List<HomeTopNutrient> {
            val selected = raw
                ?.split(",")
                ?.mapNotNull { part ->
                    val key = part.trim()
                    values().firstOrNull { it.storageKey == key || it.name == key }
                }
                .orEmpty()
            return normalized(selected, cardCount)
        }

        fun toStorage(selection: List<HomeTopNutrient>, cardCount: Int = HomeDisplayPreferences.DEFAULT_NUTRIENT_CARD_COUNT): String =
            normalized(selection, cardCount).joinToString(",") { it.storageKey }

        fun normalized(
            selection: List<HomeTopNutrient>,
            cardCount: Int = HomeDisplayPreferences.DEFAULT_NUTRIENT_CARD_COUNT,
        ): List<HomeTopNutrient> =
            (selection.distinct() + DefaultSelection)
                .distinct()
                .take(cardCount.coerceIn(HomeDisplayPreferences.MIN_NUTRIENT_CARD_COUNT, HomeDisplayPreferences.MAX_NUTRIENT_CARD_COUNT))
    }
}

enum class OptionalNutrient(
    val displayName: String,
    val unit: String,
    val defaultGoal: Int,
    val displayNameRes: Int,
    val unitRes: Int,
    /** Bounds and increment for the goal wheel picker in Settings. */
    val goalRange: IntRange,
    val goalStep: Int,
    /**
     * Upper clamp for the free "Enter custom value…" input. The wheel range
     * is intentionally tighter; custom values may exceed it, but not this
     * cap (keeps goal math and gauges sane). Vitamin D's cap keeps the
     * common 10,000 IU (250 mcg) target reachable.
     */
    val maxCustomGoal: Int
) {
    SUGAR("Sugar", "g", 50, R.string.nutrition_label_sugar, R.string.unit_g, 0..200, 5, 500),
    ADDED_SUGAR("Added Sugar", "g", 25, R.string.nutrition_label_added_sugar, R.string.unit_g, 0..100, 5, 300),
    FIBER("Fiber", "g", 30, R.string.nutrition_label_fiber, R.string.unit_g, 0..100, 1, 300),
    SATURATED_FAT("Saturated Fat", "g", 20, R.string.nutrition_label_saturated_fat, R.string.unit_g, 0..80, 1, 200),
    CHOLESTEROL("Cholesterol", "mg", 300, R.string.nutrition_label_cholesterol, R.string.unit_mg, 0..1000, 25, 2000),
    SODIUM("Sodium", "mg", 2300, R.string.nutrition_label_sodium, R.string.unit_mg, 0..5000, 50, 10000),
    POTASSIUM("Potassium", "mg", 3500, R.string.nutrition_label_potassium, R.string.unit_mg, 0..7000, 50, 15000),
    TRANS_FAT("Trans Fat", "g", 0, R.string.nutrition_label_trans_fat, R.string.unit_g, 0..10, 1, 50),
    CALCIUM("Calcium", "mg", 1000, R.string.nutrition_label_calcium, R.string.unit_mg, 300..2000, 50, 5000),
    IRON("Iron", "mg", 18, R.string.nutrition_label_iron, R.string.unit_mg, 5..45, 1, 200),
    MAGNESIUM("Magnesium", "mg", 400, R.string.nutrition_label_magnesium, R.string.unit_mg, 100..800, 25, 2000),
    ZINC("Zinc", "mg", 11, R.string.nutrition_label_zinc, R.string.unit_mg, 3..40, 1, 100),
    VITAMIN_A("Vitamin A", "mcg", 900, R.string.nutrition_label_vitamin_a, R.string.unit_mcg, 300..3000, 50, 5000),
    VITAMIN_C("Vitamin C", "mg", 90, R.string.nutrition_label_vitamin_c, R.string.unit_mg, 20..500, 10, 2000),
    VITAMIN_D("Vitamin D", "mcg", 20, R.string.nutrition_label_vitamin_d, R.string.unit_mcg, 5..100, 1, 500),
    VITAMIN_B12("Vitamin B12", "mcg", 3, R.string.nutrition_label_vitamin_b12, R.string.unit_mcg, 1..20, 1, 100),
    VITAMIN_E("Vitamin E", "mg", 15, R.string.nutrition_label_vitamin_e, R.string.unit_mg, 5..100, 1, 1000),
    VITAMIN_K("Vitamin K", "mcg", 120, R.string.nutrition_label_vitamin_k, R.string.unit_mcg, 30..300, 10, 1000),
    FOLATE("Folate", "mcg", 400, R.string.nutrition_label_folate, R.string.unit_mcg, 100..1000, 50, 2000),
    OMEGA3("Omega-3", "g", 2, R.string.nutrition_label_omega3, R.string.unit_g, 0..10, 1, 50),
    CAFFEINE("Caffeine", "mg", 400, R.string.nutrition_label_caffeine, R.string.unit_mg, 0..1000, 25, 2000)
}

@Serializable
data class OptionalNutrientGoals(
    val sugar: Int = OptionalNutrient.SUGAR.defaultGoal,
    val addedSugar: Int = OptionalNutrient.ADDED_SUGAR.defaultGoal,
    val fiber: Int = OptionalNutrient.FIBER.defaultGoal,
    val saturatedFat: Int = OptionalNutrient.SATURATED_FAT.defaultGoal,
    val cholesterol: Int = OptionalNutrient.CHOLESTEROL.defaultGoal,
    val sodium: Int = OptionalNutrient.SODIUM.defaultGoal,
    val potassium: Int = OptionalNutrient.POTASSIUM.defaultGoal,
    val transFat: Int = OptionalNutrient.TRANS_FAT.defaultGoal,
    val calcium: Int = OptionalNutrient.CALCIUM.defaultGoal,
    val iron: Int = OptionalNutrient.IRON.defaultGoal,
    val magnesium: Int = OptionalNutrient.MAGNESIUM.defaultGoal,
    val zinc: Int = OptionalNutrient.ZINC.defaultGoal,
    val vitaminA: Int = OptionalNutrient.VITAMIN_A.defaultGoal,
    val vitaminC: Int = OptionalNutrient.VITAMIN_C.defaultGoal,
    val vitaminD: Int = OptionalNutrient.VITAMIN_D.defaultGoal,
    val vitaminB12: Int = OptionalNutrient.VITAMIN_B12.defaultGoal,
    val vitaminE: Int = OptionalNutrient.VITAMIN_E.defaultGoal,
    val vitaminK: Int = OptionalNutrient.VITAMIN_K.defaultGoal,
    val folate: Int = OptionalNutrient.FOLATE.defaultGoal,
    val omega3: Int = OptionalNutrient.OMEGA3.defaultGoal,
    val caffeine: Int = OptionalNutrient.CAFFEINE.defaultGoal
) {
    fun valueFor(nutrient: OptionalNutrient): Int = when (nutrient) {
        OptionalNutrient.SUGAR -> sugar
        OptionalNutrient.ADDED_SUGAR -> addedSugar
        OptionalNutrient.FIBER -> fiber
        OptionalNutrient.SATURATED_FAT -> saturatedFat
        OptionalNutrient.CHOLESTEROL -> cholesterol
        OptionalNutrient.SODIUM -> sodium
        OptionalNutrient.POTASSIUM -> potassium
        OptionalNutrient.TRANS_FAT -> transFat
        OptionalNutrient.CALCIUM -> calcium
        OptionalNutrient.IRON -> iron
        OptionalNutrient.MAGNESIUM -> magnesium
        OptionalNutrient.ZINC -> zinc
        OptionalNutrient.VITAMIN_A -> vitaminA
        OptionalNutrient.VITAMIN_C -> vitaminC
        OptionalNutrient.VITAMIN_D -> vitaminD
        OptionalNutrient.VITAMIN_B12 -> vitaminB12
        OptionalNutrient.VITAMIN_E -> vitaminE
        OptionalNutrient.VITAMIN_K -> vitaminK
        OptionalNutrient.FOLATE -> folate
        OptionalNutrient.OMEGA3 -> omega3
        OptionalNutrient.CAFFEINE -> caffeine
    }

    fun withValue(nutrient: OptionalNutrient, value: Int): OptionalNutrientGoals {
        val safe = value.coerceIn(0, nutrient.maxCustomGoal)
        return when (nutrient) {
            OptionalNutrient.SUGAR -> copy(sugar = safe)
            OptionalNutrient.ADDED_SUGAR -> copy(addedSugar = safe)
            OptionalNutrient.FIBER -> copy(fiber = safe)
            OptionalNutrient.SATURATED_FAT -> copy(saturatedFat = safe)
            OptionalNutrient.CHOLESTEROL -> copy(cholesterol = safe)
            OptionalNutrient.SODIUM -> copy(sodium = safe)
            OptionalNutrient.POTASSIUM -> copy(potassium = safe)
            OptionalNutrient.TRANS_FAT -> copy(transFat = safe)
            OptionalNutrient.CALCIUM -> copy(calcium = safe)
            OptionalNutrient.IRON -> copy(iron = safe)
            OptionalNutrient.MAGNESIUM -> copy(magnesium = safe)
            OptionalNutrient.ZINC -> copy(zinc = safe)
            OptionalNutrient.VITAMIN_A -> copy(vitaminA = safe)
            OptionalNutrient.VITAMIN_C -> copy(vitaminC = safe)
            OptionalNutrient.VITAMIN_D -> copy(vitaminD = safe)
            OptionalNutrient.VITAMIN_B12 -> copy(vitaminB12 = safe)
            OptionalNutrient.VITAMIN_E -> copy(vitaminE = safe)
            OptionalNutrient.VITAMIN_K -> copy(vitaminK = safe)
            OptionalNutrient.FOLATE -> copy(folate = safe)
            OptionalNutrient.OMEGA3 -> copy(omega3 = safe)
            OptionalNutrient.CAFFEINE -> copy(caffeine = safe)
        }
    }

    companion object {
        val Default = OptionalNutrientGoals()
    }
}
