local addonName, NS = ...

-- =========================================================================
-- CORE: INITIALIZATION & EVENTS
-- =========================================================================

-- Create the Main Frame Object
local GR = CreateFrame("Frame", "GRS_MainFrame", UIParent)
GR:Hide()
NS.GR = GR -- Expose to Namespace

-- Global reference
_G["GRS_MainFrame"] = GR

-- Core Properties
GR.isInitialized = false
GR.activeTab = 1
GR.views = {}
GR.db = {}

-- Scanner Properties
GR.Scanner = {
    frame = nil,
    rows = {},
    timer = 0,
    interval = 1.0, -- Refresh every 1s
    prevCPU = {},   -- Table to store previous CPU total times
    data = {},      -- Snapshot data
    sortKey = "cpu",-- Default sort key
    sortAsc = false -- Default Descending (Highest first)
}

-- =========================================================================
-- SLASH COMMANDS
-- =========================================================================
SLASH_AXI_ADDON1 = "/axi"
SLASH_AXI_ADDON2 = "/grs"
SlashCmdList["AXI_ADDON"] = function(msg) 
    local cmd = msg:lower():match("^%s*(%S+)")
    
    if cmd == "scan" then
        GR:ToggleScanner()
        return
    end

    if not GR.isInitialized then
        GR:Initialize()
    end

    if cmd == "rio" then
        print("|cff00ffff--- GRS (AXI) RIO DIAGNOSTIC ---|r")
        return
    end

    if GR:IsShown() then GR:Hide() else GR:Show() end 
end

-- =========================================================================
-- EVENT HANDLING
-- =========================================================================
GR:RegisterEvent("ADDON_LOADED")
GR:RegisterEvent("WHO_LIST_UPDATE")
GR:RegisterEvent("CHAT_MSG_WHISPER")
GR:RegisterEvent("CHAT_MSG_WHISPER_INFORM")
GR:RegisterEvent("PLAYER_ENTERING_WORLD") 
GR:RegisterEvent("CHAT_MSG_ADDON")
GR:RegisterEvent("PLAYER_GUILD_UPDATE") 
GR:RegisterEvent("GUILD_ROSTER_UPDATE")

GR:SetScript("OnEvent", function(self, event, ...)
    if event == "ADDON_LOADED" and ... == addonName then
        if not self.isInitialized then self:Initialize() end
        
        if self.db.scale then 
            self:SetScale(self.db.scale) 
            if self.UpdateResizeBounds then self:UpdateResizeBounds() end
        end
        if self.db.alpha then self:SetAlpha(self.db.alpha) end
        if self.db.windowWidth and self.db.windowHeight then
            self:SetSize(self.db.windowWidth, self.db.windowHeight)
        end
        
        -- Safety check for recent list update
        if self.UpdateRecentList then self:UpdateRecentList() end
        
        C_ChatInfo.RegisterAddonMessagePrefix("GRS_SYNC")
        
        local blSize = self.db.blacklist and NS.Engine.GetTableSize(self.db.blacklist) or 0
        local pblSize = self.db.permaBlacklist and NS.Engine.GetTableSize(self.db.permaBlacklist) or 0
        
        print("|cff00ff00GRS V1.2.3 (Perma-Blacklist) Loaded.|r")
        print("|cff00ffff[System]: Blacklist: " .. blSize .. " | Perma: " .. pblSize .. "|r")
        
        -- [[ COMPACT MODE INIT: Restore AFTER all geometry is applied ]]
        if self.db.compactMode and self.ToggleCompactMode then
            self:ToggleCompactMode(true)
        end
        
        -- [[ FIX: Force-fill Level Range inputs when addon window becomes visible ]]
        self:HookScript("OnShow", function(s)
            if s.LevelMinInput then
                local minTxt = s.LevelMinInput:GetText()
                if minTxt == "" or minTxt == "0" then
                    s.LevelMinInput:SetText(tostring(s.db.levelMin or 80))
                end
                s.LevelMinInput:SetCursorPosition(0)
            end
            if s.LevelMaxInput then
                local maxTxt = s.LevelMaxInput:GetText()
                if maxTxt == "" or maxTxt == "0" then
                    s.LevelMaxInput:SetText(tostring(s.db.levelMax or 80))
                end
                s.LevelMaxInput:SetCursorPosition(0)
            end
        end)
        
    elseif event == "PLAYER_ENTERING_WORLD" then
        if self.isInitialized then 
            if self.UpdateRaiderIOStatus then self:UpdateRaiderIOStatus() end
            if self.UpdateOfficerStatus then self:UpdateOfficerStatus() end
            if self.ScanGuildForBlacklistMatches then self:ScanGuildForBlacklistMatches() end
        end
    elseif event == "WHO_LIST_UPDATE" then
        if self.ProcessWhoResults then self:ProcessWhoResults() end
    elseif event == "CHAT_MSG_WHISPER" or event == "CHAT_MSG_WHISPER_INFORM" then
        if self.HandleChatEvent then self:HandleChatEvent(event, ...) end
    elseif event == "CHAT_MSG_ADDON" then
        local prefix, msg, channel, sender = ...
        
        -- Handle Standard Comm
        if self.HandleComm then self:HandleComm(...) end
        
        -- [[ NEW: SYNC PROTOCOL HANDLER ]]
        if prefix == "GRS_SYNC" then
            if self.HandleSyncComm then
                self:HandleSyncComm(msg, sender)
            end
        end
        
    elseif event == "PLAYER_GUILD_UPDATE" then
        if self.isInitialized and self.UpdateOfficerStatus then self:UpdateOfficerStatus() end
    elseif event == "GUILD_ROSTER_UPDATE" then
        if self.isInitialized and self.ScanGuildForBlacklistMatches then self:ScanGuildForBlacklistMatches() end
    end
end)

-- =========================================================================
-- INITIALIZATION
-- =========================================================================
function GR:Initialize()
    self:InitDB()
    
    -- Apply localization after DB is loaded (so we can check selectedLanguage)
    if NS.ApplyLocale then NS.ApplyLocale() end
    
    self.db.debugMode = false 
    
    -- UI Initialization
    if self.BuildStructuralUI then self:BuildStructuralUI() end
    if self.BuildHomeView then self:BuildHomeView() end

    if NS and NS.Blacklist and NS.Blacklist.Attach then
        NS.Blacklist.Attach(self)
    end
    if self.BuildBlacklistView then self:BuildBlacklistView() end

    -- [[ REALM MIGRATION CHECK: Delayed to allow full init chain to complete ]]
    if self.CheckRealmMigration then
        C_Timer.After(3, function() self:CheckRealmMigration() end)
    end
    
    if self.BuildModuleContainers then self:BuildModuleContainers() end
    if self.BuildSettingsView then self:BuildSettingsView() end
    
    -- Engine Initialization
    if self.InjectGlobalMenus then self:InjectGlobalMenus() end
    
    if self.UpdateRecentList then self:UpdateRecentList() end
    if self.SwitchView then self:SwitchView(1) end
    
    self.isInitialized = true
    if self.UpdateOfficerStatus then self:UpdateOfficerStatus() end
end

function GR:InitDB()
    if not _G.GRS_DB then _G.GRS_DB = {} end
    if not _G.GRS_DB.blacklist then _G.GRS_DB.blacklist = {} end
    if not _G.GRS_DB.permaBlacklist then _G.GRS_DB.permaBlacklist = {} end
    if not _G.GRS_DB.recentHistory then _G.GRS_DB.recentHistory = {} end
    if not _G.GRS_DB.templates then _G.GRS_DB.templates = {["Default"] = "Hi there!"} end
    if not _G.GRS_DB.customFilters then _G.GRS_DB.customFilters = {} end

    -- Defaults
    if NS.Constants and NS.Constants.DEFAULTS then
        for k, v in pairs(NS.Constants.DEFAULTS) do
            if _G.GRS_DB[k] == nil then _G.GRS_DB[k] = v end
        end
    end
    self.db = _G.GRS_DB
    
    -- Sanitize
    local function Sanitize(tbl)
        for k, v in pairs(tbl) do
            if type(v) == "boolean" then
                tbl[k] = {
                    name = k,
                    rio = 0,
                    heroic = "-",
                    mythic = "-",
                    race = "Unknown",
                    fullClass = "Unknown",
                    level = 0,
                    zone = "Unknown"
                }
            end
        end
    end
    Sanitize(self.db.blacklist)
    Sanitize(self.db.permaBlacklist)
end

-- =========================================================================
-- MODULE: A.X.I.O.M. SCANNER
-- =========================================================================

function GR:ToggleScanner()
    -- Check CVar for CPU Profiling
    local profiling = GetCVar("scriptProfile")
    if profiling == "0" then
        StaticPopupDialogs["AXI_ENABLE_PROFILING"] = {
            text = "A.X.I.O.M. Scanner requires CPU Profiling.\nEnable it now? (Client will Reload)",
            button1 = "Enable & Reload",
            button2 = "Cancel",
            OnAccept = function()
                SetCVar("scriptProfile", "1")
                ReloadUI()
            end,
            timeout = 0,
            whileDead = true,
            hideOnEscape = true,
            preferredIndex = 3,
        }
        StaticPopup_Show("AXI_ENABLE_PROFILING")
        return
    end

    if not self.Scanner.frame then
        self:BuildScannerUI()
    end

    if self.Scanner.frame:IsShown() then
        self.Scanner.frame:Hide()
    else
        self.Scanner.frame:Show()
        -- Reset previous data to prevent spikes on open
        self.Scanner.prevCPU = {} 
        UpdateAddOnCPUUsage()
        -- Initial sort
        self:SortScannerData()
    end
end

function GR:BuildScannerUI()
    local f = CreateFrame("Frame", "GRS_ScannerFrame", UIParent)
    f:SetSize(600, 500)
    f:SetPoint("CENTER")
    f:SetFrameStrata("HIGH")
    f:EnableMouse(true)
    f:SetMovable(true)
    f:RegisterForDrag("LeftButton")
    f:SetScript("OnDragStart", f.StartMoving)
    f:SetScript("OnDragStop", f.StopMovingOrSizing)
    
    NS.CreateBg(f, 0.1, 0.1, 0.1, 0.95)
    NS.CreateBorder(f, 0, 0, 0, 1)
    
    -- Header Title
    local header = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    header:SetPoint("TOPLEFT", 15, -15)
    header:SetText("A.X.I.O.M. | |cffADD8E6Scanner|r")
    
    -- Close Button
    local closeBtn = CreateFrame("Button", nil, f, "UIPanelCloseButton")
    closeBtn:SetPoint("TOPRIGHT", -5, -5)
    
    -- Header Sort Buttons (Name | CPU | RAM)
    local hBar = CreateFrame("Frame", nil, f)
    hBar:SetPoint("TOPLEFT", 10, -50)
    hBar:SetPoint("TOPRIGHT", -30, -50)
    hBar:SetHeight(20)
    
    local function CreateHeaderBtn(text, width, key, anchor, relative, x)
        local b = CreateFrame("Button", nil, hBar)
        b:SetSize(width, 20)
        b:SetPoint(anchor, relative, anchor, x, 0)
        
        local t = b:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        t:SetPoint("LEFT", 5, 0)
        t:SetText(text)
        b.text = t
        
        -- Hover
        local hl = b:CreateTexture(nil, "HIGHLIGHT")
        hl:SetAllPoints()
        hl:SetColorTexture(1, 1, 1, 0.1)
        
        b:SetScript("OnClick", function()
            if GR.Scanner.sortKey == key then
                GR.Scanner.sortAsc = not GR.Scanner.sortAsc
            else
                GR.Scanner.sortKey = key
                GR.Scanner.sortAsc = true
            end
            GR:SortScannerData()
            GR:RenderScannerList()
        end)
        
        return b
    end
    
    -- Columns
    CreateHeaderBtn("Name", 200, "name", "LEFT", hBar, 0)
    CreateHeaderBtn("CPU", 200, "cpu", "CENTER", hBar, 0)
    CreateHeaderBtn("RAM", 80, "mem", "RIGHT", hBar, 0)
    
    -- Scroll Frame
    local scroll = CreateFrame("ScrollFrame", nil, f, "UIPanelScrollFrameTemplate")
    scroll:SetPoint("TOPLEFT", 10, -75) -- Shifted down for headers
    scroll:SetPoint("BOTTOMRIGHT", -30, 50)
    if NS.StyleScrollBar then NS.StyleScrollBar(scroll) end
    
    local content = CreateFrame("Frame", nil, scroll)
    content:SetSize(560, 1000)
    scroll:SetScrollChild(content)
    f.content = content
    
    -- Footer Buttons
    local exportBtn = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    exportBtn:SetSize(120, 25)
    exportBtn:SetPoint("BOTTOMLEFT", 10, 10)
    exportBtn:SetText("Export Log")
    NS.SkinButton(exportBtn)
    exportBtn:SetScript("OnClick", function() self:ExportScannerLog() end)
    
    local closeFooter = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    closeFooter:SetSize(80, 25)
    closeFooter:SetPoint("BOTTOMRIGHT", -10, 10)
    closeFooter:SetText("Close")
    NS.SkinButton(closeFooter)
    closeFooter:SetScript("OnClick", function() f:Hide() end)
    
    -- OnUpdate Loop
    f:SetScript("OnUpdate", function(self, elapsed)
        GR.Scanner.timer = GR.Scanner.timer + elapsed
        if GR.Scanner.timer >= GR.Scanner.interval then
            GR:UpdateScannerData()
            GR.Scanner.timer = 0
        end
    end)
    
    self.Scanner.frame = f
end

function GR:SortScannerData()
    local sc = self.Scanner
    local key = sc.sortKey
    local asc = sc.sortAsc
    
    table.sort(sc.data, function(a, b)
        local valA = a[key]
        local valB = b[key]
        
        if asc then
            return valA < valB
        else
            return valA > valB
        end
    end)
end

function GR:UpdateScannerData()
    local sc = self.Scanner
    
    UpdateAddOnMemoryUsage()
    UpdateAddOnCPUUsage()
    
    -- Collect Data (Does NOT sort)
    local num = C_AddOns.GetNumAddOns()
    
    -- If data table is empty or size mismatch, rebuild it
    -- Otherwise, update existing entries to maintain stability
    if #sc.data ~= num then
        sc.data = {}
        for i = 1, num do
            table.insert(sc.data, { index = i })
        end
    end
    
    for _, entry in ipairs(sc.data) do
        local i = entry.index
        -- If user disabled an addon mid-session, index might shift, but
        -- GetAddOnInfo(i) is generally stable for session.
        if i <= num then
            local name = C_AddOns.GetAddOnInfo(i)
            local mem = GetAddOnMemoryUsage(i)
            local totalCPU = GetAddOnCPUUsage(i)
            
            local prev = sc.prevCPU[name] or 0
            local delta = totalCPU - prev
            sc.prevCPU[name] = totalCPU
            
            if delta < 0 then delta = 0 end
            
            entry.name = name
            entry.mem = mem
            entry.cpu = delta
        end
    end
    
    -- DO NOT SORT HERE. 
    -- Only Render.
    self:RenderScannerList()
end

function GR:RenderScannerList()
    local sc = self.Scanner
    local rowHeight = 25
    local content = sc.frame.content
    local num = #sc.data
    
    -- Hide unused rows
    for _, row in pairs(sc.rows) do row:Hide() end
    
    for i, info in ipairs(sc.data) do
        if not sc.rows[i] then
            sc.rows[i] = self:CreateScannerRow(content, i)
        end
        
        local row = sc.rows[i]
        row:SetPoint("TOPLEFT", 0, -((i-1) * rowHeight))
        row:Show()
        
        -- Update Data
        row.name:SetText(info.name)
        row.mem:SetText(string.format("%.2f MB", info.mem / 1024))
        
        -- CPU Bar Logic
        local cpuPercent = (info.cpu / 1000) * 100 
        if cpuPercent > 100 then cpuPercent = 100 end
        
        row.cpuBar:SetValue(cpuPercent)
        row.cpuVal:SetText(string.format("%.1f ms", info.cpu))
        
        -- Color Logic
        if cpuPercent < 10 then
            row.cpuBar:SetStatusBarColor(0, 1, 0) 
        elseif cpuPercent < 15 then
            row.cpuBar:SetStatusBarColor(1, 1, 0) 
        else
            row.cpuBar:SetStatusBarColor(1, 0, 0) 
        end
    end
    
    content:SetHeight(num * rowHeight)
end

function GR:CreateScannerRow(parent, index)
    local row = CreateFrame("Frame", nil, parent)
    row:SetSize(560, 25)
    
    local name = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    name:SetPoint("LEFT", 5, 0)
    name:SetWidth(200)
    name:SetJustifyH("LEFT")
    name:SetWordWrap(false)
    row.name = name
    
    local mem = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    mem:SetPoint("RIGHT", -5, 0)
    mem:SetWidth(80)
    mem:SetJustifyH("RIGHT")
    row.mem = mem
    
    local bar = CreateFrame("StatusBar", nil, row)
    bar:SetSize(200, 16)
    bar:SetPoint("CENTER", 0, 0)
    bar:SetMinMaxValues(0, 100) 
    bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
    NS.CreateBorder(bar, 0.5, 0.5, 0.5, 0.5)
    row.cpuBar = bar
    
    local barText = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    barText:SetPoint("CENTER")
    row.cpuVal = barText
    
    return row
end

function GR:ExportScannerLog()
    -- Generate CSV
    local csv = "Addon,CPU_Delta_MS,RAM_KB\n"
    for _, d in ipairs(self.Scanner.data) do
        csv = csv .. string.format("%s,%.2f,%.2f\n", d.name, d.cpu, d.mem)
    end
    
    -- Create Export Window
    if not self.Scanner.exportFrame then
        local f = CreateFrame("Frame", "GRS_ExportFrame", UIParent)
        f:SetSize(720, 720)
        f:SetPoint("CENTER")
        f:SetFrameStrata("DIALOG")
        NS.CreateBg(f, 0.1, 0.1, 0.1, 1)
        NS.CreateBorder(f, 0, 0, 0, 1)
        
        local scroll = CreateFrame("ScrollFrame", "GRS_ExportScroll", f, "UIPanelScrollFrameTemplate")
        scroll:SetPoint("TOPLEFT", 10, -10)
        scroll:SetPoint("BOTTOMRIGHT", -30, 10)
        if NS.StyleScrollBar then NS.StyleScrollBar(scroll) end
        
        local editBox = CreateFrame("EditBox", nil, scroll)
        editBox:SetMultiLine(true)
        editBox:SetFontObject(ChatFontNormal)
        editBox:SetWidth(680)
        editBox:SetAutoFocus(true)
        scroll:SetScrollChild(editBox)
        
        -- Auto Close on Ctrl+C
        editBox:SetScript("OnKeyUp", function(self, key)
            if IsControlKeyDown() and key == "C" then
                f:Hide()
            end
        end)
        
        -- Close on Escape
        editBox:SetScript("OnEscapePressed", function() f:Hide() end)
        
        self.Scanner.exportFrame = f
        self.Scanner.editBox = editBox
    end
    
    self.Scanner.exportFrame:Show()
    self.Scanner.editBox:SetText(csv)
    self.Scanner.editBox:HighlightText()
    self.Scanner.editBox:SetFocus()
end