local _, addon = ...
local HLY = addon.HLY

local CreateFrame = CreateFrame
local GetMoney = GetMoney
local GetNormalizedRealmName = GetNormalizedRealmName
local GetRealmName = GetRealmName
local RequestTimePlayed = RequestTimePlayed
local UnitClass = UnitClass
local UnitFullName = UnitFullName
local UnitLevel = UnitLevel
local floor = math.floor
local pairs = pairs
local string = string
local table = table
local sort = table.sort
local strrep = string.rep
local time = time
local tonumber = tonumber
local tostring = tostring

local statisticsFrame = CreateFrame("Frame")

local CLASS_FALLBACKS = {
    DEATHKNIGHT = "Death Knight",
    DEMONHUNTER = "Demon Hunter",
    DRUID = "Druid",
    EVOKER = "Evoker",
    HUNTER = "Hunter",
    MAGE = "Mage",
    MONK = "Monk",
    PALADIN = "Paladin",
    PRIEST = "Priest",
    ROGUE = "Rogue",
    SHAMAN = "Shaman",
    WARLOCK = "Warlock",
    WARRIOR = "Warrior",
}

local function GroupNumber(value)
    value = tostring(floor(tonumber(value) or 0))
    local result = value

    while true do
        local nextResult, replacements = string.gsub(result, "^(-?%d+)(%d%d%d)", "%1.%2")
        result = nextResult
        if replacements == 0 then
            break
        end
    end

    return result
end

local function FormatMoney(copper)
    copper = tonumber(copper) or 0
    if copper < 0 then
        copper = 0
    end

    local gold = floor(copper / 10000)
    copper = copper - (gold * 10000)
    local silver = floor(copper / 100)
    copper = copper - (silver * 100)

    return string.format("%s|TInterface\\MoneyFrame\\UI-GoldIcon:0:0:2:0|t %02d|TInterface\\MoneyFrame\\UI-SilverIcon:0:0:2:0|t %02d|TInterface\\MoneyFrame\\UI-CopperIcon:0:0:2:0|t",
        GroupNumber(gold),
        silver,
        copper)
end

local function WrapClassColor(self, classFile, text)
    local color = self:GetClassColor(classFile)
    if not color then
        return text
    end

    local r = floor((color.r or 1) * 255)
    local g = floor((color.g or 1) * 255)
    local b = floor((color.b or 1) * 255)

    return string.format("|cff%02x%02x%02x%s|r", r, g, b, text)
end

local function FormatDuration(seconds)
    seconds = tonumber(seconds) or 0
    if seconds < 0 then
        seconds = 0
    end

    local days = floor(seconds / 86400)
    seconds = seconds - (days * 86400)
    local hours = floor(seconds / 3600)
    seconds = seconds - (hours * 3600)
    local minutes = floor(seconds / 60)

    if days > 0 then
        return string.format("%dd %dh %dm", days, hours, minutes)
    end
    if hours > 0 then
        return string.format("%dh %dm", hours, minutes)
    end

    return string.format("%dm", minutes)
end

local function FormatClassBar(self, classFile, played, maxPlayed)
    local width = 28
    played = tonumber(played) or 0
    maxPlayed = tonumber(maxPlayed) or 0

    local filled = 0
    if played > 0 and maxPlayed > 0 then
        filled = floor((played / maxPlayed) * width + 0.5)
        if filled < 1 then
            filled = 1
        elseif filled > width then
            filled = width
        end
    end

    local filledText = strrep("=", filled)
    local emptyText = strrep("-", width - filled)

    return string.format("%s|cff252525%s|r",
        WrapClassColor(self, classFile, filledText),
        emptyText)
end

local function GetCurrentCharacterKey()
    local name, realm = UnitFullName("player")
    if not name or name == "" then
        return nil
    end

    realm = realm or (GetNormalizedRealmName and GetNormalizedRealmName()) or GetRealmName()
    realm = realm or "Unknown"
    return realm .. ":" .. name
end

local function GetWarbankMoney()
    if C_Bank and C_Bank.FetchDepositedMoney and Enum and Enum.BankType and Enum.BankType.Account then
        local ok, money = pcall(C_Bank.FetchDepositedMoney, Enum.BankType.Account)
        if ok and money ~= nil then
            return tonumber(money) or 0
        end
    end

    return nil
end

local function GetCurrentCharacterInfo()
    local localizedClass, classFile = UnitClass("player")
    classFile = classFile or "UNKNOWN"

    local name, realm = UnitFullName("player")
    realm = realm or (GetNormalizedRealmName and GetNormalizedRealmName()) or GetRealmName()

    return {
        name = name or "Unknown",
        realm = realm or "Unknown",
        classFile = classFile,
        className = localizedClass or CLASS_FALLBACKS[classFile] or classFile,
        level = UnitLevel("player") or 0,
    }
end

function HLY:EnsureStatisticsDefaults()
    if not self.database then
        return nil
    end

    self.database.global = self.database.global or {}
    self.database.global.statistics = self.database.global.statistics or {}

    local statistics = self.database.global.statistics
    statistics.characters = statistics.characters or {}
    statistics.goldByCharacter = statistics.goldByCharacter or {}
    statistics.version = statistics.version or 1

    return statistics
end

function HLY:GetStatisticsCharacter()
    local statistics = self:EnsureStatisticsDefaults()
    local key = GetCurrentCharacterKey()
    if not statistics or not key then
        return nil
    end

    local character = statistics.characters[key]
    if not character then
        character = {
            totalTime = 0,
            levelTime = 0,
        }
        statistics.characters[key] = character
    end

    local info = GetCurrentCharacterInfo()
    character.name = info.name
    character.realm = info.realm
    character.classFile = info.classFile
    character.className = info.className
    character.level = info.level
    character.lastSeen = time()

    return character
end

function HLY:RefreshStatisticsGold()
    local statistics = self:EnsureStatisticsDefaults()
    local character = self:GetStatisticsCharacter()

    if character and GetMoney then
        local money = self:SafeNumber(GetMoney())
        local previousMoney = self:SafeNumber(character.money)
        if money <= 0 and previousMoney > 0 then
            money = previousMoney
        end

        character.money = money
        character.lastMoneyUpdate = time()
        character.moneyCollected = true

        local key = GetCurrentCharacterKey()
        if key then
            statistics.goldByCharacter = statistics.goldByCharacter or {}
            statistics.goldByCharacter[key] = money
        end
    end

    if statistics then
        local warbankMoney = GetWarbankMoney()
        if warbankMoney ~= nil then
            local previousWarbankMoney = self:SafeNumber(statistics.warbankMoney)
            if warbankMoney <= 0 and previousWarbankMoney > 0 then
                warbankMoney = previousWarbankMoney
            end

            statistics.warbankMoney = warbankMoney
            statistics.warbankMoneyCollected = true
        end
        statistics.lastMoneyUpdate = time()
        self:RefreshStatisticsGoldTotals()
    end
end

function HLY:RefreshStatisticsGoldTotals()
    local statistics = self:EnsureStatisticsDefaults()
    if not statistics then
        return 0, 0, 0
    end

    local characterMoney = 0
    local allCharactersMoney = 0
    local currentKey = GetCurrentCharacterKey()
    statistics.goldByCharacter = statistics.goldByCharacter or {}

    -- Migrate old per-character money into the dedicated gold map.
    for key, character in pairs(statistics.characters) do
        if character.money ~= nil then
            statistics.goldByCharacter[key] = self:SafeNumber(character.money)
        end
    end

    for key, money in pairs(statistics.goldByCharacter) do
        money = self:SafeNumber(money)
        allCharactersMoney = allCharactersMoney + money

        if currentKey and key == currentKey then
            characterMoney = money
        end
    end

    local warbankMoney = statistics.warbankMoneyCollected and self:SafeNumber(statistics.warbankMoney) or 0
    local totalMoney = allCharactersMoney + warbankMoney

    statistics.characterMoney = characterMoney
    statistics.allCharactersMoney = allCharactersMoney
    statistics.totalMoney = totalMoney

    return characterMoney, warbankMoney, totalMoney
end

function HLY:RecordStatisticsPlayedTime(totalTime, levelTime)
    totalTime = self:SafeNumber(totalTime)
    levelTime = self:SafeNumber(levelTime)

    local character = self:GetStatisticsCharacter()
    if not character then
        return
    end

    character.totalTime = totalTime
    character.levelTime = levelTime
    character.lastPlayedUpdate = time()
end

function HLY:CommitStatisticsCurrentSession()
    local character = self:GetStatisticsCharacter()
    if not character or not character.lastPlayedUpdate then
        return
    end

    local now = time()
    local elapsed = now - self:SafeNumber(character.lastPlayedUpdate)
    if elapsed > 0 then
        character.totalTime = self:SafeNumber(character.totalTime) + elapsed
        character.levelTime = self:SafeNumber(character.levelTime) + elapsed
    end

    character.lastPlayedUpdate = nil
    character.lastSeen = now
end

function HLY:RefreshStatisticsCurrentCharacter()
    self:GetStatisticsCharacter()
    self:RefreshStatisticsGold()

    if RequestTimePlayed then
        RequestTimePlayed()
    end
end

function HLY:GetStatisticsCharacterPlayedSeconds(character)
    if not character then
        return 0
    end

    local total = self:SafeNumber(character.totalTime)
    local currentKey = GetCurrentCharacterKey()
    local characterKey = character.realm and character.name and (character.realm .. ":" .. character.name)

    if currentKey and characterKey == currentKey and character.lastPlayedUpdate then
        local elapsed = time() - self:SafeNumber(character.lastPlayedUpdate)
        if elapsed > 0 then
            total = total + elapsed
        end
    end

    return total
end

function HLY:GetStatisticsGoldSummary()
    local statistics = self:EnsureStatisticsDefaults()

    if not statistics then
        return 0, 0, 0
    end

    local characterMoney, warbankMoney, totalMoney = self:RefreshStatisticsGoldTotals()

    return characterMoney, warbankMoney, totalMoney
end

function HLY:GetStatisticsSummary()
    local statistics = self:EnsureStatisticsDefaults()
    local classTotals = {}
    local totalPlayed = 0
    local characterCount = 0

    if not statistics then
        return totalPlayed, {}, characterCount
    end

    for _, character in pairs(statistics.characters) do
        local played = self:GetStatisticsCharacterPlayedSeconds(character)
        local hasPlayedData = played > 0 or character.classFile ~= nil
        if hasPlayedData then
            totalPlayed = totalPlayed + played
            characterCount = characterCount + 1

            local classFile = character.classFile or "UNKNOWN"
            local classData = classTotals[classFile]
            if not classData then
                classData = {
                    classFile = classFile,
                    className = character.className or CLASS_FALLBACKS[classFile] or classFile,
                    played = 0,
                    characters = 0,
                }
                classTotals[classFile] = classData
            end

            classData.played = classData.played + played
            classData.characters = classData.characters + 1
        end
    end

    local topClasses = {}
    for _, classData in pairs(classTotals) do
        topClasses[#topClasses + 1] = classData
    end

    sort(topClasses, function(a, b)
        if a.played == b.played then
            return tostring(a.className) < tostring(b.className)
        end
        return a.played > b.played
    end)

    return totalPlayed, topClasses, characterCount
end

function HLY:GetStatisticsPlayedSummaryText()
    local totalPlayed, classTotals, characterCount = self:GetStatisticsSummary()
    local lines = {
        "|cffffd100Gesamtspielzeit:|r " .. FormatDuration(totalPlayed),
        "|cffffd100Charaktere:|r " .. tostring(characterCount),
        " ",
    }

    if #classTotals == 0 then
        lines[#lines + 1] = "Noch keine Daten. Logge weitere Charaktere ein, um sie zu erfassen."
    else
        local maxPlayed = classTotals[1] and classTotals[1].played or 0
        for index, classData in ipairs(classTotals) do
            local className = classData.className or classData.classFile or "Unknown"
            className = WrapClassColor(self, classData.classFile, className)
            lines[#lines + 1] = string.format("%s  |cffffffff%s|r (%d Char%s)",
                className,
                FormatDuration(classData.played),
                classData.characters or 0,
                classData.characters == 1 and "" or "s")
            lines[#lines + 1] = "    " .. FormatClassBar(self, classData.classFile, classData.played, maxPlayed)
        end
    end

    return table.concat(lines, "\n")
end

function HLY:GetStatisticsGoldSummaryText()
    local characterMoney, warbankMoney, totalMoney = self:GetStatisticsGoldSummary()
    local lines = {
        "|cffffd100Gold|r",
        " ",
        "|cffffd100Character:|r " .. FormatMoney(characterMoney),
        "|cffffd100Warbank:|r " .. FormatMoney(warbankMoney),
        "|cffffd100Total:|r " .. FormatMoney(totalMoney),
    }

    return table.concat(lines, "\n")
end

function HLY:GetStatisticsSummaryText()
    return self:GetStatisticsPlayedSummaryText() .. "\n\n" .. self:GetStatisticsGoldSummaryText()
end

function HLY:ResetStatisticsData()
    local statistics = self:EnsureStatisticsDefaults()
    if not statistics then
        return
    end

    for key in pairs(statistics) do
        statistics[key] = nil
    end

    statistics.characters = {}
    statistics.goldByCharacter = {}
    statistics.version = 1
    statistics.characterMoney = 0
    statistics.allCharactersMoney = 0
    statistics.totalMoney = 0

    self:RefreshStatisticsCurrentCharacter()
end

function HLY:HandleStatisticsEvent(event, ...)
    if event == "TIME_PLAYED_MSG" then
        self:RecordStatisticsPlayedTime(...)
    elseif event == "PLAYER_LOGOUT" then
        self:RefreshStatisticsGold()
        self:CommitStatisticsCurrentSession()
    elseif event == "PLAYER_MONEY" or event == "ACCOUNT_MONEY" then
        self:RefreshStatisticsGold()
    else
        self:RefreshStatisticsCurrentCharacter()
    end
end

function HLY:SetupStatistics()
    if self.statisticsSetup then
        return
    end

    self:EnsureStatisticsDefaults()

    statisticsFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
    statisticsFrame:RegisterEvent("TIME_PLAYED_MSG")
    statisticsFrame:RegisterEvent("PLAYER_LEVEL_UP")
    statisticsFrame:RegisterEvent("PLAYER_MONEY")
    statisticsFrame:RegisterEvent("ACCOUNT_MONEY")
    statisticsFrame:RegisterEvent("PLAYER_LOGOUT")
    statisticsFrame:SetScript("OnEvent", function(_, event, ...)
        self:HandleStatisticsEvent(event, ...)
    end)

    self.statisticsSetup = true
end

-- LibHLYUI options pane (v2) ------------------------------------------------
function HLY:BuildStatisticsOptions(tab)
    self:EnsureStatisticsDefaults()
    tab:TabGroup({
        {
            name = "Played Time",
            build = function(p)
                local total, classTotals, charCount = self:GetStatisticsSummary()
                p:Note("Gesamtspielzeit " .. FormatDuration(total) .. "  ·  " .. tostring(charCount) .. " Charaktere")
                if not classTotals or #classTotals == 0 then
                    p:Note("Noch keine Daten. Logge weitere Charaktere ein.")
                else
                    local maxP = (classTotals[1] and classTotals[1].played) or 1
                    for _, cd in ipairs(classTotals) do
                        local name = WrapClassColor(self, cd.classFile, cd.className or cd.classFile or "Unknown")
                        p:Bar({
                            label = name,
                            value = cd.played, max = maxP,
                            valueText = FormatDuration(cd.played),
                            color = self:GetClassColor(cd.classFile),
                        })
                    end
                end
                p:Button({ label = "Reset Data", func = function() self:ResetStatisticsData() end })
            end,
        },
        {
            name = "Gold",
            build = function(p)
                local charMoney, warbankMoney, totalMoney = self:GetStatisticsGoldSummary()
                local maxG = math.max(totalMoney or 0, 1)
                p:Bar({ label = "Character", value = charMoney, max = maxG, valueText = FormatMoney(charMoney), valueWidth = 130 })
                p:Bar({ label = "Warbank", value = warbankMoney, max = maxG, valueText = FormatMoney(warbankMoney), valueWidth = 130 })
                p:Bar({ label = "Total", value = totalMoney, max = maxG, valueText = FormatMoney(totalMoney), valueWidth = 130 })
                p:Button({ label = "Reset Data", func = function() self:ResetStatisticsData() end })
            end,
        },
    })
end
