local ADDON_NAME, ns = ...
ns = ns or {}
local Const = ns.Const
local Util = ns.Util
local Panel = ns.Panel
local DEFAULT_HEADER = "ACA: Top 5"
local STRATA_ORDER = { BACKGROUND = 1, LOW = 2, MEDIUM = 3, HIGH = 4, DIALOG = 5, FULLSCREEN = 6, FULLSCREEN_DIALOG = 7, TOOLTIP = 8 }
local function getHigherStrata(strata)
    local order = STRATA_ORDER[strata or "MEDIUM"] or STRATA_ORDER.MEDIUM
    for k, v in pairs(STRATA_ORDER) do
        if v == order + 1 then return k end
    end
    return "TOOLTIP"
end

local Tracker = {}
ns.Tracker = Tracker

local function resolveParent(parentName)
    if type(parentName) == "string" then
        local p = _G[parentName]
        if p and p.GetObjectType then
            return p
        end
    end
    return UIParent
end

local function restorePosition(frame)
    local point = (ACAU_Global and ACAU_Global.positionAccountWide and ACAU_Global.top5Point) or (ACAU_Top5DB and ACAU_Top5DB.point)
    if type(point) ~= "table" then
        frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
        return
    end
    local anchor, parentName, relPoint, x, y = point[1], point[2], point[3], point[4], point[5]
    if not anchor or not relPoint then
        frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
        return
    end
    frame:ClearAllPoints()
    frame:SetPoint(anchor, resolveParent(parentName), relPoint, x or 0, y or 0)
end

local function savePosition(frame)
    local a1, parent, a2, x, y = frame:GetPoint()
    local parentName = (parent and parent.GetName and parent:GetName()) or "UIParent"
    if ACAU_Global and ACAU_Global.positionAccountWide then
        ACAU_Global.top5Point = { a1 or "CENTER", parentName, a2 or "CENTER", x or 0, y or 0 }
    elseif ACAU_Top5DB then
        ACAU_Top5DB.point = { a1 or "CENTER", parentName, a2 or "CENTER", x or 0, y or 0 }
    end
end

local function applyClickThrough(frame)
    local clickThrough = ACAU_Top5DB and ACAU_Top5DB.clickThrough
    if frame then
        frame:EnableMouse(true)
        if frame.SetPropagateMouseClicks then frame:SetPropagateMouseClicks(clickThrough) end
        if frame.SetPropagateMouseMotion then frame:SetPropagateMouseMotion(clickThrough) end
    end
    if frame and frame.rows then
        for _, row in ipairs(frame.rows) do
            row:EnableMouse(true)
            if row.SetPropagateMouseClicks then row:SetPropagateMouseClicks(clickThrough) end
            if row.SetPropagateMouseMotion then row:SetPropagateMouseMotion(clickThrough) end
        end
    end
end

local function onTrackerRowClick(self, button)
    if ACAU_Top5DB and ACAU_Top5DB.clickThrough then return end
    local data = self and self._data
    if not data then return end
    if (not button or button == "LeftButton") and IsShiftKeyDown and IsShiftKeyDown() then
        if ns.Panel and ns.Panel.trackAchievement then
            ns.Panel.trackAchievement(data.id)
        elseif AddTrackedAchievement then
            AddTrackedAchievement(data.id)
        elseif C_AchievementInfo and C_AchievementInfo.SetSuperTrackedAchievement then
            C_AchievementInfo.SetSuperTrackedAchievement(data.id)
        end
        return
    end
    if ns.Panel and ns.Panel.Toggle then
        ns.Panel:Toggle()
    end
end

local function onTrackerRowEnter(self)
    local data = self and self._data
    if not data then return end
    if ns and ns.Panel and ns.Panel.showAchievementTooltip then
        ns.Panel.showAchievementTooltip(self, data.id, data.reward)
        return
    end
    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    GameTooltip:ClearLines()
    GameTooltip:AddLine(data.name or ("[" .. tostring(data.id) .. "]"), 1, 0.82, 0)
    GameTooltip:AddDoubleLine("Character progress:", ("%.0f%%"):format(data.percent or 0), 1, 1, 1, 1, 1, 1)
    if data.reward and data.reward ~= "" then
        GameTooltip:AddLine(" ")
        GameTooltip:AddLine("Reward", 0.6, 0.8, 1)
        GameTooltip:AddLine(data.reward, 0.9, 0.9, 0.9, true)
    end
    GameTooltip:Show()
end

local function onTrackerRowLeave()
    GameTooltip:Hide()
end

function Tracker:Init()
    if self.frame then return end
    ACAU_Top5DB = Util:MergeDefaults(ACAU_Top5DB or {}, Const.DEFAULTS.top5)

    local baseStrata = (Panel and Panel.frame and Panel.frame.GetFrameStrata and Panel.frame:GetFrameStrata()) or "MEDIUM"
    local f = CreateFrame("Frame", Const.TOP5_NAME, UIParent, "BackdropTemplate")
    f:SetFrameStrata(getHigherStrata(baseStrata))
    local panelLevel = ((Panel and Panel.frame and Panel.frame.GetFrameLevel and Panel.frame:GetFrameLevel()) or 10) + 30
    f:SetFrameLevel(math.max(150, panelLevel))
    local rowOffset = 20
    f:SetSize(280, 32 + (22 * 5) + 12 + rowOffset)
    f:SetBackdrop({
        bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = false, edgeSize = 12,
        insets = { left = 6, right = 6, top = 6, bottom = 6 },
    })
    f:SetBackdropColor(0, 0, 0, ACAU_Top5DB.alpha or 0.9)
    f:SetBackdropBorderColor(0.35, 0.35, 0.35)
    restorePosition(f)
    f:SetMovable(true)
    f:EnableMouse(not ACAU_Top5DB.clickThrough)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", function(self)
        if not ACAU_Top5DB.locked then self:StartMoving() end
    end)
    f:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        savePosition(self)
    end)
    f:Hide()

    local header = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    header:SetPoint("TOP", f, "TOP", 0, -8)
    header:SetText(DEFAULT_HEADER)
    f.header = header

    f.rows = {}
    local function makeRow(idx)
        local row = CreateFrame("Button", nil, f)
        row:SetSize(248, 20)
        row:SetPoint("TOPLEFT", f, "TOPLEFT", 12, - (12 + (idx - 1) * 24) - 4 - rowOffset)
        row:SetPoint("TOPRIGHT", f, "TOPRIGHT", -12, - (12 + (idx - 1) * 24) - 4 - rowOffset)
        row:RegisterForClicks("AnyUp")

        row.bg = row:CreateTexture(nil, "BACKGROUND")
        row.bg:SetAllPoints()
        row.bg:SetColorTexture(0, 0, 0, 0)

        row.highlight = row:CreateTexture(nil, "HIGHLIGHT")
        row.highlight:SetAllPoints()
        row.highlight:SetColorTexture(1, 1, 1, 0.08)

        row.Name = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        row.Name:SetPoint("LEFT", row, "LEFT", 4, 0)
        row.Name:SetPoint("RIGHT", row, "RIGHT", -70, 0)
        row.Name:SetJustifyH("LEFT")
        row.Name:SetWordWrap(false)

        row.Percent = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        row.Percent:SetPoint("RIGHT", row, "RIGHT", 0, 0)
        row.Percent:SetWidth(64)
        row.Percent:SetJustifyH("RIGHT")
        return row
    end

    for i = 1, 5 do
        f.rows[i] = makeRow(i)
    end

    local close
    if Util and Util.IsRetail and Util:IsRetail() then
        local ok, btn = pcall(CreateFrame, "Button", nil, f, "UIResetButtonTemplate")
        if ok and btn then
            close = btn
            close:SetSize(23, 23)
            close:SetPoint("TOPRIGHT", f, "TOPRIGHT", -6, -6)
            close:Show()
        end
    end
    if not close then
        close = CreateFrame("Button", nil, f, "UIPanelCloseButton")
        close:SetPoint("TOPRIGHT", f, "TOPRIGHT", 8, 8)
    end
    close:SetScript("OnClick", function()
        ACAU_Top5DB.shown = false
        f:Hide()
    end)

    applyClickThrough(f)
    self.frame = f
    self:ApplyTheme()
    self:SetAlpha(ACAU_Top5DB.alpha or 0.9)
end

function Tracker:Toggle()
    self:Init()
    if self.frame:IsShown() then
        ACAU_Top5DB.shown = false
        self.frame:Hide()
    else
        ACAU_Top5DB.shown = true
        self.frame:Show()
    end
end

function Tracker:SetLocked(lock)
    ACAU_Top5DB.locked = not not lock
end

function Tracker:SetAlpha(alpha)
    local a = Util:Clamp(alpha or 0.9, 0.2, 1)
    ACAU_Top5DB.alpha = a
    self:ApplyTheme()
end

function Tracker:SetClickThrough(clickThrough)
    ACAU_Top5DB.clickThrough = not not clickThrough
    if self.frame then applyClickThrough(self.frame) end
end

function Tracker:SetHeader(text)
    if self.frame and self.frame.header then
        self.frame.header:SetText(text or DEFAULT_HEADER)
    end
end

function Tracker:ApplyTheme()
    if not self.frame then return end
    local useTheme = ACAU_Top5DB.applyTheme ~= false
    local alpha = ACAU_Top5DB.alpha or 0.9
    if useTheme then
        local themeKey = (ACAU_Config and ACAU_Config.theme) or Const.DEFAULTS.theme
        local style = (Panel and Panel.GetThemeStyle and Panel:GetThemeStyle(themeKey)) or (getThemeStyle and getThemeStyle(themeKey))
        local palette = style and style.palette
        self.frame:SetBackdrop({
            bgFile = (style and style.bgFile) or "Interface\\Buttons\\WHITE8X8",
            edgeFile = (style and style.edgeFile) or "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = style and style.tile ~= false,
            tileSize = 8,
            edgeSize = 12,
            insets = { left = 3, right = 3, top = 3, bottom = 3 },
        })
        if style and style.bgColor then
            local r, g, b, a = style.bgColor[1] or 0, style.bgColor[2] or 0, style.bgColor[3] or 0, style.bgColor[4] or 1
            self.frame:SetBackdropColor(r, g, b, a * alpha)
        else
            self.frame:SetBackdropColor(0, 0, 0, alpha)
        end
        if style and style.borderColor then
            local r, g, b, a = style.borderColor[1] or 0.35, style.borderColor[2] or 0.35, style.borderColor[3] or 0.35, style.borderColor[4] or 1
            self.frame:SetBackdropBorderColor(r, g, b, (a or 1) * alpha)
        else
            self.frame:SetBackdropBorderColor(0.35, 0.35, 0.35, alpha)
        end
        local primary = (palette and palette.textPrimary) or { 1, 0.82, 0 }
        local secondary = (palette and palette.textSecondary) or primary
        local accent = (palette and palette.accent) or { 1, 0.78, 0.2 }
        local rowEvenAlpha = (palette and palette.rowEvenAlpha) or 0.18
        local rowOddAlpha = (palette and palette.rowOddAlpha) or 0.10
        local hi = (palette and palette.highlight) or { 1, 1, 1, 0.12 }
        if self.frame.header then
            self.frame.header:SetTextColor(primary[1], primary[2], primary[3])
        end
        for idx, row in ipairs(self.frame.rows or {}) do
            local isEven = (idx % 2) == 0
            local alphaRow = (isEven and rowEvenAlpha or rowOddAlpha) * alpha
            if row.bg then row.bg:SetColorTexture(0, 0, 0, alphaRow) end
            if row.highlight then row.highlight:SetColorTexture(hi[1] or 1, hi[2] or 1, hi[3] or 1, hi[4] or 0.12) end
            if row.Name then row.Name:SetTextColor(secondary[1], secondary[2], secondary[3]) end
            if row.Percent then row.Percent:SetTextColor(accent[1], accent[2], accent[3]) end
            local shadow = (alpha <= 0.5)
            local sx, sy = shadow and 1.5 or 0, shadow and -1.5 or 0
            local sa = shadow and 0.95 or 0
            if row.Name then
                row.Name:SetShadowColor(0, 0, 0, sa)
                row.Name:SetShadowOffset(sx, sy)
            end
            if row.Percent then
                row.Percent:SetShadowColor(0, 0, 0, sa)
                row.Percent:SetShadowOffset(sx, sy)
            end
        end
    else
        self.frame:SetBackdrop({
            bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = false, edgeSize = 12,
            insets = { left = 6, right = 6, top = 6, bottom = 6 },
        })
        self.frame:SetBackdropColor(0, 0, 0, alpha)
        self.frame:SetBackdropBorderColor(0.35, 0.35, 0.35)
        if self.frame.header then
            self.frame.header:SetTextColor(1, 0.82, 0)
        end
        for _, row in ipairs(self.frame.rows or {}) do
            if row.bg then row.bg:SetColorTexture(0, 0, 0, 0) end
            if row.highlight then row.highlight:SetColorTexture(1, 1, 1, 0.08) end
            if row.Name then row.Name:SetTextColor(1, 1, 1) end
            if row.Percent then row.Percent:SetTextColor(1, 0.82, 0) end
            if row.Name then
                row.Name:SetShadowColor(0, 0, 0, alpha <= 0.5 and 0.95 or 0)
                row.Name:SetShadowOffset(alpha <= 0.5 and 1.5 or 0, alpha <= 0.5 and -1.5 or 0)
            end
            if row.Percent then
                row.Percent:SetShadowColor(0, 0, 0, alpha <= 0.5 and 0.95 or 0)
                row.Percent:SetShadowOffset(alpha <= 0.5 and 1.5 or 0, alpha <= 0.5 and -1.5 or 0)
            end
        end
    end
end

function Tracker:RefreshPosition()
    if self.frame then
        restorePosition(self.frame)
    end
end

function Tracker:Update(list)
    self:Init()
    if not list then return end
    for i = 1, 5 do
        local row = self.frame.rows[i]
        local data = list[i]
        if data then
            row._data = data
            row.Name:SetText(data.name or "")
            row.Percent:SetText(("%.0f%%"):format(data.percent or 0))
            row:Show()
            if not row._handlersBound then
                row:SetScript("OnClick", onTrackerRowClick)
                row:SetScript("OnEnter", onTrackerRowEnter)
                row:SetScript("OnLeave", onTrackerRowLeave)
                row._handlersBound = true
            end
        else
            row._data = nil
            row.Name:SetText("")
            row.Percent:SetText("")
            if row._handlersBound then
                row:SetScript("OnClick", onTrackerRowClick)
                row:SetScript("OnEnter", onTrackerRowEnter)
                row:SetScript("OnLeave", onTrackerRowLeave)
            end
        end
    end
    if ACAU_Top5DB.shown then self.frame:Show() end
end
