local ADDON_NAME, ns = ...
ns = ns or {}

local Util = ns.Util
local Const = ns.Const
local Filters = ns.Filters
local Meta = ns.Meta
local Remix = ns.Remix
local type = type
local tostring = tostring

ns.State = {
    lastScan = 0,
    results = {},
    needsRescan = false,
    loginScanDone = false,
}

local function removeAchievementFromList(list, achID)
    if type(list) ~= "table" then return 0 end
    local removed = 0
    for i = #list, 1, -1 do
        local row = list[i]
        if row and row.id == achID then
            table.remove(list, i)
            removed = removed + 1
        end
    end
    return removed
end

function ns.CullEarnedAchievement(achID)
    achID = tonumber(achID)
    if not achID then return false end

    local changed = false
    if ns.Scan and ns.Scan.raw then
        if removeAchievementFromList(ns.Scan.raw, achID) > 0 then
            changed = true
        end
    end
    if ACAU_Cache and type(ACAU_Cache.raw) == "table" then
        if removeAchievementFromList(ACAU_Cache.raw, achID) > 0 then
            changed = true
        end
    end
    if ns.State and ns.State.results then
        if removeAchievementFromList(ns.State.results, achID) > 0 then
            changed = true
        end
    end

    if ns.ApplyFilters then ns.ApplyFilters() end

    ns.State.needsRescan = true
    if ns.Panel and ns.Panel.frame and ns.Panel.frame.progress and ns.Panel.frame.progress.Text then
        ns.Panel.frame.progress.Text:SetText("Achievement earned — list updated. Rescan recommended.")
    end

    return changed
end

local function restoreCachedResults()
    if not ns.Scan or not ACAU_Cache or type(ACAU_Cache.raw) ~= "table" then return end
    ns.Scan.raw = ACAU_Cache.raw
    if ns.Scan.ApplyFilters then
        local list = ns.Scan:ApplyFilters()
        ns.State.results = list
    end
end

function ns.ApplyFilters()
    local Scan = ns.Scan
    if not Scan or not Scan.ApplyFilters or not Scan.raw or #Scan.raw == 0 then
        return false
    end
    local list = Scan:ApplyFilters()
    ns.State.results = list
    if ns.Panel and ns.Panel.Render then ns.Panel:Render(list) end
    if ns.Panel and ns.Panel.RefreshTop5Override then
        ns.Panel:RefreshTop5Override(list)
    elseif ns.Tracker and ns.Tracker.Update then
        ns.Tracker:Update(list)
    end
    return true
end

function ns.MarkRescanNeeded()
    ns.State.needsRescan = true
    if ns.ApplyFilters then ns.ApplyFilters() end
    if ns.Panel and ns.Panel.frame and ns.Panel.frame.progress and ns.Panel.frame.progress.Text then
        ns.Panel.frame.progress.Text:SetText("List may be out of date, please rescan")
    end
end

local function printAddonMessage(text)
    local message = "|cff33ff99ACA:|r " .. tostring(text or "")
    if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then
        DEFAULT_CHAT_FRAME:AddMessage(message)
        return
    end
    print("ACA: " .. tostring(text or ""))
end
ns.Print = printAddonMessage

local function openOptionsPanel()
    local launcher = ns.KagrokSharedLauncher
    if type(launcher) == "table" and type(launcher.OpenSettings) == "function" then
        if launcher:OpenSettings(ADDON_NAME) then
            return true
        end
    end
    if Settings and Settings.OpenToCategory then
        if ns.Options and ns.Options.category and ns.Options.category.ID then
            Settings.OpenToCategory(ns.Options.category.ID)
            return true
        end
        if ns.Options and ns.Options.categoryName then
            Settings.OpenToCategory(ns.Options.categoryName)
            return true
        end
        Settings.OpenToCategory("Almost Completed Achievements")
        return true
    end
    if InterfaceOptionsFrame_OpenToCategory then
        local target = (ns.Options and (ns.Options.category or ns.Options.categoryName)) or "Almost Completed Achievements"
        InterfaceOptionsFrame_OpenToCategory(target)
        InterfaceOptionsFrame_OpenToCategory(target)
        return true
    end
    return false
end
ns.OpenOptionsPanel = openOptionsPanel

function ns.EnsureDevInfoModule()
    if ns.devInfoModule then
        return ns.devInfoModule
    end

    local module = ns.DevInfoModule
    if type(module) ~= "table" or type(module.Create) ~= "function" then
        return nil
    end

    ns.devInfoModule = module:Create(ns, {
        addon_name = ADDON_NAME,
        frame_prefix = ADDON_NAME,
        media_root = "Interface\\AddOns\\" .. ADDON_NAME .. "\\Templates\\Media\\Social\\",
        panel_title = "Developer Info",
        button_text = "Dev Info",
        profile_body = "Almost Completed Achievements and other World of Warcraft projects.\n\nClick any platform on the right to open a copyable link.",
        print = printAddonMessage,
    })

    return ns.devInfoModule
end

function ns.ShowDeveloperInfoPanel()
    local module = ns.EnsureDevInfoModule and ns.EnsureDevInfoModule()
    if module and type(module.ShowPanel) == "function" then
        return module:ShowPanel()
    end
    return false
end

local function importLegacy()
    if not ACAU_Config then
        ACAU_Config = {}
    end
    if ACA_ScanThreshold and not ACAU_Config.threshold then ACAU_Config.threshold = ACA_ScanThreshold end
    if ACA_FilterMode and not ACAU_Config.filterMode then ACAU_Config.filterMode = ACA_FilterMode end
    if ACA_ParseSpeed and not ACAU_Config.parseSpeed then ACAU_Config.parseSpeed = ACA_ParseSpeed end
    if ACA_AnchorSide and not ACAU_Config.anchorSide then ACAU_Config.anchorSide = ACA_AnchorSide end
    if ACA_SortMode and not ACAU_Config.sortMode then ACAU_Config.sortMode = ACA_SortMode end
    if ACA_PanelHidden ~= nil and ACAU_Config.panelHidden == nil then ACAU_Config.panelHidden = ACA_PanelHidden end
    if ACA_Top5DB and not ACAU_Top5DB then
        ACAU_Top5DB = Util:DeepCopy(ACA_Top5DB)
    end
    if ACA_CatFilterDB and not ACAU_CategoryDB then
        ACAU_CategoryDB = Util:DeepCopy(ACA_CatFilterDB)
    end
    if ACA_ExpFilterDB and not ACAU_ExpFilterDB then
        ACAU_ExpFilterDB = Util:DeepCopy(ACA_ExpFilterDB)
    end
    if ACA_IgnoreList and not ACAU_Ignore then
        ACAU_Ignore = Util:DeepCopy(ACA_IgnoreList)
    end
end

local function initDB()
    importLegacy()
    ACAU_Config = Util:MergeDefaults(ACAU_Config or {}, Const.DEFAULTS)
    ACAU_Global = Util:MergeDefaults(ACAU_Global or {}, {
        manualScanOnly = Const.DEFAULTS.manualScanOnly,
        toggleWithAchievementFrame = Const.DEFAULTS.toggleWithAchievementFrame,
        positionAccountWide = true,
        panelPoint = nil,
        top5Point = nil,
        ignoreAll = {},
    })
    if ACAU_Global.manualScanOnly == nil and ACAU_Config.manualScanOnly ~= nil then
        ACAU_Global.manualScanOnly = ACAU_Config.manualScanOnly
    end
    if ACAU_Global.toggleWithAchievementFrame == nil and ACAU_Config.toggleWithAchievementFrame ~= nil then
        ACAU_Global.toggleWithAchievementFrame = ACAU_Config.toggleWithAchievementFrame
    end
    if ACAU_Global.positionAccountWide and ACAU_Global.panelPoint == nil and ACAU_Config.panelPoint ~= nil then
        ACAU_Global.panelPoint = Util:DeepCopy(ACAU_Config.panelPoint)
    end
    ACAU_Ignore = ACAU_Ignore or {}
    ACAU_Cache = ACAU_Cache or {}
    ACAU_Top5DB = Util:MergeDefaults(ACAU_Top5DB or {}, Const.DEFAULTS.top5)
    if ACAU_Global.positionAccountWide and ACAU_Global.top5Point == nil and ACAU_Top5DB.point ~= nil then
        ACAU_Global.top5Point = Util:DeepCopy(ACAU_Top5DB.point)
    end
    ACAU_CategoryDB = Util:MergeDefaults(ACAU_CategoryDB or {}, Const.CATEGORY_DEFAULTS)
    ACAU_ExpFilterDB = Util:MergeDefaults(ACAU_ExpFilterDB or {}, Const.EXPANSION_DEFAULTS)
    Filters:ResetDB()
end

local Events = Util:CreateEventFrame()
ns.Events = Events

if Util:IsRetail() and Meta and Meta.Init then
    Meta:Init(Events)
end

local function rescanAndUpdate(force)
    if not force and (ACAU_Global and ACAU_Global.manualScanOnly) then
        return
    end
    local Panel = ns.Panel
    local Tracker = ns.Tracker
    local Scan = ns.Scan
    if not Scan or not Scan.Start then return end
    local function onComplete(list)
        ns.State.results = list
        ns.State.lastScan = time()
        if Panel and Panel.Render then Panel:Render(list) end
        if Panel and Panel.RefreshTop5Override then
            Panel:RefreshTop5Override(list)
        elseif Tracker and Tracker.Update then
            Tracker:Update(list)
        end
        ns.State.needsRescan = false
    end
    local function onProgress(done, total)
        if Panel and Panel.OnProgress then Panel:OnProgress(done, total) end
    end
    Scan:Start(onComplete, onProgress)
end
ns.TriggerScan = rescanAndUpdate

local function resetToDefaults()
    ACAU_Config = Util:MergeDefaults({}, Const.DEFAULTS)
    ACAU_CategoryDB = Util:MergeDefaults({}, Const.CATEGORY_DEFAULTS)
    ACAU_ExpFilterDB = Util:MergeDefaults({}, Const.EXPANSION_DEFAULTS)
    ACAU_Top5DB = Util:MergeDefaults({}, Const.DEFAULTS.top5)
    if Remix and Remix.Disable then Remix:Disable() end
    if ns.Panel and ns.Panel.ResetPosition then
        ns.Panel:ResetPosition()
    else
        ACAU_Config.panelPoint = nil
    end
    ACAU_Ignore = {}
    ACAU_Cache = {}
    Filters:ResetDB()
    if ns.Panel and ns.Panel.frame and ns.Panel.frame.progress and ns.Panel.frame.progress.Text then
        ns.Panel.frame.progress.Text:SetText(ACAU_Global.manualScanOnly and "Manual mode: press Rescan" or "Idle")
    end
    rescanAndUpdate(true)
end

Events:On("ADDON_LOADED", function(name)
    if name ~= ADDON_NAME then return end
    initDB()
    restoreCachedResults()
end)

Events:On("ACHIEVEMENT_EARNED", function(achID)
    if ns.CullEarnedAchievement then
        ns.CullEarnedAchievement(achID)
    end
end)

Events:On("PLAYER_ENTERING_WORLD", function()
    if Remix and Remix.MaybeAutoApply then Remix:MaybeAutoApply() end
    if ns.Panel and ns.Panel.Init then ns.Panel:Init(rescanAndUpdate) end
    if ns.Options and ns.Options.Register then ns.Options:Register(rescanAndUpdate) end
    if ns.Minimap and ns.Minimap.Init then ns.Minimap:Init() end
    if ns.Panel and ns.Panel.frame and ns.State.results and #ns.State.results > 0 then
        if ns.Panel.Render then ns.Panel:Render(ns.State.results) end
        if ns.Panel.RefreshTop5Override then
            ns.Panel:RefreshTop5Override(ns.State.results)
        elseif ns.Tracker and ns.Tracker.Update then
            ns.Tracker:Update(ns.State.results)
        end
    end
    if not ns.State.loginScanDone then
        rescanAndUpdate(false)
        ns.State.loginScanDone = true
    end
end)

local function handleSlash(msg)
    msg = tostring(msg or ""):lower()
    if msg == "scan" then
        rescanAndUpdate(true)
    elseif msg == "top" then
        if ns.Tracker and ns.Tracker.Toggle then ns.Tracker:Toggle() end
    elseif msg == "show" or msg == "open" then
        if ns.Panel and ns.Panel.frame then ns.Panel.frame:Show() else print("ACA: panel not ready yet.") end
    elseif msg == "hide" or msg == "close" then
        if ns.Panel and ns.Panel.frame then ns.Panel.frame:Hide() else print("ACA: panel not ready yet.") end
    elseif msg == "toggle" or msg == "" then
        if ns.Panel and ns.Panel.Toggle then ns.Panel:Toggle() else print("ACA: panel not ready yet.") end
    elseif msg == "reset" or msg == "default" then
        resetToDefaults()
        print("ACA: settings reset to defaults.")
    elseif msg == "options" or msg == "config" or msg == "settings" then
        openOptionsPanel()
    elseif msg == "devinfo" then
        ns.ShowDeveloperInfoPanel()
    else
        print("ACA commands:")
        print("/aca - toggle window")
        print("/aca show|hide - show or hide the window")
        print("/aca scan - run a scan now")
        print("/aca top - toggle Top 5 tracker")
        print("/aca reset - restore defaults")
        print("/aca options - open settings")
        print("/aca devinfo - open developer info")
    end
end

SLASH_ACA1 = "/aca"
SLASH_ACAU1 = "/acau"
SlashCmdList["ACA"] = handleSlash
SlashCmdList["ACAU"] = handleSlash
