local _, ns = ...

------------------------------------------------------------
-- Recommendation labels and colors
------------------------------------------------------------
ns.RECOMMEND = {
    UPGRADE_NOW       = { label = "Upgrade now",            color = "ff00ff00" }, -- green
    UPGRADE_LATER     = { label = "Upgrade later",          color = "ffffff00" }, -- yellow
    HOLD_CRESTS       = { label = "Hold crests",            color = "ffff8800" }, -- orange
    SAFE_TEMP         = { label = "Safe temporary upgrade", color = "ff88bbff" }, -- light blue
    BAD_INVESTMENT    = { label = "Bad investment",          color = "ffff0000" }, -- red
    CRAFT_INSTEAD     = { label = "Craft instead",           color = "ffcc66ff" }, -- purple
    WAIT_BETTER       = { label = "Wait for better source",  color = "ffaaaaaa" }, -- grey
    MAXED             = { label = "Max rank",                color = "ff888888" }, -- dark grey
    NO_ITEM           = { label = "",                        color = "ff555555" },
}

------------------------------------------------------------
-- Crest cost system
------------------------------------------------------------
-- Base cost per upgrade
ns.BASE_CREST_COST = 20
-- Discounted cost (high-watermark achievement)
ns.DISCOUNTED_CREST_COST = 10

-- Account-wide achievement IDs for 50% crest cost discount (Midnight S1)
-- When unlocked, upgrades of that crest type cost 10 instead of 20 on alts
ns.DISCOUNT_ACHIEVEMENTS = {
    Adventurer = 61809, -- Adventurer of the Dawn
    Veteran    = 42767, -- Veteran of the Dawn
    Champion   = 42768, -- Champion of the Dawn
    Hero       = 42769, -- Hero of the Dawn
    Myth       = 42770, -- Myth of the Dawn
}

-- Cache achievement status (checked once per session)
local discountCache = {}

function ns:HasDiscountAchievement(crestTrack)
    if discountCache[crestTrack] ~= nil then
        return discountCache[crestTrack]
    end

    local achieveID = ns.DISCOUNT_ACHIEVEMENTS[crestTrack]
    if achieveID then
        local ok, _, _, _, completed = pcall(GetAchievementInfo, achieveID)
        if ok and completed then
            discountCache[crestTrack] = true
            return true
        end
    end

    discountCache[crestTrack] = false
    return false
end

-- Clear cache (call on login/reload if needed)
function ns:ClearDiscountCache()
    wipe(discountCache)
end

function ns:GetCrestCost(crestTrack)
    -- Manual override first (user can toggle via /yyh discount)
    local db = YippYappHelperDB or {}
    local discounts = db.discounts or {}

    if discounts[crestTrack] then
        return ns.DISCOUNTED_CREST_COST
    end

    -- Check account-wide achievement
    if ns:HasDiscountAchievement(crestTrack) then
        return ns.DISCOUNTED_CREST_COST
    end

    return ns.BASE_CREST_COST
end

-- Is the discount missing but potentially coming from the player's main?
-- Used in Alt mode to decide whether to hold spending
function ns:IsDiscountPending(crestTrack)
    local profile = ns:GetCurrentProfile()
    if profile.id ~= "alt" then return false end

    -- If discount already unlocked, not pending
    if ns:HasDiscountAchievement(crestTrack) then return false end

    -- Manual override means player already has it
    local db = YippYappHelperDB or {}
    local discounts = db.discounts or {}
    if discounts[crestTrack] then return false end

    -- In alt mode, lower-value crests are less worth waiting for
    -- Only flag as pending for precious crests
    return ns:IsCrestPrecious(crestTrack)
end

-- How many upgrades can the player afford for a given crest track?
function ns:GetAffordableUpgrades(crestTrack)
    local count = ns:GetCrestCountByTrack(crestTrack)
    local cost = ns:GetCrestCost(crestTrack)
    if cost == 0 then return 99 end
    return math.floor(count / cost)
end

-- Count how many items still need upgrades in a given crest track
-- Returns: remaining slots, total crest cost to finish
function ns:GetTrackCompletionInfo(crestTrack)
    local remaining = 0
    local totalCost = 0
    local cost = ns:GetCrestCost(crestTrack)

    for _, slotInfo in ipairs(ns.SLOT_IDS) do
        local canUpgrade, upgradeInfo = ns:CanUpgradeItem(slotInfo.slot)
        if upgradeInfo then
            local track = upgradeInfo.track
            local itemCrestTrack = ns.TRACK_CREST[track]
            if itemCrestTrack == crestTrack and canUpgrade then
                remaining = remaining + 1
                local upgradesLeft = upgradeInfo.maxUpgrade - upgradeInfo.currUpgrade
                totalCost = totalCost + (upgradesLeft * cost)
            end
        end
    end

    return remaining, totalCost
end

------------------------------------------------------------
-- Replacement risk assessment
------------------------------------------------------------
function ns:GetReplacementRisk(slotID, ilvl)
    local profile = ns:GetCurrentProfile()

    local farmableIlvl = 0
    local vaultIlvl = 0

    -- M+ loot ceiling based on profile key level
    for _, entry in ipairs(ns.DUNGEON_LOOT) do
        local keyNum = tonumber(entry.key:match("M(%d+)"))
        if keyNum and keyNum <= profile.maxKeyLevel then
            if entry.loot > farmableIlvl then farmableIlvl = entry.loot end
            if entry.vault and entry.vault > vaultIlvl then vaultIlvl = entry.vault end
        end
    end

    -- Raid ceiling
    local raidTrack = ns.RAID_TRACKS[profile.raidTier]
    if raidTrack then
        local raidMax = ns:GetMaxIlvlForTrack(raidTrack)
        if raidMax > farmableIlvl then farmableIlvl = raidMax end
    end

    local ceiling = math.max(farmableIlvl, vaultIlvl)

    if ceiling == 0 then
        return "low"
    end

    local gap = ceiling - ilvl

    if gap >= 20 then
        return "high"
    elseif gap >= 10 then
        return "moderate"
    else
        return "low"
    end
end

------------------------------------------------------------
-- Core recommendation engine
------------------------------------------------------------
function ns:GetRecommendation(slotID)
    local info = ns:GetSlotInfo(slotID)
    if not info then
        return ns.RECOMMEND.NO_ITEM, ""
    end

    local canUpgrade, upgradeInfo = ns:CanUpgradeItem(slotID)

    if not upgradeInfo then
        return ns.RECOMMEND.NO_ITEM, ""
    end

    if not canUpgrade then
        return ns.RECOMMEND.MAXED, "Fully upgraded"
    end

    local ilvl = info.ilvl
    local track = upgradeInfo.track or ns:GetTrackFromIlvl(ilvl)
    local crestTrack = ns.TRACK_CREST[track]
    local slotPriority = ns.SLOT_PRIORITY[slotID] or 2
    local replacementRisk = ns:GetReplacementRisk(slotID, ilvl)
    local isFree = ns:IsCrestFree(crestTrack)
    local isPrecious = ns:IsCrestPrecious(crestTrack)
    local trackRank = ns.TRACK_RANK[track] or 1
    local rank = upgradeInfo.currUpgrade
    local maxRank = upgradeInfo.maxUpgrade

    local crestCost = ns:GetCrestCost(crestTrack)
    local affordable = ns:GetAffordableUpgrades(crestTrack)
    local crestCount = ns:GetCrestCountByTrack(crestTrack)
    local upgradesNeeded = maxRank - rank
    local totalCost = upgradesNeeded * crestCost
    local costStr = crestCost .. " " .. crestTrack .. " per rank"
    -- ============================================================
    -- RULE 1: Item at track cap, next track exists
    -- ============================================================
    if rank == maxRank and trackRank < #ns.TRACK_ORDER then
        local nextTrack = ns.TRACK_ORDER[trackRank + 1]
        return ns.RECOMMEND.WAIT_BETTER,
            "At " .. track .. " cap — need " .. nextTrack .. " drops"
    end

    -- ============================================================
    -- RULE 2: Alt mode — discount achievement not yet unlocked
    -- ============================================================
    if ns:IsDiscountPending(crestTrack) then
        if slotPriority >= 4 and replacementRisk == "low" then
            return ns.RECOMMEND.SAFE_TEMP,
                "Alt: 2x cost without " .. crestTrack .. " of the Dawn — OK for high-value slot"
        else
            return ns.RECOMMEND.HOLD_CRESTS,
                "Alt: Wait for " .. crestTrack .. " of the Dawn (50% off)"
        end
    end

    -- ============================================================
    -- RULE 3: Precious crest savings via lower-tier upgrade path
    -- For Hero/Myth items at rank 1, advise upgrading a lower-tier
    -- item in the same slot to 6/6 first. The auto-promotion gives
    -- the first 2 ranks of the higher tier for free.
    -- Special case: Hero→Myth crest conversion is especially
    -- valuable since Hero crests become surplus mid-season.
    -- ============================================================
    if isPrecious and trackRank >= 2 and rank == 1 then
        local prevTrack = ns.TRACK_ORDER[trackRank - 1]
        local prevCrestTrack = ns.TRACK_CREST[prevTrack]
        local prevCrestCount = ns:GetCrestCountByTrack(prevCrestTrack)
        local prevCrestCost = ns:GetCrestCost(prevCrestTrack)

        if track == "Myth" then
            return ns.RECOMMEND.UPGRADE_LATER,
                "Upgrade Hero item here to 6/6 first — converts " .. prevCrestCost ..
                " Hero into a free Myth rank (" .. prevCrestCount .. " Hero available)"
        elseif track == "Hero" then
            return ns.RECOMMEND.UPGRADE_LATER,
                "Upgrade Champion item here to 6/6 first — free Hero rank (" ..
                prevCrestCount .. " Champion available)"
        end
    end

    -- ============================================================
    -- RULE 4: Champion gear — much weaker this season
    -- ============================================================
    if track == "Champion" then
        if rank == maxRank then
            return ns.RECOMMEND.WAIT_BETTER,
                "Champion caps at 259 — need Hero drops"
        end
        if isFree then
            if slotPriority >= 4 then
                return ns.RECOMMEND.UPGRADE_NOW,
                    "Free crests — caps low (259), replace with Hero when possible"
            else
                return ns.RECOMMEND.UPGRADE_LATER,
                    "Low slot, Champion caps at 259 — upgrade last"
            end
        elseif isPrecious then
            return ns.RECOMMEND.HOLD_CRESTS,
                "Save " .. crestTrack .. " for Hero+ items (Champion caps at 259)"
        end
    end

    -- ============================================================
    -- RULE 4.5: Achievement completion priority
    -- ============================================================
    if not ns:HasDiscountAchievement(crestTrack) then
        local remaining, costToFinish = ns:GetTrackCompletionInfo(crestTrack)
        if remaining > 0 and remaining <= 3 then
            if remaining == 1 then
                return ns.RECOMMEND.UPGRADE_NOW,
                    "Last " .. crestTrack .. " item! Unlocks 50% off for alts (" .. totalCost .. " crests)"
            else
                return ns.RECOMMEND.UPGRADE_NOW,
                    remaining .. " " .. crestTrack .. " items left for 50% alt discount (" .. costToFinish .. " crests total)"
            end
        end
    end

    -- ============================================================
    -- RULE 5: Free crests — spend freely, just prioritize by slot
    -- ============================================================
    local affordStr = affordable >= upgradesNeeded
        and " — " .. totalCost .. " crests"
        or " — " .. crestCount .. "/" .. totalCost .. " crests"

    if isFree then
        if slotPriority >= 4 then
            return ns.RECOMMEND.UPGRADE_NOW, "Spend freely" .. affordStr
        else
            return ns.RECOMMEND.UPGRADE_NOW, "Low slot, crests are cheap" .. affordStr
        end
    end

    -- ============================================================
    -- RULE 6: Precious crest + item will be replaced soon
    -- ============================================================
    if isPrecious and replacementRisk == "high" then
        return ns.RECOMMEND.BAD_INVESTMENT,
            "Will be replaced by farmable drops"
    end

    -- ============================================================
    -- RULE 7: Precious crest + low priority slot
    -- ============================================================
    if isPrecious and slotPriority <= 2 then
        if replacementRisk == "moderate" then
            return ns.RECOMMEND.BAD_INVESTMENT,
                "Low slot — spend " .. crestTrack .. " on weapons/chest/legs first"
        else
            return ns.RECOMMEND.UPGRADE_LATER,
                "Low slot — save " .. crestTrack .. " for high-value slots"
        end
    end

    -- ============================================================
    -- RULE 8: Precious crest + high priority + strong staying power
    -- ============================================================
    if isPrecious and slotPriority >= 4 and replacementRisk == "low" then
        return ns.RECOMMEND.UPGRADE_NOW,
            "High value, won't be replaced" .. affordStr
    end

    -- ============================================================
    -- RULE 9: Precious crest + mid priority + low risk
    -- ============================================================
    if isPrecious and slotPriority >= 3 and replacementRisk == "low" then
        return ns.RECOMMEND.UPGRADE_LATER,
            "Good slot — after weapons/chest/legs/helm" .. affordStr
    end

    -- ============================================================
    -- RULE 10: Precious crest + moderate replacement risk
    -- ============================================================
    if isPrecious and replacementRisk == "moderate" then
        if slotPriority >= 4 then
            return ns.RECOMMEND.SAFE_TEMP,
                "High value but may be replaced soon"
        else
            return ns.RECOMMEND.HOLD_CRESTS,
                "May be replaced — " .. crestTrack .. " crests are valuable"
        end
    end

    -- ============================================================
    -- FALLBACK
    -- ============================================================
    return ns.RECOMMEND.SAFE_TEMP,
        "Safe to upgrade" .. affordStr
end

------------------------------------------------------------
-- Batch: get recommendations for all slots
------------------------------------------------------------
function ns:GetAllRecommendations()
    local results = {}
    for _, slotInfo in ipairs(ns.SLOT_IDS) do
        local rec, reason = ns:GetRecommendation(slotInfo.slot)
        results[slotInfo.slot] = {
            recommendation = rec,
            reason = reason,
            slotName = slotInfo.name,
            slotID = slotInfo.slot,
        }
    end
    return results
end
