-- ============================================================
--  Core.lua  |  v2.1.17
--  AzerothAngler — Main logic
--
--  Part of the ForgeDMC Addon Suite
--  Author  : ForgeDMC
--  Updated : 2026-03-21
--  License : MIT
-- ============================================================


local _, ns = ...

ns.Core = {}
local Core = ns.Core

ns.Debug = ns.Debug or {}
local Debug = ns.Debug

local function trimDebugText(text)
    text = tostring(text or "Unknown error")
    text = text:gsub("\r", "")
    text = text:gsub("\n\n+", "\n")
    return text
end

function Debug:Initialize()
    if not ns.DB then return end
    ns.DB.debug = ns.DB.debug or {}
    ns.DB.debug.logs = ns.DB.debug.logs or {}
    ns.DB.debug.lastError = ns.DB.debug.lastError or ""
end

function Debug:GetMaxEntries()
    local configured = ns.DB and ns.DB.profile and ns.DB.profile.debug and ns.DB.profile.debug.maxEntries or 120
    configured = tonumber(configured) or 120
    return math.max(20, math.min(500, configured))
end

function Debug:GetLines()
    if not (ns.DB and ns.DB.debug and ns.DB.debug.logs) then return {} end
    local out = {}
    for i, line in ipairs(ns.DB.debug.logs) do
        out[i] = line
    end
    return out
end

function Debug:GetText()
    local lines = self:GetLines()
    if #lines == 0 then
        return "No debug messages captured yet."
    end
    return table.concat(lines, "\n\n")
end

function Debug:Clear()
    self:Initialize()
    for i = #(ns.DB.debug.logs or {}), 1, -1 do
        table.remove(ns.DB.debug.logs, i)
    end
    ns.DB.debug.lastError = ""
end

function Debug:Capture(context, errText)
    if not ns.DB then return end
    if ns.DB.profile and ns.DB.profile.debug and ns.DB.profile.debug.captureLuaErrors == false then
        return
    end

    self:Initialize()
    local line = string.format("[%s] %s\n%s", date("%H:%M:%S"), tostring(context or "Error"), trimDebugText(errText))
    table.insert(ns.DB.debug.logs, 1, line)
    while #(ns.DB.debug.logs) > self:GetMaxEntries() do
        table.remove(ns.DB.debug.logs)
    end
    ns.DB.debug.lastError = line

    local now = GetTime and GetTime() or 0
    if (ns.state.lastDebugNoticeAt or 0) + 5 <= now then
        ns.state.lastDebugNoticeAt = now
        ns.Utils:Print(string.format("%sDebug captured an error.|r Use /angler debug to inspect logs.", ns.constants.RED_COLOR))
    end

    if ns.UI and ns.state.debugFrame and ns.state.debugFrame:IsShown() and ns.UI.RefreshDebug then
        ns.UI:RefreshDebug()
    end
end

function Core:InitializeDB()
    if type(AzerothAnglerDB) ~= "table" then
        AzerothAnglerDB = ns.Utils:CopyDefaults()
    else
        ns.Utils:MergeDefaults(AzerothAnglerDB, ns.defaults)
    end
    ns.DB = AzerothAnglerDB

    -- NOTE: Scanner is feature-gated; it respects the user's saved setting.
    -- Do NOT force-reset scanner.enabled here — that would clobber SavedVariables
    -- on every login and prevent users from enabling the scanner even manually.
    -- If the scanner should ship disabled by default, the default is already
    -- false in ns.defaults.profile.scanner.enabled (see Data.lua).

    -- Migrate stale iconTexture path from old AzerothAnglerLogo reference
    if ns.DB.profile and ns.DB.profile.minimap then
        local icon = ns.DB.profile.minimap.iconTexture or ""
        if icon:find("AzerothAnglerLogo", 1, true) then
            ns.DB.profile.minimap.iconTexture = "Interface\\AddOns\\AzerothAngler\\Media\\generated\\app_icon_face.png"
        end
    end
end

function Core:ApplySavedPoints()
    if ns.state.frame and ns.DB.profile.ui.point then
        local p = ns.DB.profile.ui.point
        ns.state.frame:ClearAllPoints()
        ns.state.frame:SetPoint(p[1], _G[p[2]] or UIParent, p[3], p[4], p[5])
    end
    if ns.state.compactFrame and ns.DB.profile.ui.compactPoint then
        local p = ns.DB.profile.ui.compactPoint
        ns.state.compactFrame:ClearAllPoints()
        ns.state.compactFrame:SetPoint(p[1], _G[p[2]] or UIParent, p[3], p[4], p[5])
    end
end

function Core:SaveFramePosition(frame, key)
    if not frame or not ns.DB then return end
    local point, _, relativePoint, x, y = frame:GetPoint(1)
    ns.DB.profile.ui[key] = { point, "UIParent", relativePoint, math.floor(x + 0.5), math.floor(y + 0.5) }
end

function Core:InstallErrorHandler()
    if ns.state.debugHandlerInstalled then return end
    if not (geterrorhandler and seterrorhandler) then return end

    local previous = geterrorhandler()
    ns.state.previousErrorHandler = previous

    seterrorhandler(function(err)
        if ns.Debug and ns.Debug.Capture then
            ns.Debug:Capture("Lua Error", err)
        end
        if previous then
            pcall(previous, err)
        end
    end)

    ns.state.debugHandlerInstalled = true
end

function Core:Startup()
    self:InitializeDB()
    if ns.Session and ns.Session.ResetForLogin then
        ns.Session:ResetForLogin()
    end
    if ns.Debug and ns.Debug.Initialize then ns.Debug:Initialize() end
    self:InstallErrorHandler()
    ns.UI:CreateMainFrame()
    ns.Minimap:CreateButton()
    if ns.Nearby and ns.Nearby.Initialize then ns.Nearby:Initialize() end
    ns.Tracking:RefreshAll()
    self:ApplySavedPoints()

    if ns.DB.profile.ui.showMainOnLogin and ns.state.frame then ns.state.frame:Show() else ns.state.frame:Hide() end
    if ns.state.compactFrame then ns.state.compactFrame:SetShown(ns.DB.profile.ui.compactMode) end

    ns.state.initialized = true
    local addonVersion = (C_AddOns and C_AddOns.GetAddOnMetadata and C_AddOns.GetAddOnMetadata(ns.addonName, "Version"))
                       or (GetAddOnMetadata and GetAddOnMetadata(ns.addonName, "Version"))
                       or "2.1.17"
    ns.Utils:Print(string.format("%sv%s|r loaded. Type %s/aa|r to open. Nearby app pings and minimap tracking are active.", ns.constants.GOLD_COLOR, addonVersion, ns.constants.GREEN_COLOR))
end

function Core:OnUpdate(elapsed)
    if not ns.state.initialized then return end
    if ns.Nearby and ns.Nearby.OnUpdate then ns.Nearby:OnUpdate(elapsed) end
    self._refreshElapsed = (self._refreshElapsed or 0) + (elapsed or 0)
    if self._refreshElapsed < 0.5 then return end
    self._refreshElapsed = 0

    local mainShown = ns.state.frame and ns.state.frame:IsShown()
    local compactShown = ns.state.compactFrame and ns.state.compactFrame:IsShown()
    if not (ns.DB.currentSession.active or mainShown or compactShown) then return end

    ns.DB.currentSession.zone = ns.Utils:GetZoneName()
    ns.DB.currentSession.fishingSkill = ns.Utils:GetFishingSkill()
    if ns.Minimap and ns.Minimap.RefreshButton then ns.Minimap:RefreshButton() end
    if ns.UI then ns.UI:RefreshAll() end
end
