-- "My Alts" overview: a standalone window listing all of your own characters with their
-- dungeon/raid lockout status, current keystone and Great Vault progress.
--
-- Data is read from MythicMateDB.shared.players[name] for each name flagged in
-- MythicMateDB.ownCharacters — both persisted in SavedVariables by the main addon's
-- MythicMate_SaveSelfSnapshot(), so this works offline for characters you've logged into.
--
-- Split file (own Lua chunk for its locals) and standalone frame (no main-panel tab surgery),
-- opened via /wl alts or MythicMateNS.Alts.Toggle().

local MythicMateNS = _G.MythicMate
if not MythicMateNS then return end

local frame = nil
local MAX_ROWS = 14
local ROW_H = 24
local scrollOffset = 0
local lastCount = 0

------------------------------------------------------------
-- Data helpers
------------------------------------------------------------
local function ownCharacterList()
    local out = {}
    local db = _G.MythicMateDB
    if type(db) ~= "table" then return out end
    local own = db.ownCharacters
    local players = db.shared and db.shared.players
    if type(own) ~= "table" or type(players) ~= "table" then return out end
    local seen = {}
    for name in pairs(own) do
        -- Prefer the full "Name-Realm" entry; skip bare short-name aliases when a full one exists.
        if type(name) == "string" and name:find("-", 1, true) then
            local t = players[name]
            if type(t) == "table" and not seen[name] then
                seen[name] = true
                out[#out + 1] = { name = name, data = t }
            end
        end
    end
    -- Include short-name-only own chars that never got a realm suffix.
    for name in pairs(own) do
        if type(name) == "string" and not name:find("-", 1, true) and not seen[name] then
            local t = players[name]
            if type(t) == "table" then
                seen[name] = true
                out[#out + 1] = { name = name, data = t }
            end
        end
    end
    table.sort(out, function(a, b) return tostring(a.name):lower() < tostring(b.name):lower() end)
    return out
end

local function displayName(name)
    local n, r = tostring(name):match("^([^%-]+)%-?(.*)$")
    if r and r ~= "" then
        return tostring(n) .. " |cff888888-" .. tostring(r) .. "|r"
    end
    return tostring(n or name)
end

-- Returns text + a color triple for the dungeon column.
local function dungeonSummary(t)
    local dg = t and t.dungeons
    if type(dg) ~= "table" or #dg == 0 then return "—", 0.6, 0.6, 0.6 end
    local locked, total = 0, 0
    for _, e in ipairs(dg) do
        if type(e) == "table" then
            total = total + 1
            if e.locked then locked = locked + 1 end
        end
    end
    local avail = total - locked
    local text = ("%d avail / %d locked"):format(avail, locked)
    if avail > 0 then return text, 0.4, 1, 0.4 end
    return text, 1, 0.5, 0.3
end

local function raidLockedCount(t)
    local rd = t and t.raids
    if type(rd) ~= "table" then return 0 end
    local locked = 0
    for _, e in ipairs(rd) do
        if type(e) == "table" and e.locked then locked = locked + 1 end
    end
    return locked
end

local function keystoneSummary(t)
    local ks = t and t.keystone
    if type(ks) == "table" and tonumber(ks.level) and tonumber(ks.level) > 0 then
        local mn = tostring(ks.mapName or "?")
        return mn .. " (" .. tostring(ks.level) .. ")", 0.53, 0.83, 1
    end
    return "none", 0.6, 0.6, 0.6
end

local function vaultAbbrev(label)
    if label == "Mythic+" then return "M+" end
    if label == "Raid" then return "R" end
    if label == "World" then return "W" end
    if label == "PvP" then return "P" end
    return tostring(label)
end

local function vaultSummary(t)
    local v = t and t.vault
    if type(v) ~= "table" or #v == 0 then return "—", 0.6, 0.6, 0.6 end
    local parts = {}
    local anyEarned = false
    for _, row in ipairs(v) do
        if type(row) == "table" then
            local earned = tonumber(row.earned) or 0
            local total = tonumber(row.total) or 0
            if earned > 0 then anyEarned = true end
            parts[#parts + 1] = ("%s %d/%d"):format(vaultAbbrev(row.label), earned, total)
        end
    end
    if #parts == 0 then return "—", 0.6, 0.6, 0.6 end
    if anyEarned then return table.concat(parts, "  "), 1, 0.82, 0 end
    return table.concat(parts, "  "), 0.7, 0.7, 0.7
end

------------------------------------------------------------
-- ElvUI (best-effort)
------------------------------------------------------------
local function skinElvUI(p)
    local db = _G.MythicMateDB
    local st = db and db.settings
    if st and st.useElvUISkinDefaults == false then return end
    if type(_G.MythicMate_GetElvUISkinsModule) ~= "function" then return end
    local okS, S = pcall(_G.MythicMate_GetElvUISkinsModule)
    if not okS or not S then return end
    pcall(function()
        if S.HandleButton then
            if p.refresh then S:HandleButton(p.refresh) end
            if p.close then S:HandleCloseButton(p.close) end
        end
        if S.HandleFrame then S:HandleFrame(p) end
    end)
end

------------------------------------------------------------
-- UI
------------------------------------------------------------
local refreshAlts -- forward decl

local function buildFrame()
    if frame then return frame end
    local f = CreateFrame("Frame", "MythicMateAltsFrame", UIParent, "BackdropTemplate")
    f:SetSize(600, 420)
    f:SetPoint("CENTER")
    -- One strata above the main MythicMate window (which is DIALOG + toplevel) so this
    -- standalone window never disappears behind it when opened from the "My Alts" button.
    f:SetFrameStrata("FULLSCREEN_DIALOG")
    f:SetToplevel(true)
    f:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        tile = true, tileSize = 16, edgeSize = 14,
        insets = { left = 4, right = 4, top = 4, bottom = 4 },
    })
    f:SetBackdropColor(0.04, 0.05, 0.09, 0.96)
    f:SetBackdropBorderColor(0.4, 0.3, 0.7, 1)
    f:EnableMouse(true)
    -- Remember where the user drags this window (persists across reloads).
    if type(_G.MythicMate_PersistFrame) == "function" then
        _G.MythicMate_PersistFrame(f, "alts")
    else
        f:SetMovable(true); f:RegisterForDrag("LeftButton")
        f:SetScript("OnDragStart", f.StartMoving); f:SetScript("OnDragStop", f.StopMovingOrSizing)
    end

    -- Close on Escape.
    _G.UISpecialFrames = _G.UISpecialFrames or {}
    table.insert(_G.UISpecialFrames, "MythicMateAltsFrame")

    local title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    title:SetPoint("TOPLEFT", 14, -12)
    title:SetText("|cffc868ffMythicMate|r — My Characters")

    f.close = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    f.close:SetPoint("TOPRIGHT", -6, -6)
    f.close:SetScript("OnClick", function() f:Hide() end)

    f.refresh = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    f.refresh:SetSize(80, 22)
    f.refresh:SetPoint("TOPRIGHT", -34, -10)
    f.refresh:SetText("Refresh")
    f.refresh:SetScript("OnClick", function() if refreshAlts then refreshAlts() end end)

    -- Column headers.
    local function hdr(text, x)
        local fs = f:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        fs:SetPoint("TOPLEFT", x, -44)
        fs:SetTextColor(0.72, 0.72, 0.85)
        fs:SetText(text)
        return fs
    end
    hdr("Character", 16)
    hdr("Dungeons", 190)
    hdr("Keystone", 330)
    hdr("Great Vault", 452)

    f.hdrLine = f:CreateTexture(nil, "ARTWORK")
    f.hdrLine:SetColorTexture(0.4, 0.4, 0.55, 0.6)
    f.hdrLine:SetPoint("TOPLEFT", 12, -58)
    f.hdrLine:SetPoint("TOPRIGHT", -12, -58)
    f.hdrLine:SetHeight(1)

    f.status = f:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
    f.status:SetPoint("BOTTOMLEFT", 14, 12)
    f.status:SetPoint("BOTTOMRIGHT", -14, 12)
    f.status:SetJustifyH("LEFT")

    f.empty = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    f.empty:SetPoint("CENTER", 0, 0)
    f.empty:SetText("No character data yet.\nLog into your alts (with MythicMate enabled) to populate this list.")
    f.empty:SetJustifyH("CENTER")
    f.empty:Hide()

    f.rows = {}
    local ry = -64
    for i = 1, MAX_ROWS do
        local row = CreateFrame("Frame", nil, f)
        row:SetPoint("TOPLEFT", 8, ry)
        row:SetPoint("TOPRIGHT", -8, ry)
        row:SetHeight(ROW_H)
        row.bg = row:CreateTexture(nil, "BACKGROUND")
        row.bg:SetAllPoints(row)
        row.bg:SetColorTexture(1, 1, 1, (i % 2 == 0) and 0.05 or 0.015)

        local function col(x, w)
            local fs = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
            fs:SetPoint("LEFT", x, 0)
            if w then fs:SetWidth(w) end
            fs:SetJustifyH("LEFT")
            if fs.SetWordWrap then fs:SetWordWrap(false) end
            return fs
        end
        row.name = col(8, 170)
        row.dungeon = col(182, 135)
        row.keystone = col(322, 122)
        row.vault = col(444, 140)
        row:Hide()
        f.rows[i] = row
        ry = ry - ROW_H
    end

    f:EnableMouseWheel(true)
    f:SetScript("OnMouseWheel", function(_, delta)
        local maxOff = math.max(0, lastCount - MAX_ROWS)
        scrollOffset = math.max(0, math.min(maxOff, scrollOffset - (delta or 0) * 3))
        if refreshAlts then refreshAlts() end
    end)

    skinElvUI(f)
    frame = f
    f:Hide()
    return f
end

function refreshAlts()
    local f = buildFrame()
    if not f then return end
    local list = ownCharacterList()
    lastCount = #list
    local maxOff = math.max(0, lastCount - MAX_ROWS)
    if scrollOffset > maxOff then scrollOffset = maxOff end

    if lastCount == 0 then
        f.empty:Show()
    else
        f.empty:Hide()
    end

    for i = 1, MAX_ROWS do
        local row = f.rows[i]
        local entry = list[scrollOffset + i]
        if entry then
            local t = entry.data
            row.name:SetText(displayName(entry.name))

            local dText, dr, dg, db = dungeonSummary(t)
            local raids = raidLockedCount(t)
            if raids > 0 then
                dText = dText .. "  |cffff8888+" .. raids .. "R|r"
            end
            row.dungeon:SetText(dText)
            row.dungeon:SetTextColor(dr, dg, db)

            local kText, kr, kg, kb = keystoneSummary(t)
            row.keystone:SetText(kText)
            row.keystone:SetTextColor(kr, kg, kb)

            local vText, vr, vg, vb = vaultSummary(t)
            row.vault:SetText(vText)
            row.vault:SetTextColor(vr, vg, vb)

            row:Show()
        else
            row:Hide()
        end
    end

    if lastCount == 0 then
        f.status:SetText("")
    else
        local chars = (lastCount == 1) and "character" or "characters"
        local to = math.min(scrollOffset + MAX_ROWS, lastCount)
        f.status:SetText(("%d %s — showing %d-%d  •  +NR = N locked raids"):format(
            lastCount, chars, scrollOffset + 1, to))
    end
end

------------------------------------------------------------
-- Public interface
------------------------------------------------------------
local function showAlts()
    local f = buildFrame()
    if not f then return end
    scrollOffset = 0
    -- Make sure our own current data is fresh before showing.
    if type(_G.ScanLockouts) == "function" then pcall(_G.ScanLockouts) end
    refreshAlts()
    f:Show()
    if f.Raise then f:Raise() end
end

local function hideAlts()
    if frame then frame:Hide() end
end

local function toggleAlts()
    if frame and frame:IsShown() then
        frame:Hide()
    else
        showAlts()
    end
end

MythicMateNS.Alts = { Show = showAlts, Hide = hideAlts, Toggle = toggleAlts, Refresh = refreshAlts }
