AddonDiagnostics = AddonDiagnostics or {}
AddonDiagnostics.TestLoadout = {}
local TestLoadout = AddonDiagnostics.TestLoadout

local MAX_INSTALLED_ADDONS = 200
local MAX_ADDON_VERSION_CHARACTERS = 64
local SELF_ADDON_NAME = "AddonDiagnostics"

local baselineEnabledByIndex = {}
local pendingEnabledByIndex = {}

local function BoundText(value, maximumCharacters, fallback)
    local text = fallback
    if value ~= nil and value ~= "" then
        text = tostring(value)
    end

    if string.len(text) > maximumCharacters then
        return string.sub(text, 1, maximumCharacters)
    end

    return text
end

local function GetInstalledAddonCount()
    if C_AddOns and type(C_AddOns.GetNumAddOns) == "function" then
        return tonumber(C_AddOns.GetNumAddOns()) or 0
    end

    if type(GetNumAddOns) == "function" then
        return tonumber(GetNumAddOns()) or 0
    end

    return 0
end

local function GetInstalledAddonName(index)
    if C_AddOns and type(C_AddOns.GetAddOnInfo) == "function" then
        local okay, name = pcall(C_AddOns.GetAddOnInfo, index)
        if okay then
            return name
        end
    end

    if type(GetAddOnInfo) == "function" then
        local okay, name = pcall(GetAddOnInfo, index)
        if okay then
            return name
        end
    end

    return nil
end

local function GetInstalledAddonVersion(name)
    if C_AddOns and type(C_AddOns.GetAddOnMetadata) == "function" then
        local okay, version = pcall(C_AddOns.GetAddOnMetadata, name, "Version")
        if okay then
            return version
        end
    end

    if type(GetAddOnMetadata) == "function" then
        local okay, version = pcall(GetAddOnMetadata, name, "Version")
        if okay then
            return version
        end
    end

    return nil
end

local function GetCurrentCharacterIdentifier()
    if type(UnitGUID) ~= "function" then
        return nil
    end

    local identifier = UnitGUID("player")
    if type(identifier) ~= "string" or identifier == "" then
        return nil
    end

    return identifier
end

local function GetCurrentCharacterName()
    if type(UnitName) ~= "function" then
        return nil
    end

    local name = UnitName("player")
    if type(name) ~= "string" or name == "" then
        return nil
    end

    return name
end

local function GetAddonEnabledState(index)
    local characterIdentifier = GetCurrentCharacterIdentifier()
    if C_AddOns and type(C_AddOns.GetAddOnEnableState) == "function" and characterIdentifier then
        local okay, state = pcall(C_AddOns.GetAddOnEnableState, index, characterIdentifier)
        if okay and state ~= nil then
            return (tonumber(state) or 0) > 0, true
        end
    end

    local characterName = GetCurrentCharacterName()
    if type(GetAddOnEnableState) == "function" and characterName then
        local okay, state = pcall(GetAddOnEnableState, characterName, index)
        if okay and state ~= nil then
            return (tonumber(state) or 0) > 0, true
        end
    end

    return nil, false
end

local function SetAddonEnabledState(index, enabled)
    local characterIdentifier = GetCurrentCharacterIdentifier()
    local characterName = GetCurrentCharacterName()

    local modernFunction = nil
    local legacyFunction = nil
    if enabled then
        if C_AddOns then
            modernFunction = C_AddOns.EnableAddOn
        end
        legacyFunction = EnableAddOn
    else
        if C_AddOns then
            modernFunction = C_AddOns.DisableAddOn
        end
        legacyFunction = DisableAddOn
    end

    if type(modernFunction) == "function" and characterIdentifier then
        local okay = pcall(modernFunction, index, characterIdentifier)
        if okay then
            return true, nil
        end
    end

    if type(legacyFunction) == "function" and characterName then
        local okay = pcall(legacyFunction, characterName, index)
        if okay then
            return true, nil
        end
    end

    return false, "addon enable/disable API unavailable"
end

local function SaveAddonStates()
    if C_AddOns and type(C_AddOns.SaveAddOns) == "function" then
        local okay = pcall(C_AddOns.SaveAddOns)
        if okay then
            return true
        end
    end

    if type(SaveAddOns) == "function" then
        local okay = pcall(SaveAddOns)
        if okay then
            return true
        end
    end

    return false
end

local function CaptureBaseline(index, enabled)
    if baselineEnabledByIndex[index] == nil then
        baselineEnabledByIndex[index] = enabled == true
    end
end

local function GetDesiredState(index, baseline)
    if pendingEnabledByIndex[index] ~= nil then
        return pendingEnabledByIndex[index] == true
    end
    return baseline == true
end

function TestLoadout:GetInstalledAddons()
    local totalInstalled = GetInstalledAddonCount()
    local scanCount = totalInstalled
    if scanCount > MAX_INSTALLED_ADDONS then
        scanCount = MAX_INSTALLED_ADDONS
    end

    local items = {}
    local retainedCount = 0

    for index = 1, scanCount do
        local name = GetInstalledAddonName(index)
        if type(name) == "string" and name ~= "" then
            local item = {}
            item.index = index
            item.name = name
            item.version = BoundText(
                GetInstalledAddonVersion(name),
                MAX_ADDON_VERSION_CHARACTERS,
                "version unknown"
            )
            item.locked = name == SELF_ADDON_NAME

            local enabled, controlAvailable = GetAddonEnabledState(index)
            if item.locked then
                baselineEnabledByIndex[index] = true
                pendingEnabledByIndex[index] = nil
                item.enabled = true
                item.controlAvailable = true
            else
                if controlAvailable then
                    CaptureBaseline(index, enabled)
                    item.enabled = GetDesiredState(index, baselineEnabledByIndex[index])
                else
                    item.enabled = false
                end
                item.controlAvailable = controlAvailable == true
            end

            table.insert(items, item)
            retainedCount = retainedCount + 1
        end
    end

    return items, retainedCount, totalInstalled, totalInstalled > MAX_INSTALLED_ADDONS
end

function TestLoadout:SetAddonEnabled(index, name, enabled)
    if name == SELF_ADDON_NAME then
        return false, "Addon Diagnostics stays enabled while managing a test loadout"
    end

    local numericIndex = tonumber(index)
    if not numericIndex or numericIndex < 1 or numericIndex > MAX_INSTALLED_ADDONS then
        return false, "invalid addon index"
    end

    if InCombatLockdown and InCombatLockdown() then
        return false, "test-loadout changes are unavailable during combat"
    end

    local currentEnabled, controlAvailable = GetAddonEnabledState(numericIndex)
    if not controlAvailable then
        return false, "addon enable/disable API unavailable"
    end

    CaptureBaseline(numericIndex, currentEnabled)
    local desired = enabled == true
    if desired == baselineEnabledByIndex[numericIndex] then
        pendingEnabledByIndex[numericIndex] = nil
    else
        pendingEnabledByIndex[numericIndex] = desired
    end

    return true, nil
end

function TestLoadout:HasPendingChanges()
    for index = 1, MAX_INSTALLED_ADDONS do
        if pendingEnabledByIndex[index] ~= nil then
            return true
        end
    end
    return false
end

function TestLoadout:ApplyPendingChanges()
    if InCombatLockdown and InCombatLockdown() then
        return false, "test-loadout changes are unavailable during combat"
    end

    for index = 1, MAX_INSTALLED_ADDONS do
        local desired = pendingEnabledByIndex[index]
        if desired ~= nil then
            local changed, reason = SetAddonEnabledState(index, desired == true)
            if not changed then
                return false, reason
            end
        end
    end

    if TestLoadout:HasPendingChanges() and not SaveAddonStates() then
        return false, "unable to save addon enable/disable state"
    end

    return true, nil
end

function TestLoadout:IsSelfAddon(name)
    return name == SELF_ADDON_NAME
end

function TestLoadout:GetMaximumInstalledAddons()
    return MAX_INSTALLED_ADDONS
end
