-- Bounty tab logic — methods added to TaskBoardController
local function formatPercent(value)
    if value * 10 == math.floor(value * 10) then
        return string.format('%.1f', value)
    else
        return string.format('%.2f', value)
    end
end

local function normalizeTalismanValues(index, currentRaw, nextRaw)
    local currentValue = tonumber(currentRaw) or 0
    local nextValue = tonumber(nextRaw) or 0

    -- Some payloads report level 0 as current=0 with a legacy next value.
    -- Normalize the level-0 baseline so UI always starts at 2.5/5.0%.
    if currentValue == 0 and nextValue > 0 then
        local baseCurrent = (index == 4) and 500 or 250
        local baseStep = (index == 4) and 100 or 50
        currentValue = baseCurrent
        if nextValue <= currentValue then
            nextValue = currentValue + baseStep
        end
    end

    return currentValue / 100, nextValue / 100
end

--  Server data handler 

function TaskBoardController:onBountyServerData(header, monsters, talisman)
    local rerollPoints = tonumber(header.rerollPoints) or 0
    local claimDaily = tonumber(header.claimDaily) or 0
    local difficulty = tonumber(header.difficulty) or 1

    self.claimDailyServerAvailable = claimDaily == 1
    self.rerollPoints = rerollPoints
    self.claimDailyAvailable = self.claimDailyServerAvailable and rerollPoints < REROLL_TOKEN_CAP
    self.claimDailyWarning = (rerollPoints >= REROLL_TOKEN_CAP)
    self.bountyDifficulty = difficulty

    -- Build bountyTasks table for *for loop in HTML
    local tasks = {}

    for _, m in ipairs(monsters) do
        local raceId = tonumber(m.raceId) or 0
        local currentKills = tonumber(m.currentKills) or 0
        local totalKills = tonumber(m.totalKills) or 0
        local rarity = tonumber(m.rarity) or 0
        local isActive = tonumber(m.isActive) == 1
        local isCompleted = tonumber(m.isCompleted) == 1
        local raceData = raceId > 0 and g_things.getRaceData(raceId) or nil
        local name = raceData and raceData.name or 'Unknown'
        name = name:capitalize()
        if #name > 20 then
            name = name:sub(1, 20) .. '...'
        end

        table.insert(tasks, {
            taskIndex = tonumber(m.taskIndex) or 0,
            raceId = raceId,
            name = name,
            outfit = raceData and raceData.outfit or nil,
            current = currentKills,
            total = totalKills,
            rarity = rarity,
            backdrop = RARITY_BACKDROPS[rarity] or RARITY_BACKDROPS[0],
            xp = comma_value(tonumber(m.rewardXp) or 0),
            points = tostring(tonumber(m.rewardPoints) or 0),
            reroll = tostring(tonumber(m.rewardReroll) or 0),
            isActive = isActive,
            isCompleted = isCompleted,
            canClaim = (not isCompleted) and isActive and currentKills >= totalKills
        })
    end

    self.bountyTasks = tasks
    self:syncBountyDifficultySelect()

    -- Build talismans table for *for loop
    local tals = {}
    for i = 1, 4 do
        local s = type(talisman) == 'table' and talisman[i] or nil
        if s then
            local currentValue, nextValue = normalizeTalismanValues(i, s.currentValue, s.nextValue)
            local upgradeCost = tonumber(s.upgradeCost) or 0
            local rawNextValue = tonumber(s.nextValue) or 0
            local isMaxed = rawNextValue == 0 and upgradeCost == 0
            table.insert(tals, {
                icon = TALISMAN_ICONS[i],
                title = TALISMAN_TITLES[i],
                current = string.format('Current: %s%%', formatPercent(currentValue)),
                buttonText = isMaxed and 'MAX' or string.format('Upgrade to %s%%', formatPercent(nextValue)),
                cost = isMaxed and '-' or tostring(upgradeCost),
                statType = i - 1,
                isMaxed = isMaxed,
                upgradeable = not isMaxed,
                showSeparator = i > 1
            })
        end
    end
    self.talismans = tals

    -- Update kill tracker
    self:updateBountyTracker(monsters)
end

--  Kill update 

function TaskBoardController:onBountyKillUpdate(raceId, currentKills, totalKills, isCompleted)
    -- Update tracker
    self:onBountyTrackerKillUpdate(raceId, currentKills, totalKills, isCompleted)

    -- Update the active task entry in bountyTasks if window is open
    for i, task in ipairs(self.bountyTasks) do
        if task.raceId == raceId then
            self.bountyTasks[i].current = currentKills
            self.bountyTasks[i].total = totalKills
            self.bountyTasks[i].isCompleted = currentKills >= totalKills
            self.bountyTasks[i].canClaim = (not self.bountyTasks[i].isCompleted) and task.isActive and currentKills >=
                                               totalKills
            -- Trigger reactive update by reassigning the table
            self.bountyTasks = self.bountyTasks
            break
        end
    end
end

--  Actions 

function TaskBoardController:rerollMonsters()
    if self.rerollPoints <= 0 then
        return
    end
    local msgBox
    local function yes()
        if msgBox then
            msgBox:destroy();
            msgBox = nil
        end
        g_game.bountyTaskAction(BOUNTY_ACTION_REROLL, 0)
    end
    local function cancel()
        if msgBox then
            msgBox:destroy();
            msgBox = nil
        end
    end
    msgBox = displayGeneralBox(tr('Reroll Tasks'),
        tr('Do you want to reroll your bounty tasks? This will consume 1 reroll token.'), {{
            text = tr('Yes'),
            callback = yes
        }, {
            text = tr('Cancel'),
            callback = cancel
        }}, yes, cancel)
end

function TaskBoardController:selectTask(taskIndex)
    local parsedTaskIndex = tonumber(taskIndex) or 0
    g_game.bountyTaskAction(BOUNTY_ACTION_SELECT, parsedTaskIndex)
end

function TaskBoardController:claimReward()
    local msgBox
    local function yes()
        if msgBox then
            msgBox:destroy();
            msgBox = nil
        end
        g_game.bountyTaskAction(BOUNTY_ACTION_CLAIM_REWARD, 0)
    end
    local function cancel()
        if msgBox then
            msgBox:destroy();
            msgBox = nil
        end
    end
    msgBox = displayGeneralBox(tr('Claim Reward'), tr('Claim reward for completing the bounty task?'), {{
        text = tr('Yes'),
        callback = yes
    }, {
        text = tr('Cancel'),
        callback = cancel
    }}, yes, cancel)
end

function TaskBoardController:changeDifficulty(event)
    -- event.data = selected option data (difficulty id set when populating the combobox)
    local diffId = event and (tonumber(event.data) or tonumber(event.value)) or nil
    if not diffId then
        return
    end
    g_game.bountyTaskAction(BOUNTY_ACTION_CHANGE_DIFFICULTY, diffId)
end

function TaskBoardController:claimDaily()
    if self.claimDailyWarning then
        local msgBox
        local function yes()
            if msgBox then
                msgBox:destroy();
                msgBox = nil
            end
            g_game.bountyTaskAction(BOUNTY_ACTION_CLAIM_DAILY, 0)
        end
        local function cancel()
            if msgBox then
                msgBox:destroy();
                msgBox = nil
            end
        end
        msgBox = displayGeneralBox(tr('Reroll Token Cap'), tr(
            'You already have 10 reroll tokens. Claiming another will discard the oldest. Continue?'), {{
            text = tr('Yes'),
            callback = yes
        }, {
            text = tr('Cancel'),
            callback = cancel
        }}, yes, cancel)
    else
        g_game.bountyTaskAction(BOUNTY_ACTION_CLAIM_DAILY, 0)
    end
end

function TaskBoardController:upgradeTalisman(statType)
    g_game.bountyTalismanUpgrade(statType)
end

--  Tracker helpers 

function TaskBoardController:updateBountyTracker(monsters)
    if not Tracker or not Tracker.Bounty then
        return
    end
    local activeMonster = nil
    for _, m in ipairs(monsters) do
        if tonumber(m.isActive) == 1 then
            activeMonster = m
            break
        end
    end
    if not activeMonster then
        Tracker.Bounty.setInactive()
        return
    end
    local raceId = tonumber(activeMonster.raceId) or 0
    local currentKills = tonumber(activeMonster.currentKills) or 0
    local totalKills = tonumber(activeMonster.totalKills) or 0
    local isCompleted = currentKills >= totalKills and 1 or 0
    self:onBountyTrackerKillUpdate(raceId, currentKills, totalKills, isCompleted)
end

function TaskBoardController:onBountyTrackerKillUpdate(raceId, currentKills, totalKills, isCompleted)
    if not Tracker or not Tracker.Bounty then
        return
    end
    Tracker.Bounty.onKillUpdate(raceId, currentKills, totalKills, isCompleted)
end
