local _, Orbit = ...
local DT = Orbit.Datatexts
local GameTooltip = Orbit.Tooltip
local Fmt = DT.Formatting
local RingBuffer = Fmt.RingBuffer
local L = Orbit.L

-- [ CONSTANTS ] -------------------------------------------------------------------------------------
local COPPER_PER_GOLD = 10000
local GRAPH_WIDTH = 200
local GRAPH_HEIGHT = 50
local GRAPH_OFFSET_Y = -5
local HISTORY_SIZE = 60
local HISTORY_INTERVAL_SEC = 60
local SECONDS_PER_HOUR = 3600
local SECONDS_PER_DAY = 86400
local BAG_COUNT = 4
local JUNK_QUALITY = 0
local MIN_HISTORY_POINTS = 2
local DAILY_HISTORY_DAYS = 7

-- [ DATATEXT ] --------------------------------------------------------------------------------------
local W = DT.BaseDatatext:New("Gold", L.PLU_DT_GOLD_NAME)

-- [ STATE ] -----------------------------------------------------------------------------------------
W.history = RingBuffer:New(HISTORY_SIZE)
W.sessionStart = 0
W.sessionStartTime = 0
W.lastHistoryTime = 0
W.autoSellEnabled = true

-- [ HELPERS ] ---------------------------------------------------------------------------------------
local function FormatProfit(profit)
    local color = profit > 0 and "|cff00ff00+" or (profit < 0 and "|cffff0000" or "|cffffffff")
    return color .. Fmt:FormatMoney(math.abs(profit)) .. "|r"
end

local function GetAcctStore()
    local acct = Orbit.db.AccountSettings
    acct.GoldAccountData = acct.GoldAccountData or {}
    acct.GoldDailyHistory = acct.GoldDailyHistory or {}
    return acct
end

local function GetAccountData()
    local acct = GetAcctStore()
    local result = {}
    for realm, chars in pairs(acct.GoldAccountData) do
        for name, data in pairs(chars) do
            result[#result + 1] = { name = name, realm = realm, class = data.class, level = data.level, gold = data.gold }
        end
    end
    table.sort(result, function(a, b) return a.gold > b.gold end)
    return result
end

-- [ CROSS-CHARACTER ] -------------------------------------------------------------------------------
function W:SaveCharacterGold(copper)
    local acct = GetAcctStore()
    local realm = GetRealmName()
    acct.GoldAccountData[realm] = acct.GoldAccountData[realm] or {}
    local name = UnitName("player")
    local _, class = UnitClass("player")
    acct.GoldAccountData[realm][name] = { gold = copper, class = class, level = UnitLevel("player") }
    self:UpdateDailyHistory(copper)
end

function W:UpdateDailyHistory(copper)
    local acct = GetAcctStore()
    local today = math.floor(time() / SECONDS_PER_DAY)
    local hist = acct.GoldDailyHistory
    if #hist == 0 or hist[#hist].day ~= today then
        hist[#hist + 1] = { day = today, gold = copper }
        if #hist > DAILY_HISTORY_DAYS then table.remove(hist, 1) end
    else
        hist[#hist].gold = copper
    end
end

function W:GetDailyDeltas()
    local hist = GetAcctStore().GoldDailyHistory
    local deltas = {}
    for i = 2, #hist do deltas[#deltas + 1] = { day = hist[i].day, delta = hist[i].gold - hist[i - 1].gold } end
    return deltas
end

-- [ UPDATE ] ----------------------------------------------------------------------------------------
function W:Update()
    local money = GetMoney()
    self:SetText(Fmt:FormatMoney(money))
    local t = GetTime()
    if t - self.lastHistoryTime > HISTORY_INTERVAL_SEC then
        self.history:Push(money)
        self.lastHistoryTime = t
    end
end

function W:OnMoneyChange()
    self:Update()
    self:SaveCharacterGold(GetMoney())
end

-- [ AUTO SELL ] -------------------------------------------------------------------------------------
function W:AutoSellJunk()
    if not self.autoSellEnabled then return end
    local profit = 0
    for bag = 0, BAG_COUNT do
        for slot = 1, C_Container.GetContainerNumSlots(bag) do
            local info = C_Container.GetContainerItemInfo(bag, slot)
            if info and info.quality == JUNK_QUALITY and not info.isLocked then
                local price = select(11, GetItemInfo(info.hyperlink))
                if price and price > 0 then C_Container.UseContainerItem(bag, slot); profit = profit + (price * info.stackCount) end
            end
        end
    end
    if profit > 0 then print("|cff00ff00" .. L.MSG_DT_AUTOSELL_F:format(Fmt:FormatMoney(profit)) .. "|r") end
end

-- [ CONTEXT MENU ] ----------------------------------------------------------------------------------
function W:GetMenuItems()
    return {
        { text = L.PLU_DT_GOLD_AUTOSELL, checked = self.autoSellEnabled, func = function() self.autoSellEnabled = not self.autoSellEnabled end, closeOnClick = false },
        { text = L.PLU_DT_GOLD_RESET_SESSION, func = function() self.sessionStart = GetMoney(); self.sessionStartTime = GetTime(); self.history:Clear(); self:Update() end },
    }
end

-- [ TOOLTIP ] ---------------------------------------------------------------------------------------
function W:ShowTooltip()
    GameTooltip:SetOwner(self.frame, "ANCHOR_TOP")
    GameTooltip:ClearLines()
    GameTooltip:AddLine(L.PLU_DT_GOLD_WEALTH_TITLE, 1, 0.82, 0)
    GameTooltip:AddLine(" ")
    local current = GetMoney()
    GameTooltip:AddDoubleLine(L.PLU_DT_GOLD_CURRENT, Fmt:FormatMoney(current), 1, 1, 1, 1, 1, 1)
    GameTooltip:AddDoubleLine(L.PLU_DT_GOLD_SESSION, FormatProfit(current - self.sessionStart), 1, 1, 1, 1, 1, 1)
    local elapsed = GetTime() - self.sessionStartTime
    if elapsed > 0 then
        GameTooltip:AddDoubleLine(L.PLU_DT_GOLD_PER_HOUR, FormatProfit((current - self.sessionStart) / elapsed * SECONDS_PER_HOUR), 1, 1, 1, 1, 1, 1)
    end
    local deltas = self:GetDailyDeltas()
    if #deltas > 0 then
        GameTooltip:AddLine(" ")
        GameTooltip:AddLine(L.PLU_DT_GOLD_DAILY_HISTORY, 0.7, 0.7, 0.7)
        for _, d in ipairs(deltas) do GameTooltip:AddDoubleLine(date("%m/%d", d.day * SECONDS_PER_DAY), FormatProfit(d.delta), 1, 1, 1, 1, 1, 1) end
    end
    local chars = GetAccountData()
    if #chars > 1 then
        GameTooltip:AddLine(" ")
        GameTooltip:AddLine(L.PLU_DT_GOLD_ACCOUNT, 0.7, 0.7, 0.7)
        local total = 0
        for _, char in ipairs(chars) do
            local cc = RAID_CLASS_COLORS[char.class]
            GameTooltip:AddDoubleLine(string.format("%s (%d)", char.name, char.level), Fmt:FormatMoney(char.gold), cc and cc.r or 1, cc and cc.g or 1, cc and cc.b or 1, 1, 1, 1)
            total = total + char.gold
        end
        GameTooltip:AddLine(" ")
        GameTooltip:AddDoubleLine(L.PLU_DT_GOLD_TOTAL, Fmt:FormatMoney(total), 1, 0.82, 0, 1, 1, 1)
    end
    GameTooltip:AddLine(" ")
    GameTooltip:AddDoubleLine(L.CMN_LEFT_CLICK, L.PLU_DT_BAG_OPEN_BAGS, 0.7, 0.7, 0.7, 1, 1, 1)
    GameTooltip:AddDoubleLine(L.CMN_RIGHT_CLICK, L.PLU_DT_GOLD_SETTINGS, 0.7, 0.7, 0.7, 1, 1, 1)
    GameTooltip:Show()
    -- Graph
    if self.history:Count() > MIN_HISTORY_POINTS then
        if not self.graphFrame then
            self.graphFrame = CreateFrame("Frame", nil, GameTooltip)
            self.graphFrame:SetSize(GRAPH_WIDTH, GRAPH_HEIGHT)
            self.graph = DT.Graph:New(self.graphFrame, GRAPH_WIDTH, GRAPH_HEIGHT)
        end
        self.graphFrame:SetParent(GameTooltip)
        self.graphFrame:SetPoint("TOP", GameTooltip, "BOTTOM", 0, GRAPH_OFFSET_Y)
        self.graphFrame:Show()
        self.graph:Clear()
        self.graph:SetColor(1, 0.84, 0, 1)
        for _, val in self.history:Iterate() do self.graph:AddData(val) end
        self.graph:Draw()
    elseif self.graphFrame then
        self.graphFrame:Hide()
    end
end

-- [ LIFECYCLE ] -------------------------------------------------------------------------------------
function W:Init()
    self:CreateFrame()
    self.sessionStart = GetMoney()
    self.sessionStartTime = GetTime()
    self.lastHistoryTime = GetTime()
    self:SetUpdateFunc(function() self:Update() end)
    self:SetTooltipFunc(function() self:ShowTooltip() end)
    self:SetClickFunc(function(_, btn) if btn == "RightButton" then self:ShowContextMenu() else ToggleAllBags() end end)
    self.leftClickHint = L.PLU_DT_BAG_OPEN_BAGS
    self.rightClickHint = L.PLU_DT_GOLD_SETTINGS
    self:RegisterEvent("PLAYER_MONEY", function() self:OnMoneyChange() end)
    self:RegisterEvent("PLAYER_ENTERING_WORLD", function() self:OnMoneyChange() end)
    self:RegisterEvent("MERCHANT_SHOW", function() self:AutoSellJunk() end)
    self:Register()
    self:Update()
end

W:Init()
