GuildHelper = GuildHelper or {}

local addon = GuildHelper

local FEATURE_DEFAULTS = {
    bis = true,
    groups = true,
    mythics = true,
    raidbingo = true,
}

local FEATURE_TOGGLE_DEFINITIONS = {
    {
        description = "Hide Mythic+ and stop automatic live refreshes and party Mythic sync requests.",
        key = "mythics",
        label = "Mythic+",
    },
    {
        description = "Hide BIS and stop inventory scans, loot alerts, Great Vault checks, and BIS share messages.",
        key = "bis",
        label = "BIS",
    },
    {
        description = "Hide Raid Bingo and stop Raid Bingo group updates, addon message handling, and export access.",
        key = "raidbingo",
        label = "Raid Bingo",
    },
    {
        description = "Hide Groups and stop Groups login and guild-roster refresh processing.",
        key = "groups",
        label = "Groups",
    },
}

local VIEW_FEATURE_MAP = {
    bis = "bis",
    groups = "groups",
    mythics = "mythics",
    raidbingo = "raidbingo",
}

local function registerAddonPrefix(prefix)
    if C_ChatInfo and C_ChatInfo.RegisterAddonMessagePrefix then
        return C_ChatInfo.RegisterAddonMessagePrefix(prefix)
    end

    if RegisterAddonMessagePrefix then
        return RegisterAddonMessagePrefix(prefix)
    end

    return false
end

local function trimMessage(value)
    return string.lower((value or ""):match("^%s*(.-)%s*$"))
end

local function isInCombat()
    return InCombatLockdown and InCombatLockdown() and true or false
end

local function readAddonMetadataValue(addonName, fieldName)
    if type(addonName) ~= "string" or addonName == "" or type(fieldName) ~= "string" or fieldName == "" then
        return nil
    end

    if C_AddOns and C_AddOns.GetAddOnMetadata then
        local ok, value = pcall(C_AddOns.GetAddOnMetadata, addonName, fieldName)

        if ok and type(value) == "string" and value ~= "" then
            return value
        end
    end

    if GetAddOnMetadata then
        local ok, value = pcall(GetAddOnMetadata, addonName, fieldName)

        if ok and type(value) == "string" and value ~= "" then
            return value
        end
    end

    return nil
end

function addon:SetLoadedAddonName(addonName)
    if type(addonName) == "string" and addonName ~= "" then
        self.loadedAddonName = addonName
    end
end

function addon:GetKnownAddonNames()
    local names = {}
    local seen = {}

    local function addName(addonName)
        if type(addonName) ~= "string" or addonName == "" or seen[addonName] then
            return
        end

        seen[addonName] = true
        names[#names + 1] = addonName
    end

    addName(self.loadedAddonName)
    addName("GH3")
    addName("GuildHelper")

    return names
end

function addon:GetMetadata(fieldName)
    for _, addonName in ipairs(self:GetKnownAddonNames()) do
        local value = readAddonMetadataValue(addonName, fieldName)

        if value then
            return value, addonName
        end
    end

    return nil, nil
end

function addon:GetVersion()
    local version = self:GetMetadata("Version")
    return version or "0"
end

function addon:GetSyncMetadataVersion()
    local metadataValue = self:GetMetadata("X-Sync-Version")
    local syncVersion = tonumber(metadataValue)

    if syncVersion and syncVersion > 0 then
        return syncVersion
    end

    return nil
end

function addon:GetRuntime()
    self.runtime = self.runtime or {
        combatSuspended = false,
        instanceFeatureSuspended = false,
        reopenMainFrameAfterCombat = false,
    }

    return self.runtime
end

function addon:IsCombatPauseEnabled()
    if not (self.Store and self.Store.IsReady and self.Store:IsReady()) then
        return false
    end

    local combatProfile = self.Store:GetProfile().combat
    return combatProfile and combatProfile.pauseDuringCombat and true or false
end

function addon:SetCombatPauseEnabled(enabled)
    if not (self.Store and self.Store.IsReady and self.Store:IsReady()) then
        return false
    end

    local profile = self.Store:GetProfile()
    profile.combat = profile.combat or {}
    profile.combat.pauseDuringCombat = enabled and true or false
    return profile.combat.pauseDuringCombat
end

function addon:GetFeatureSettings()
    local profile = self.Store and self.Store.GetProfile and self.Store:GetProfile() or nil

    if not profile then
        return FEATURE_DEFAULTS
    end

    profile.features = type(profile.features) == "table" and profile.features or {}

    for key, defaultValue in pairs(FEATURE_DEFAULTS) do
        if profile.features[key] == nil then
            profile.features[key] = defaultValue and true or false
        else
            profile.features[key] = profile.features[key] and true or false
        end
    end

    return profile.features
end

function addon:IsFeatureEnabled(featureKey)
    if FEATURE_DEFAULTS[featureKey] == nil then
        return true
    end

    local settings = self:GetFeatureSettings()
    return settings[featureKey] and true or false
end

function addon:SetFeatureEnabled(featureKey, enabled)
    if FEATURE_DEFAULTS[featureKey] == nil then
        return false
    end

    self:GetFeatureSettings()[featureKey] = enabled and true or false
    return self:GetFeatureSettings()[featureKey]
end

function addon:GetFeatureToggleDefinitions()
    return FEATURE_TOGGLE_DEFINITIONS
end

function addon:GetFeatureKeyForView(viewKey)
    return VIEW_FEATURE_MAP[viewKey]
end

local function pauseBackgroundServices(service, options)
    local resolvedOptions = type(options) == "table" and options or {}

    if service.Reconciler and service.Reconciler.CancelActiveSyncs then
        service.Reconciler:CancelActiveSyncs("sync cancelled because background services were suspended")
    end

    if service.Transport and service.Transport.Pause then
        service.Transport:Pause()
    end

    if service.GratsUService and service.GratsUService.Pause then
        service.GratsUService:Pause(resolvedOptions)
    end

    if service.ToonCelebrationService and service.ToonCelebrationService.Pause then
        service.ToonCelebrationService:Pause(resolvedOptions)
    end
end

local function requestGuildRosterSnapshot()
    if C_GuildInfo and type(C_GuildInfo.GuildRoster) == "function" then
        local ok = pcall(C_GuildInfo.GuildRoster)

        if ok then
            return true
        end
    end

    if type(GuildRoster) == "function" then
        local ok = pcall(GuildRoster)

        if ok then
            return true
        end
    end

    return false
end

local function resumeBackgroundServices(service, options)
    local resolvedOptions = type(options) == "table" and options or {}

    if service.GratsUService and service.GratsUService.Resume then
        service.GratsUService:Resume(resolvedOptions)
    end

    if service.ToonCelebrationService and service.ToonCelebrationService.Resume then
        service.ToonCelebrationService:Resume(resolvedOptions)
    end

    if service.Transport and service.Transport.Resume then
        service.Transport:Resume()
    end
end

function addon:IsInRestrictedInstance()
    if not IsInInstance then
        return false
    end

    local inInstance, instanceType = IsInInstance()
    return inInstance and type(instanceType) == "string" and instanceType ~= "" and instanceType ~= "none" or false
end

function addon:IsInstanceFeatureSuspended()
    return self:GetRuntime().instanceFeatureSuspended and true or false
end

function addon:IsSyncSuspended()
    local runtime = self:GetRuntime()
    return (runtime.combatSuspended or runtime.instanceFeatureSuspended) and true or false
end

function addon:IsSuspended()
    return self:GetRuntime().combatSuspended and true or false
end

function addon:RefreshInstanceFeatureSuspension()
    local runtime = self:GetRuntime()
    local shouldSuspend = self:IsInRestrictedInstance()

    if shouldSuspend == (runtime.instanceFeatureSuspended and true or false) then
        return false
    end

    runtime.instanceFeatureSuspended = shouldSuspend

    if shouldSuspend then
        pauseBackgroundServices(self, { instanceSuspend = true })
        return true
    end

    if runtime.combatSuspended then
        runtime.pendingPostCombatInstanceResume = true
        return true
    end

    runtime.pendingPostCombatInstanceResume = false

    resumeBackgroundServices(self, { instanceResume = true })

    if self.initialized and self.Store and self.Store.IsReady and self.Store:IsReady() then
        self.Store:EnsureHomeGuild()
        requestGuildRosterSnapshot()

        if self.Reconciler and self.Reconciler.IsManualOnlySync and not self.Reconciler:IsManualOnlySync() and self.Reconciler.RequestGuildStatus then
            self.Reconciler:RequestGuildStatus()
        end

        if self.MainFrame and self.MainFrame.MarkBackgroundRefreshPending then
            self.MainFrame:MarkBackgroundRefreshPending()
        end
    end

    return true
end

function addon:EnterCombatSuspend()
    if self:IsSuspended() or not self:IsCombatPauseEnabled() then
        return false
    end

    local runtime = self:GetRuntime()
    runtime.combatSuspended = true
    runtime.reopenMainFrameAfterCombat = false

    pauseBackgroundServices(self, { combatSuspend = true })

    return true
end

function addon:ExitCombatSuspend()
    local runtime = self:GetRuntime()
    local shouldRunDeferredInstanceResume = runtime.pendingPostCombatInstanceResume and not runtime.instanceFeatureSuspended

    if not runtime.combatSuspended then
        return false
    end

    runtime.combatSuspended = false
    runtime.pendingPostCombatInstanceResume = false

    if not runtime.instanceFeatureSuspended then
        if shouldRunDeferredInstanceResume then
            resumeBackgroundServices(self, { instanceResume = true })
        else
            resumeBackgroundServices(self, { lightResume = true })
        end
    end

    if self.initialized and self.Store and self.Store.IsReady and self.Store:IsReady() then
        if shouldRunDeferredInstanceResume then
            self.Store:EnsureHomeGuild()
            requestGuildRosterSnapshot()

            if self.Reconciler and self.Reconciler.IsManualOnlySync and not self.Reconciler:IsManualOnlySync() and self.Reconciler.RequestGuildStatus then
                self.Reconciler:RequestGuildStatus()
            end
        end

        if self:IsFeatureEnabled("mythics") and self.MythicPlusService and self.MythicPlusService.RefreshCurrentCharacter then
            self.MythicPlusService:RefreshCurrentCharacter()
        end

        if not runtime.instanceFeatureSuspended and self:IsFeatureEnabled("raidbingo") and self.RaidBingoService and self.RaidBingoService.HandleGroupUpdate then
            self.RaidBingoService:HandleGroupUpdate()
        end

        if self.MainFrame and self.MainFrame.MarkBackgroundRefreshPending then
            self.MainFrame:MarkBackgroundRefreshPending()
        end
    end

    runtime.reopenMainFrameAfterCombat = false

    return true
end

function addon:Initialize()
    if self.initialized then
        return
    end

    if math and math.randomseed then
        math.randomseed(time())
        math.random()
        math.random()
        math.random()
    end

    addon.Store:Initialize()
    addon.ContentService:EnsureSeedData()
    local suppressNonEssentialInInstance = addon:IsInRestrictedInstance()

    if not suppressNonEssentialInInstance then
        addon.RosterService:CaptureGuildRoster()

        if addon.MaintenanceService and addon.MaintenanceService.MarkRosterScanDirty then
            addon.MaintenanceService:MarkRosterScanDirty()
        else
            addon.MaintenanceService:ScanRoster()
        end
    end

    if addon:IsFeatureEnabled("mythics") and addon.MythicPlusService and addon.MythicPlusService.RefreshCurrentCharacter then
        addon.MythicPlusService:RefreshCurrentCharacter(true)

        if addon.MythicPlusService.InitializeOverlay then
            addon.MythicPlusService:InitializeOverlay()
        end

        if C_Timer and C_Timer.After then
            C_Timer.After(3, function()
                if addon.initialized and addon:IsFeatureEnabled("mythics") and addon.MythicPlusService and addon.MythicPlusService.RefreshCurrentCharacter then
                    addon.MythicPlusService:RefreshCurrentCharacter(true)
                end
            end)

            SLASH_GUILDHELPERV3PREVIEW1 = "/gh3preview"
            SlashCmdList.GUILDHELPERV3PREVIEW = function()
                if addon.OpenNewUIPreview then
                    addon:OpenNewUIPreview()
                end
            end
        end
    end

    registerAddonPrefix(addon.Store:GetSync().prefix)
    registerAddonPrefix(addon.MaintenanceService:GetRecruiterSyncPrefix())
    registerAddonPrefix(addon.RaidBingoService:GetPrefix())
    registerAddonPrefix(addon.GratsUService:GetPrefix())
    registerAddonPrefix(addon.BISService:GetPrefix())
    registerAddonPrefix("LibKS")
    registerAddonPrefix("LRS")

    if addon.BISService and addon.BISService.InitializeBagTooltipAdvisor then
        addon.BISService:InitializeBagTooltipAdvisor()
    end

    addon.RaidBingoService:Initialize()
    addon.GratsUService:Initialize()
    addon.ToonCelebrationService:Initialize()

    SLASH_GUILDHELPERV31 = "/gh3"
    SlashCmdList.GUILDHELPERV3 = function(message)
        local rawCommand = (message or ""):match("^%s*(.-)%s*$") or ""
        local command = trimMessage(rawCommand)
        local commandKey, commandArgs = rawCommand:match("^(%S+)%s*(.-)$")
        local normalizedCommandKey = trimMessage(commandKey)
        local normalizedCommandArgs = trimMessage(commandArgs)

        if normalizedCommandKey == "perf" then
            local thresholdValue = string.match(normalizedCommandArgs, "^threshold%s+([%d%.]+)$")

            if normalizedCommandArgs == "" or normalizedCommandArgs == "report" or normalizedCommandArgs == "status" then
                addon:ShowPerfReport()
                return
            end

            if normalizedCommandArgs == "on" then
                addon:SetPerfProfilingEnabled(true)
                addon:Print("GH3 performance profiler enabled. Reproduce the spike, then run /gh3 perf report.")
                return
            end

            if normalizedCommandArgs == "off" then
                addon:SetPerfProfilingEnabled(false)
                addon:Print("GH3 performance profiler disabled.")
                return
            end

            if normalizedCommandArgs == "clear" then
                addon:ClearPerfStats()
                addon:Print("GH3 performance profiler samples cleared.")
                return
            end

            if thresholdValue then
                local thresholdMs = addon:SetPerfThresholdMs(tonumber(thresholdValue) or 0)
                addon:Print(string.format("GH3 performance profiler threshold set to %sms.", tostring(thresholdMs or 0)))
                return
            end

            addon:Print("Usage: /gh3 perf on | off | report | clear | threshold <ms>")
            return
        end

        if command == "combatpause" or command == "combatsuspend" then
            local enabled = addon:SetCombatPauseEnabled(not addon:IsCombatPauseEnabled())

            if enabled and isInCombat() then
                addon:EnterCombatSuspend()
            elseif not enabled and addon:IsSuspended() then
                addon:ExitCombatSuspend()
            end

            addon:Print(enabled and "Combat suspend enabled. GH3 will pause during combat and resume afterwards." or "Combat suspend disabled.")
            return
        end

        if command == "sync" then
            if addon.IsSyncSuspended and addon:IsSyncSuspended() then
                if addon.IsInstanceFeatureSuspended and addon:IsInstanceFeatureSuspended() then
                    addon:Print("GH3 sync is disabled while you are in an instance. It will resume after you leave.")
                else
                    addon:Print("GH3 is paused during combat. Sync will resume after combat ends.")
                end
                return
            end

            addon.Reconciler:RequestGuildStatus()
            addon:Print("Requested guild sync status from visible peers.")
            return
        end

        if command == "bingo" then
            if addon:IsSuspended() then
                addon:Print("GH3 is paused during combat. Raid Bingo will resume after combat ends.")
                return
            end

            if not addon:IsFeatureEnabled("raidbingo") then
                addon:Print("Raid Bingo is disabled in Settings.")
                addon.Router:Navigate("settings")
                addon.MainFrame:ShowForCurrentView()
                return
            end

            addon.Router:Navigate("raidbingo")
            addon.MainFrame:ShowForCurrentView()
            return
        end

        if command == "mythics" or command == "mplus" then
            if addon:IsSuspended() then
                addon:Print("GH3 is paused during combat. Mythic+ will resume after combat ends.")
                return
            end

            if not addon:IsFeatureEnabled("mythics") then
                addon:Print("Mythic+ is disabled in Settings.")
                addon.Router:Navigate("settings")
                addon.MainFrame:ShowForCurrentView()
                return
            end

            addon.Router:Navigate("mythics")
            addon.MainFrame:ShowForCurrentView()
            return
        end

        if normalizedCommandKey == "mplushud" or normalizedCommandKey == "mplusoverlay" or normalizedCommandKey == "mythichud" then
            if not (addon.MythicPlusService and addon.MythicPlusService.SetOverlayEnabled) then
                addon:Print("Mythic+ overlay is not available right now.")
                return
            end

            if normalizedCommandArgs == "on" or normalizedCommandArgs == "show" then
                addon.MythicPlusService:SetOverlayEnabled(true)
                addon:Print("Mythic+ overlay enabled. It will show during active keys.")
                return
            end

            if normalizedCommandArgs == "off" or normalizedCommandArgs == "hide" then
                addon.MythicPlusService:SetOverlayEnabled(false)
                addon:Print("Mythic+ overlay disabled.")
                return
            end

            if normalizedCommandArgs == "reset" then
                addon.MythicPlusService:ResetOverlayPosition()
                addon.MythicPlusService:SetOverlayEnabled(true)
                addon:Print("Mythic+ overlay position reset.")
                return
            end

            if normalizedCommandArgs == "lock" then
                addon.MythicPlusService:SetOverlayLocked(true)
                addon:Print("Mythic+ overlay locked.")
                return
            end

            if normalizedCommandArgs == "unlock" then
                addon.MythicPlusService:SetOverlayLocked(false)
                addon:Print("Mythic+ overlay unlocked. Drag it to move.")
                return
            end

            local enabled = addon.MythicPlusService:ToggleOverlayEnabled()
            addon:Print(enabled and "Mythic+ overlay enabled. It will show during active keys." or "Mythic+ overlay disabled.")
            return
        end

        if normalizedCommandKey == "mythicdebug" or normalizedCommandKey == "mplusdebug" then
            if addon.MythicPlusService and addon.MythicPlusService.GetDebugReportLines then
                for _, line in ipairs(addon.MythicPlusService:GetDebugReportLines()) do
                    addon:Print(line)
                end
            end
            return
        end

        if normalizedCommandKey == "mplussummary" or normalizedCommandKey == "mythicsummary" then
            if addon.MythicPlusService and addon.MythicPlusService.GetRuntimeState and addon.MythicPlusService.RenderMythicPlusCompletionSummary then
                local runtime = addon.MythicPlusService:GetRuntimeState().summary

                if type(runtime.run) == "table" and (tonumber(runtime.completedAt) or 0) > 0 then
                    addon.MythicPlusService:RenderMythicPlusCompletionSummary()
                else
                    addon:Print("No completed Mythic+ summary is available yet.")
                end
            end
            return
        end

        if command == "bis" then
            if addon:IsSuspended() then
                addon:Print("GH3 is paused during combat. BIS will resume after combat ends.")
                return
            end

            if not addon:IsFeatureEnabled("bis") then
                addon:Print("BIS is disabled in Settings.")
                addon.Router:Navigate("settings")
                addon.MainFrame:ShowForCurrentView()
                return
            end

            addon.Router:Navigate("bis")
            addon.MainFrame:ShowForCurrentView()
            return
        end

        if command == "cleanup" or command == "clean" then
            if InCombatLockdown and InCombatLockdown() then
                addon:Print("GH3 Helper popup is unavailable during combat.")
                return
            end

            if not (addon.MaintenanceService and addon.MaintenanceService.CanGuildRemoveMembers and addon.MaintenanceService:CanGuildRemoveMembers()) then
                addon:Print("GH3 Helper is only available when you can remove guild members.")
                return
            end

            if addon.MainFrame and addon.MainFrame.ShowGuildCleanupDialog then
                addon.MainFrame:ShowGuildCleanupDialog("kick")
            end
            return
        end

        if command == "loot" or command == "loothelper" then
            if addon:IsSuspended() then
                addon:Print("GH3 is paused during combat. Loot Helper will resume after combat ends.")
                return
            end

            if not addon:IsFeatureEnabled("bis") then
                addon:Print("BIS is disabled in Settings.")
                addon.Router:Navigate("settings")
                addon.MainFrame:ShowForCurrentView()
                return
            end

            addon.Router:Navigate("bis")
            addon.MainFrame:ShowForCurrentView()

            if addon.MainFrame and addon.MainFrame.ShowBISLootHelperDialog then
                addon.MainFrame:ShowBISLootHelperDialog("GH3 Loot Helper")
            end
            return
        end

        if command == "loottest" or command == "lootpreview" or command == "lootest" then
            if addon:IsSuspended() then
                addon:Print("GH3 is paused during combat. Loot Helper preview will resume after combat ends.")
                return
            end

            if not addon:IsFeatureEnabled("bis") then
                addon:Print("BIS is disabled in Settings.")
                addon.Router:Navigate("settings")
                addon.MainFrame:ShowForCurrentView()
                return
            end

            if addon.BISService and addon.BISService.QueueLootHelperPreviewRows then
                addon.BISService:QueueLootHelperPreviewRows()
            end

            addon.Router:Navigate("bis")
            addon.MainFrame:ShowForCurrentView()

            if addon.NewUIBIS and addon.NewUIBIS.ShowDropReviewDialog then
                addon.NewUIBIS:ShowDropReviewDialog()
            end

            addon:Print("Loot Helper preview rows loaded. Use /gh3 loottestclear or /gh3 lootpreviewclear to remove them.")
            return
        end

        if command == "loottestclear" or command == "lootpreviewclear" then
            if addon.BISService and addon.BISService.ClearDropReviewRows then
                addon.BISService:ClearDropReviewRows()
            end

            if addon.NewUIBIS and addon.NewUIBIS.HideDropReviewDialog then
                addon.NewUIBIS:HideDropReviewDialog()
            end

            addon:Print("Loot Helper preview rows cleared.")
            return
        end

        if command == "gratsu" then
            if addon:IsSuspended() then
                addon:Print("GH3 is paused during combat. GratsU will resume after combat ends.")
                return
            end

            if not addon.GratsUService:CanAccess() then
                addon:Print("GratsU is only available to officers and the guild master.")
                return
            end

            addon.Router:Navigate("gratsu")
            addon.MainFrame:ShowForCurrentView()
            return
        end

        if command == "bingoexport" then
            if addon:IsSuspended() then
                addon:Print("GH3 is paused during combat. Raid Bingo export is unavailable until combat ends.")
                return
            end

            if not addon:IsFeatureEnabled("raidbingo") then
                addon:Print("Raid Bingo is disabled in Settings.")
                addon.Router:Navigate("settings")
                addon.MainFrame:ShowForCurrentView()
                return
            end

            addon.RaidBingoService:ShowExportDialog()
            return
        end

        if command == "minimap" then
            local hidden = addon.MinimapButton:ToggleVisibility()
            addon:Print(hidden and "Minimap button hidden." or "Minimap button shown.")
            return
        end

        addon.MainFrame:Toggle()
    end

    addon.MainFrame:Initialize()
    addon.MinimapButton:Initialize()
    self.initialized = true
    self:RefreshInstanceFeatureSuspension()
    addon:Print("Initialized.")
end
