--=====================================================================================
-- BLU Framework
-- Our own lightweight addon framework (no external dependencies)
--=====================================================================================

-- Removed redundant BluPrint function - using BLU:Print() instead

local addonName, addonTable = ...
local ADDON_PATH = "Interface\\AddOns\\" .. addonName .. "\\"
local CORE_EVENT_ID_LOGOUT = "core_player_logout"
local CHAT_ICON = "|T" .. ADDON_PATH .. "media\\Textures\\icon.tga:16:16:0:0|t"
local CHAT_PREFIX = CHAT_ICON .. " |cff05dffa[BLU]|r"
local CHAT_DEBUG_PREFIX = CHAT_PREFIX .. " |cff808080[DEBUG]|r"
local CHAT_ERROR_PREFIX = CHAT_PREFIX .. " |cffff0000[ERROR]|r"

print("BLU: Core loading started.")

-- Create the main addon object (global)
BLU = {
    name = addonName,
    version = "v6.2.4",
    author = C_AddOns.GetAddOnMetadata(addonName, "Author"),
    
    -- Core tables
    Modules = {},
    LoadedModules = {},
    events = {},
    hooks = {},
    timers = {},
    
    -- Settings
    debugMode = false,
    isInitialized = false
}

-- Print message
function BLU:Print(message)
    print(CHAT_PREFIX .. " " .. message)
end

-- Print debug message
function BLU:PrintDebug(message)
    if self.debugMode then
        print(CHAT_DEBUG_PREFIX .. " " .. message)
    end
end

function BLU:Trace(scope, message)
    self:PrintDebug("[" .. tostring(scope) .. "] " .. tostring(message))
end

-- Print error message
function BLU:PrintError(message)
    print(CHAT_ERROR_PREFIX .. " " .. message)
end

-- Create event frame (early definition)
BLU.eventFrame = CreateFrame("Frame")
BLU.eventFrame:SetScript("OnEvent", function(self, event, ...) 
    BLU:FireEvent(event, ...)
end)

-- Register event (early definition)
local function RegisterEvent(self, event, callback, id)
    id = id or "core"
    
    if not self.events[event] then
        self.events[event] = {}
        self.eventFrame:RegisterEvent(event)
    end
    
    self.events[event][id] = callback
    self:PrintDebug("[Events] Registered event '" .. tostring(event) .. "' with id '" .. tostring(id) .. "'")
end
BLU.RegisterEvent = RegisterEvent

-- Print debug message (early definition)




-- Framework loaded - BLU is now globally accessible



-- Unregister event
local function UnregisterEvent(self, event, id)
    id = id or "core"
    
    if self.events[event] then
        self.events[event][id] = nil
        
        -- If no more callbacks, unregister the event
        if not next(self.events[event]) then
            self.eventFrame:UnregisterEvent(event)
            self.events[event] = nil
        end
        self:PrintDebug("[Events] Unregistered event '" .. tostring(event) .. "' with id '" .. tostring(id) .. "'")
    end
end
BLU.UnregisterEvent = UnregisterEvent

-- Fire event with performance optimization
local function FireEvent(self, event, ...)
    local eventTable = self.events[event]
    if not eventTable then return end
    
    -- Cache event callbacks to avoid issues if they're modified during iteration
    local callbacks = {}
    for id, callback in pairs(eventTable) do
        callbacks[#callbacks + 1] = {id = id, callback = callback}
    end
    
    for i = 1, #callbacks do
        local entry = callbacks[i]
        local success, err = pcall(entry.callback, event, ...)
        if not success then
            self:PrintError("Error in event " .. tostring(event) .. " for " .. tostring(entry.id) .. ": " .. tostring(err))
        end
    end
end
BLU.FireEvent = FireEvent

--=====================================================================================
-- Timer System
--=====================================================================================

-- Create timer
function BLU:CreateTimer(duration, callback, repeating)
    self:Trace("Timer", "Creating timer (duration=" .. tostring(duration) .. ", repeating=" .. tostring(repeating == true) .. ")")
    local timer = {
        duration = duration,
        callback = callback,
        repeating = repeating,
        elapsed = 0,
        active = true
    }
    
    table.insert(self.timers, timer)
    
    -- Start timer frame if needed
    if not self.timerFrame then
        self.timerFrame = CreateFrame("Frame")
        self.timerFrame:SetScript("OnUpdate", function(_, elapsed) 
            BLU:UpdateTimers(elapsed)
        end)
    end
    
    return timer
end

-- Update timers
function BLU:UpdateTimers(elapsed)
    for i = #self.timers, 1, -1 do
        local timer = self.timers[i]
        
        if timer.active then
            timer.elapsed = timer.elapsed + elapsed
            
            if timer.elapsed >= timer.duration then
                -- Execute callback
                local success, err = pcall(timer.callback)
                if not success then
                    self:PrintError("Timer error: " .. err)
                end
                
                if timer.repeating then
                    timer.elapsed = 0
                else
                    -- Remove one-time timer
                    table.remove(self.timers, i)
                end
            end
        end
    end
    
    -- Stop timer frame if no active timers
    if #self.timers == 0 and self.timerFrame then
        self.timerFrame:SetScript("OnUpdate", nil)
    end
end

-- Cancel timer
function BLU:CancelTimer(timer)
    if timer then
        timer.active = false
        self:Trace("Timer", "Cancelled timer")
    end
end

--=====================================================================================
-- Hook System
--=====================================================================================

-- Hook function
function BLU:Hook(target, method, callback)
    self:Trace("Hooks", "Hook request for method '" .. tostring(method) .. "'")
    local original = target[method]
    
    if not original then
        self:PrintError("Cannot hook non-existent method: " .. method)
        return
    end
    
    target[method] = function(...)
        return callback(original, ...)
    end
    
    -- Store for unhooking
    self.hooks[target] = self.hooks[target] or {}
    self.hooks[target][method] = original
    self:Trace("Hooks", "Hooked method '" .. tostring(method) .. "'")
end

-- Unhook function
function BLU:Unhook(target, method)
    if self.hooks[target] and self.hooks[target][method] then
        target[method] = self.hooks[target][method]
        self.hooks[target][method] = nil
        self:Trace("Hooks", "Unhooked method '" .. tostring(method) .. "'")
    end
end

--=====================================================================================
-- Slash Commands
--=====================================================================================

-- Register slash command
function BLU:RegisterSlashCommand(command, callback)
    self:Trace("Slash", "Registering slash command(s): " .. tostring(type(command) == "table" and table.concat(command, ", ") or command))
    -- Support multiple commands
    local commands = type(command) == "table" and command or {command}
    
    -- Use a unique identifier for this addon's commands
    local cmdName = addonName .. "CMD"
    
    for i, cmd in ipairs(commands) do
        _G["SLASH_" .. cmdName .. i] = "/" .. cmd
    end
    
    SlashCmdList[cmdName] = callback
end

--=====================================================================================
-- Print Functions
--=====================================================================================

-- Print message
function BLU:Print(message)
    print(CHAT_PREFIX .. " " .. message)
end

-- Print debug message
function BLU:PrintDebug(message)
    if self.debugMode then
        print(CHAT_DEBUG_PREFIX .. " " .. message)
    end
end

-- Print error message
function BLU:PrintError(message)
    print(CHAT_ERROR_PREFIX .. " " .. message)
end

-- Show welcome message
function BLU:ShowWelcomeMessage()
    if not (self.db and self.db.profile and self.db.profile.showWelcomeMessage ~= false) then
        self:Trace("Welcome", "Skipped welcome message")
        return
    end

    local version = "Unknown"
    if C_AddOns and C_AddOns.GetAddOnMetadata then
        version = C_AddOns.GetAddOnMetadata(addonName, "Version") or version
    elseif GetAddOnMetadata then
        version = GetAddOnMetadata(addonName, "Version") or version
    end

    print(CHAT_PREFIX .. " Welcome! Use |cff05dffa/blu|r to open the options panel or |cff05dffa/blu help|r for more commands.")
    print(CHAT_PREFIX .. " |cffffff00Version:|r |cff8080ff" .. version .. "|r")
    self:Trace("Welcome", "Displayed welcome message for version " .. tostring(version))
end

--=====================================================================================
-- Module System
--=====================================================================================

-- Register module
function BLU:RegisterModule(module, name, description)
    -- Handle both calling conventions
    if type(module) == "string" then
        -- Old style: (name, module)
        local temp = module
        module = name
        name = temp
    end
    
    self.Modules[name] = module
    self.LoadedModules[name] = module
    
    -- Don't auto-init modules here, they're initialized in init.lua
    
    self:PrintDebug("Module registered: " .. name .. (description and (" - " .. description) or ""))
end

-- Get module
function BLU:GetModule(name)
    self:Trace("Modules", "GetModule called for '" .. tostring(name) .. "'")
    return self.Modules[name]
end

--=====================================================================================
-- Profile Management Functions
--=====================================================================================

-- Create profile
function BLU:CreateProfile(name)
    self:Trace("Profiles", "CreateProfile called for '" .. tostring(name) .. "'")
    if not self.Database then
        self:PrintError("Database not initialized")
        return false
    end
    return self.Database:CreateProfile(name)
end

-- Delete profile
function BLU:DeleteProfile(name)
    self:Trace("Profiles", "DeleteProfile called for '" .. tostring(name) .. "'")
    if not self.Database then
        self:PrintError("Database not initialized")
        return false
    end
    return self.Database:DeleteProfile(name)
end

-- Load profile
function BLU:LoadProfile(name)
    self:Trace("Profiles", "LoadProfile called for '" .. tostring(name) .. "'")
    if not self.Database then
        self:PrintError("Database not initialized")
        return false
    end
    return self.Database:SetProfile(name)
end

-- Save profile
function BLU:SaveProfile(name)
    self:Trace("Profiles", "SaveProfile called for '" .. tostring(name) .. "'")
    if not self.Database then
        self:PrintError("Database not initialized")
        return false
    end
    -- Force save current settings
    self.Database:Save()
    return true
end

-- Rename profile
function BLU:RenameProfile(oldName, newName)
    self:Trace("Profiles", "RenameProfile called from '" .. tostring(oldName) .. "' to '" .. tostring(newName) .. "'")
    if not self.Database then
        self:PrintError("Database not initialized")
        return false
    end
    
    if not self.Database:CopyProfile(oldName, newName) then
        return false
    end
    
    -- Switch to new profile if it was the active one
    if BLUDB.currentProfile == oldName then
        self.Database:SetProfile(newName)
    end
    
    -- Delete old profile
    return self.Database:DeleteProfile(oldName)
end

-- Serialize profile for export
function BLU:SerializeProfile(profileName)
    self:Trace("Profiles", "SerializeProfile called for '" .. tostring(profileName) .. "'")
    if not BLUDB or not BLUDB.profiles or not BLUDB.profiles[profileName] then
        self:PrintError("Profile not found: " .. tostring(profileName))
        return nil
    end
    
    local profile = BLUDB.profiles[profileName]
    local serialized = {
        name = profileName,
        version = self.version,
        exportDate = date("%Y-%m-%d %H:%M:%S"),
        settings = profile
    }
    
    return self:TableToString(serialized)
end

-- Import profile from string
function BLU:ImportProfile(dataString, profileName)
    self:Trace("Profiles", "ImportProfile called for target '" .. tostring(profileName or "auto") .. "'")
    local success, data = pcall(loadstring("return " .. dataString))
    if not success or type(data) ~= "table" then
        self:PrintError("Invalid import data")
        return false
    end
    
    if not data.settings then
        self:PrintError("Import data missing settings")
        return false
    end
    
    local name = profileName or data.name or "Imported Profile"
    BLUDB.profiles[name] = self.Database:CopyTable(data.settings)
    
    self:Print("Profile imported: " .. name)
    return true
end

-- Table to string helper
function BLU:TableToString(t, indent)
    indent = indent or ""
    local str = "{\n"
    
    for k, v in pairs(t) do
        str = str .. indent .. "  "
        
        if type(k) == "string" then
            str = str .. "[\"" .. k .. "\"] = "
        else
            str = str .. "[" .. tostring(k) .. "] = "
        end
        
        if type(v) == "table" then
            str = str .. self:TableToString(v, indent .. "  ")
        elseif type(v) == "string" then
            str = str .. "\"" .. v .. "\""
        else
            str = str .. tostring(v)
        end
        
        str = str .. ",\n"
    end
    
    str = str .. indent .. "}"
    return str
end

--=====================================================================================
-- Advanced Settings Functions
--=====================================================================================

-- Clear sound cache
function BLU:ClearSoundCache()
    self:Trace("Advanced", "ClearSoundCache called")
    if self.Modules.registry and self.Modules.registry.soundCache then
        self.Modules.registry.soundCache = {}
        self:Print("Sound cache cleared")
    end
end

-- Reset advanced settings
function BLU:ResetAdvancedSettings()
    self:Trace("Advanced", "ResetAdvancedSettings called")
    -- Reset only advanced settings to defaults
    local profile = self.db.profile
    if profile then
        profile.soundPooling = false
        profile.asyncLoading = false
        profile.soundQueueSize = 3
        profile.fadeTime = 200
        profile.lazyLoading = true
        profile.moduleTimeout = 5
        profile.debugLevel = 0
        profile.debugToConsole = true
        profile.debugToFile = false
        profile.profiling = false
        profile.positionalAudio = false
        profile.dynamicCompression = false
        profile.aiSounds = false
        profile.weakAurasIntegration = false
        profile.discordIntegration = false
    end
end

-- Rebuild database
function BLU:RebuildDatabase()
    self:Trace("Advanced", "RebuildDatabase called")
    if self.Database then
        -- Force reload saved variables
        self.Database:LoadSavedVariables()
        self:Print("Database rebuilt")
    end
end

-- Test sound function
function BLU:PlayTestSound(category, volume)
    self:Trace("Sound", "PlayTestSound called for category '" .. tostring(category) .. "' at volume '" .. tostring(volume or 1.0) .. "'")
    if self.Modules.registry then
        local testSounds = {
            levelup = ADDON_PATH .. "media\\sounds\\level_default.ogg",
            achievement = ADDON_PATH .. "media\\sounds\\achievement_default.ogg",
            quest = ADDON_PATH .. "media\\sounds\\quest_default.ogg"
        }
        
        local soundFile = testSounds[category] or testSounds.levelup
        local channel = self.db and self.db.profile and self.db.profile.soundChannel or "Master"
        local vol = volume or 1.0
        
        PlaySoundFile(soundFile, channel)
        return true
    end
    return false
end

-- Module enable/disable functions
function BLU:EnableModule(moduleId)
    if self.Modules[moduleId] then
        -- Module is already loaded, just enable it
        if self.db and self.db.profile and self.db.profile.modules then
            self.db.profile.modules[moduleId] = true
        end
        self:PrintDebug("Enabled module: " .. moduleId)
        return true
    end
    return false
end

function BLU:DisableModule(moduleId)
    if self.db and self.db.profile and self.db.profile.modules then
        self.db.profile.modules[moduleId] = false
    end
    self:PrintDebug("Disabled module: " .. moduleId)
    return true
end

-- Reload modules function
function BLU:ReloadModules()
    self:Trace("Modules", "ReloadModules called")
    if self.Modules.loader and self.Modules.loader.LoadModulesFromSettings then
        self.Modules.loader:LoadModulesFromSettings()
    end
end

-- Show export dialog
function BLU:ShowExportDialog(profileData)
    self:Trace("Profiles", "ShowExportDialog opened")
    -- Create a simple text display dialog
    local frame = CreateFrame("Frame", "BLUExportDialog", UIParent, "BasicFrameTemplateWithInset")
    frame:SetSize(500, 400)
    frame:SetPoint("CENTER")
    frame:SetMovable(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", frame.StartMoving)
    frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
    
    frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    frame.title:SetPoint("LEFT", frame.TitleBg, "LEFT", 5, 0)
    frame.title:SetText("Export Profile")
    
    local scrollFrame = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 10, -30)
    scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -30, 40)
    
    local editBox = CreateFrame("EditBox", nil, scrollFrame)
    editBox:SetMultiLine(true)
    editBox:SetMaxLetters(0)
    editBox:SetWidth(scrollFrame:GetWidth() - 20)
    editBox:SetAutoFocus(false)
    editBox:SetFontObject(ChatFontNormal)
    editBox:SetText(profileData)
    scrollFrame:SetScrollChild(editBox)
    
    local closeBtn = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    closeBtn:SetSize(80, 22)
    closeBtn:SetPoint("BOTTOMRIGHT", -10, 10)
    closeBtn:SetText("Close")
    closeBtn:SetScript("OnClick", function() frame:Hide() end)
    
    frame:Show()
end

-- Show import dialog
function BLU:ShowImportDialog()
    self:Trace("Profiles", "ShowImportDialog opened")
    local frame = CreateFrame("Frame", "BLUImportDialog", UIParent, "BasicFrameTemplateWithInset")
    frame:SetSize(500, 400)
    frame:SetPoint("CENTER")
    frame:SetMovable(true)
    frame:EnableMouse(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetScript("OnDragStart", frame.StartMoving)
    frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
    
    frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    frame.title:SetPoint("LEFT", frame.TitleBg, "LEFT", 5, 0)
    frame.title:SetText("Import Profile")
    
    local scrollFrame = CreateFrame("ScrollFrame", nil, frame, "UIPanelScrollFrameTemplate")
    scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 10, -30)
    scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -30, 40)
    
    local editBox = CreateFrame("EditBox", nil, scrollFrame)
    editBox:SetMultiLine(true)
    editBox:SetMaxLetters(0)
    editBox:SetWidth(scrollFrame:GetWidth() - 20)
    editBox:SetAutoFocus(true)
    editBox:SetFontObject(ChatFontNormal)
    scrollFrame:SetScrollChild(editBox)
    
    local importBtn = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    importBtn:SetSize(80, 22)
    importBtn:SetPoint("BOTTOMLEFT", 10, 10)
    importBtn:SetText("Import")
    importBtn:SetScript("OnClick", function()
        local data = editBox:GetText()
        if data and data ~= "" then
            BLU:ImportProfile(data)
            frame:Hide()
        end
    end)
    
    local closeBtn = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
    closeBtn:SetSize(80, 22)
    closeBtn:SetPoint("BOTTOMRIGHT", -10, 10)
    closeBtn:SetText("Cancel")
    closeBtn:SetScript("OnClick", function() frame:Hide() end)
    
    frame:Show()
end

-- Show character copy dialog
function BLU:ShowCharacterCopyDialog()
    self:Trace("Profiles", "ShowCharacterCopyDialog opened")
    self:Print("Character copy functionality not yet implemented")
end

function BLU:Enable()
    self:Trace("State", "Enable called")
    -- Already enabled
end

function BLU:Disable()
    self:Trace("State", "Disable called")
    self:OnDisable()
end

function BLU:OnDisable()
    self:Trace("State", "OnDisable called")
    for name, module in pairs(self.LoadedModules) do
        if module and module.OnDisable then
            module:OnDisable()
        end
    end
end

BLU:RegisterEvent("PLAYER_LOGOUT", function(event)
    BLU:OnDisable()
end, CORE_EVENT_ID_LOGOUT)

-- Copy all BLU functions to addon table so other files can access them via local addonName, addonTable = ...

