local addonName, NS = ...
local GR = _G["GRS_MainFrame"]

-- Import Constants for Local Use
local STATUS_COLORS = GR.RR.CONST.STATUS_COLORS

-- =========================================================================
-- 1. DATA HELPERS
-- =========================================================================

function GR.RR.ParseDate(dateStr)
    if not dateStr then return nil end
    local d, m, y = string.match(dateStr, "(%d+)[%p](%d+)[%p](%d+)")
    d, m, y = tonumber(d), tonumber(m), tonumber(y)
    if not d or not m or not y then return nil end
    local ok, ts = pcall(time, {year=y, month=m, day=d, hour=12, min=0, sec=0})
    if ok then return ts else return 0 end
end

function GR.RR.FormatDate(timestamp)
    if not timestamp then return "" end
    return date("%d-%m-%Y", timestamp)
end

function GR.RR.Sanitize(str)
    if not str then return "" end
    str = string.gsub(str, "%^", "")
    return str
end

function GR.RR.GetRegionName()
    local regionID = GetCurrentRegion()
    if regionID == 1 then return "us"
    elseif regionID == 2 then return "kr"
    elseif regionID == 3 then return "eu"
    elseif regionID == 4 then return "tw"
    elseif regionID == 5 then return "cn"
    else return "us" end 
end

function GR.RR.GetFactionID()
    local factionGroup = UnitFactionGroup("player")
    if factionGroup == "Alliance" then return 1 else return 2 end
end

function GR.RR.GetFullPlayerName()
    local name = UnitName("player")
    return name
end

function GR.RR.GetCanonicalName(fullName)
    if not fullName then return "Unknown" end
    return Ambiguate(fullName, "none")
end

function GR:RR_ScanDB()
    if not GRS_RaidRosterDB then GRS_RaidRosterDB = { events = {}, settings = {}, attendanceHistory = {}, logs = {} } end
    if not GRS_RaidRosterDB.events then GRS_RaidRosterDB.events = {} end
    if not GRS_RaidRosterDB.logs then GRS_RaidRosterDB.logs = {} end
    
    local list = {}
    for id, data in pairs(GRS_RaidRosterDB.events) do
        table.insert(list, data)
    end
    
    -- Organizes EventCards by Date: Oldest at the top, Newest at the bottom
    table.sort(list, function(a,b)
        local tA = GR.RR.ParseDate(a.date) or 0
        local tB = GR.RR.ParseDate(b.date) or 0
        return tA < tB 
    end)
    
    GR.RR.cache = list
    if GR.RR_UpdateLeftPane then 
        GR:RR_UpdateLeftPane() 
    end
end

function GR:RR_SelectEvent(eventData)
    GR.RR.selectedEvent = eventData
    GR.RR.multiSelect = {} 
    
    if GR.RR_UpdateLeftPane then 
        GR:RR_UpdateLeftPane() 
    end
    
    if eventData then
        GR.RR.currentRoster = eventData.roster
    else
        GR.RR.currentRoster = nil
    end
    
    if GR.RR_UpdateMemberRightPane then GR:RR_UpdateMemberRightPane() end
    if GR.RR_UpdateOfficerRightPane then GR:RR_UpdateOfficerRightPane() end
end

-- [NEW] Helper to force a full UI refresh without re-selecting logic
function GR:RR_RefreshUI(evtID)
    -- If the updated event is currently selected, refresh it
    if GR.RR.selectedEvent and (not evtID or GR.RR.selectedEvent.id == evtID) then
        GR:RR_SelectEvent(GR.RR.selectedEvent)
    -- Otherwise, if we are in Officer mode, we still need to update the Raster (since it shows all events)
    elseif GR.RR.viewMode == "OFFICER" and GR.RR_UpdateRaster then
        GR:RR_UpdateRaster()
    end

    -- Always update Tracking Panels (Attendance)
    if GR.RR.trackingPanels then
        for k, p in pairs(GR.RR.trackingPanels) do p.Update() end
    end
end

function GR:RR_FetchClassCounts(includeOffline)
    local counts = {}
    if not IsInGuild() then return counts end
    C_GuildInfo.GuildRoster()
    local num = GetNumGuildMembers()
    for i = 1, num do
        local _, _, _, _, _, _, _, _, online, _, class = GetGuildRosterInfo(i)
        if online or includeOffline then
            counts[class] = (counts[class] or 0) + 1
        end
    end
    return counts
end

-- =========================================================================
-- 2. LOGGING & HISTORY HELPERS
-- =========================================================================

function GR:RR_AddLogEntry(author, description)
    if not GRS_RaidRosterDB.logs then GRS_RaidRosterDB.logs = {} end
    local cleanAuthor = Ambiguate(author, "none")
    local displayAuthor = strsplit("-", cleanAuthor)
    local h = { ts = GetServerTime(), author = cleanAuthor, desc = description }
    table.insert(GRS_RaidRosterDB.logs, 1, h)
    if #GRS_RaidRosterDB.logs > 100 then
        table.remove(GRS_RaidRosterDB.logs)
    end
    if GRS_RR_LogFrame and GRS_RR_LogFrame:IsShown() and GRS_RR_LogFrame.UpdateLog then
        GRS_RR_LogFrame.UpdateLog()
    end
end

function GR:RR_TakeSnapshot(panelKey)
    local evt = GR.RR.selectedEvent
    if not evt then return end
    
    GR:RR_AddLogEntry("SYSTEM", "Attendance state verified for " .. (evt.name or "Event"))
    
    if GR.RR.trackingPanels[panelKey] then
        GR.RR.trackingPanels[panelKey].Update()
    end
end

function GR:RR_GetSortedEventIDs(limit)
    local list = {}
    for eid, data in pairs(GRS_RaidRosterDB.events) do
        local t = GR.RR.ParseDate(data.date) or 0
        table.insert(list, { id = eid, ts = t })
    end
    
    table.sort(list, function(a,b) return a.ts < b.ts end)
    
    local out = {}
    local start = math.max(1, #list - limit + 1)
    for i = start, #list do
        table.insert(out, list[i].id)
    end
    return out
end

function GR:RR_GetSmartStatus(evtID, playerName)
    local function ScanRoster(rStruct, targetName)
        if not rStruct then return nil end
        local cleanTarget = Ambiguate(targetName, "none")
        for role, members in pairs(rStruct) do
            if type(members) == "table" then
                for _, pData in ipairs(members) do
                    if pData.name == cleanTarget then
                        -- [FIX] Ensure returned role key is uppercase for UI lookup
                        return string.upper(role) 
                    end
                end
            end
        end
        return nil
    end

    local evt = GRS_RaidRosterDB.events[evtID]
    if evt and evt.roster then
        local status = ScanRoster(evt.roster, playerName)
        if status then return status, false end 
    end
    
    return nil, false
end

function GR:RR_GetEventDetails(evtID)
    local evt = GRS_RaidRosterDB.events[evtID]
    if not evt then return { date = "??", name = "Unknown" } end
    local d = date("%d-%m", GR.RR.ParseDate(evt.date) or 0)
    return { date = d, name = evt.name, fullDate = evt.date }
end

-- =========================================================================
-- 3. ROSTER MANAGEMENT
-- =========================================================================

-- [UPDATED] Added suppressUI flag to prevent UI Thrashing during bulk imports
function GR:RR_UpdateMemberStatus(evtID, name, role, status, timestamp, suppressUI)
    local evt = GRS_RaidRosterDB.events[evtID]
    if not evt then return end
    
    local cleanName = Ambiguate(name, "none")
    GR:RR_RemoveMember(evtID, cleanName)

    -- [FIX] AGGRESSIVE Status Normalization
    -- 1. Convert to Uppercase
    -- 2. Strip leading/trailing whitespace
    if status then
        status = string.upper(strtrim(status))
    else
        status = "BENCH" -- Default fallback
    end

    -- Map aliases to canonical keys
    if status == "NO-SHOW" or status == "NOSHOW" then status = "NOSHOW" end
    if status == "PRESENT" then 
        -- If present, we rely on the role to determine the bucket
        -- but we store "PRESENT" as the status string in the object
    end
    
    local bucket = status
    if status == "PRESENT" then 
        -- If status is Present, bucket is the role (TANK, HEALER, DPS)
        -- Ensure role is also valid/uppercase
        bucket = role and string.upper(strtrim(role)) or "DPS"
    end 
    
    if not evt.roster[bucket] then evt.roster[bucket] = {} end
    
    local classFileName = "PRIEST"
    if IsInGuild() then
        local num = GetNumGuildMembers()
        for i=1, num do
            local n, _, _, _, _, _, _, _, _, _, c = GetGuildRosterInfo(i)
            if Ambiguate(n, "none") == cleanName then classFileName = c; break end
        end
    end
    
    table.insert(evt.roster[bucket], {
        name = cleanName,
        class = classFileName,
        ts = timestamp,
        status = status,
        role = role
    })
    
    if not evt.counts then evt.counts = { TANK=0, HEALER=0, DPS=0 } end
    evt.counts.TANK = #(evt.roster["TANK"] or {})
    evt.counts.HEALER = #(evt.roster["HEALER"] or {})
    evt.counts.DPS = #(evt.roster["DPS"] or {})
    
    -- Only trigger UI update if NOT suppressed
    if not suppressUI then
        GR:RR_RefreshUI(evtID)
    end
end

function GR:RR_RemoveMember(evtID, name)
    local evt = GRS_RaidRosterDB.events[evtID]
    if not evt then return end
    
    local cleanName = Ambiguate(name, "none")
    for cat, list in pairs(evt.roster) do
        for i, data in ipairs(list) do
            if data.name == cleanName then
                table.remove(list, i)
                break
            end
        end
    end
    
    if not evt.counts then evt.counts = { TANK=0, HEALER=0, DPS=0 } end
    evt.counts.TANK = #(evt.roster["TANK"] or {})
    evt.counts.HEALER = #(evt.roster["HEALER"] or {})
    evt.counts.DPS = #(evt.roster["DPS"] or {})
    
    if GR.RR.selectedEvent and GR.RR.selectedEvent.id == evtID then
        GR:RR_SelectEvent(evt)
    end
    
    if GR.RR.trackingPanels then
        for k, p in pairs(GR.RR.trackingPanels) do p.Update() end
    end
end

-- =========================================================================
-- 4. FORMATTING & COLOR HELPERS
-- =========================================================================

function GR.RR.ScoreColor(score)
    if not score then return "|cffffffff" end
    local n = tonumber(score) or 0
    if n >= 3000 then return "|cffff8000"
    elseif n >= 2500 then return "|cffa335ee"
    elseif n >= 2000 then return "|cff0070dd"
    elseif n >= 1500 then return "|cff1eff00"
    else return "|cffffffff" end
end

function GR.RR.RaidProgressColor(str)
    if not str or str == "-" then return "|cff808080" end
    if string.find(str, "(%d+)/(%d+)") then
        local k, t = string.match(str, "(%d+)/(%d+)")
        if k == t then return "|cff1eff00" end 
    end
    return "|cffffffff" 
end