local _, ns = ...
local L = ns.L or {}

local FEATURE_ID = "blizzard_window_mover"
local MIN_SCALE = 0.75
local MAX_SCALE = 1.35
local SCALE_STEP = 0.05
local SCALE_CAPTURE_STRATA = "TOOLTIP"
local SCALE_CAPTURE_LEVEL = 9999
local NO_SCALE_HARD_RESET_VERSION = 2

local FRAME_STRATA_RANK = {
    BACKGROUND = 1,
    LOW = 2,
    MEDIUM = 3,
    HIGH = 4,
    DIALOG = 5,
    FULLSCREEN = 6,
    FULLSCREEN_DIALOG = 7,
    TOOLTIP = 8,
}

local COMMON_GRIP_CHILD_PATHS = {
    "TitleContainer",
    "Header",
    "Header.TitleContainer",
}

local NO_SCALE_FRAME_KEYS = {
    CollectionsJournal = true,
}

local SKIP_FRAME_KEYS = {
    WarbandCollectionFrame = true,
    WarbandCollectionsFrame = true,
    WarbandSceneCollectionFrame = true,
    WarbandSceneJournalFrame = true,
    WardrobeCollectionFrame = true,
    WardrobeFrame = true,
}

local CORE_WINDOWS = {
    { key = "AddonList", frame = "AddonList" },
    { key = "BankFrame", frame = "BankFrame" },
    { key = "CharacterFrame", frame = "CharacterFrame" },
    { key = "ChatConfigFrame", frame = "ChatConfigFrame" },
    { key = "DressUpFrame", frame = "DressUpFrame" },
    { key = "FriendsFrame", frame = "FriendsFrame" },
    { key = "GameMenuFrame", frame = "GameMenuFrame" },
    { key = "GossipFrame", frame = "GossipFrame" },
    { key = "HelpFrame", frame = "HelpFrame" },
    { key = "ItemTextFrame", frame = "ItemTextFrame" },
    { key = "LootFrame", frame = "LootFrame" },
    { key = "MailFrame", frame = "MailFrame" },
    { key = "MerchantFrame", frame = "MerchantFrame" },
    { key = "PVEFrame", frame = "PVEFrame" },
    { key = "QuestFrame", frame = "QuestFrame" },
    { key = "QuestLogDetailFrame", frame = "QuestLogDetailFrame" },
    { key = "QuestLogFrame", frame = "QuestLogFrame" },
    { key = "QuestLogPopupDetailFrame", frame = "QuestLogPopupDetailFrame" },
    { key = "QuickKeybindFrame", frame = "QuickKeybindFrame" },
    { key = "ReadyCheckFrame", frame = "ReadyCheckFrame" },
    { key = "RecruitAFriendRecruitmentFrame", frame = "RecruitAFriendRecruitmentFrame" },
    { key = "RecruitAFriendRewardsFrame", frame = "RecruitAFriendRewardsFrame" },
    { key = "SettingsPanel", frame = "SettingsPanel" },
    { key = "SpellBookFrame", frame = "SpellBookFrame" },
    { key = "SplashFrame", frame = "SplashFrame" },
    { key = "TabardFrame", frame = "TabardFrame" },
    { key = "TalkingHeadFrame", frame = "TalkingHeadFrame" },
    { key = "TaxiFrame", frame = "TaxiFrame" },
    { key = "TradeFrame", frame = "TradeFrame" },
    { key = "TutorialFrame", frame = "TutorialFrame" },
    { key = "WorldMapFrame", frame = "WorldMapFrame" },
    { key = "WorldStateScoreFrame", frame = "WorldStateScoreFrame" },
}

local ADDON_WINDOWS = {
    Blizzard_AccountStore = {
        { key = "AccountStoreFrame", frame = "AccountStoreFrame" },
    },
    Blizzard_AchievementUI = {
        { key = "AchievementFrame", frame = "AchievementFrame" },
    },
    Blizzard_AlliedRacesUI = {
        { key = "AlliedRacesFrame", frame = "AlliedRacesFrame" },
    },
    Blizzard_AnimaDiversionUI = {
        { key = "AnimaDiversionFrame", frame = "AnimaDiversionFrame" },
    },
    Blizzard_ArchaeologyUI = {
        { key = "ArchaeologyFrame", frame = "ArchaeologyFrame" },
        { key = "ArcheologyDigsiteProgressBar", frame = "ArcheologyDigsiteProgressBar" },
    },
    Blizzard_ArtifactUI = {
        { key = "ArtifactFrame", frame = "ArtifactFrame" },
        { key = "ArtifactRelicForgeFrame", frame = "ArtifactRelicForgeFrame" },
    },
    Blizzard_AuctionHouseUI = {
        { key = "AuctionHouseFrame", frame = "AuctionHouseFrame" },
    },
    Blizzard_AuctionUI = {
        { key = "AuctionFrame", frame = "AuctionFrame" },
    },
    Blizzard_AzeriteEssenceUI = {
        { key = "AzeriteEssenceUI", frame = "AzeriteEssenceUI" },
    },
    Blizzard_AzeriteRespecUI = {
        { key = "AzeriteRespecFrame", frame = "AzeriteRespecFrame" },
    },
    Blizzard_AzeriteUI = {
        { key = "AzeriteEmpoweredItemUI", frame = "AzeriteEmpoweredItemUI" },
    },
    Blizzard_BarbershopUI = {
        { key = "BarberShopFrame", frame = "BarberShopFrame" },
    },
    Blizzard_BattlefieldMap = {
        { key = "BattlefieldMapFrame", frame = "BattlefieldMapFrame" },
    },
    Blizzard_BindingUI = {
        { key = "KeyBindingFrame", frame = "KeyBindingFrame" },
    },
    Blizzard_BlackMarketUI = {
        { key = "BlackMarketFrame", frame = "BlackMarketFrame" },
    },
    Blizzard_Calendar = {
        { key = "CalendarFrame", frame = "CalendarFrame" },
    },
    Blizzard_ChallengesUI = {
        { key = "ChallengesFrame", frame = "ChallengesFrame" },
    },
    Blizzard_ChromieTimeUI = {
        { key = "ChromieTimeFrame", frame = "ChromieTimeFrame" },
    },
    Blizzard_ClassTalentUI = {
        { key = "ClassTalentFrame", frame = "ClassTalentFrame" },
    },
    Blizzard_Collections = {
        { key = "CollectionsJournal", frame = "CollectionsJournal", allowScale = false },
    },
    Blizzard_Contribution = {
        { key = "ContributionCollectionFrame", frame = "ContributionCollectionFrame" },
    },
    Blizzard_CovenantCallings = {
        { key = "CovenantCallingFrame", frame = "CovenantCallingFrame" },
    },
    Blizzard_CovenantPreviewUI = {
        { key = "CovenantPreviewFrame", frame = "CovenantPreviewFrame" },
    },
    Blizzard_CovenantRenown = {
        { key = "CovenantRenownFrame", frame = "CovenantRenownFrame" },
    },
    Blizzard_CovenantSanctum = {
        { key = "CovenantSanctumFrame", frame = "CovenantSanctumFrame" },
    },
    Blizzard_DeathRecap = {
        { key = "DeathRecapFrame", frame = "DeathRecapFrame" },
    },
    Blizzard_EncounterJournal = {
        { key = "EncounterJournal", frame = "EncounterJournal" },
    },
    Blizzard_ExpansionLandingPage = {
        { key = "ExpansionLandingPage", frame = "ExpansionLandingPage" },
    },
    Blizzard_FlightMap = {
        { key = "FlightMapFrame", frame = "FlightMapFrame" },
    },
    Blizzard_GarrisonUI = {
        { key = "GarrisonLandingPage", frame = "GarrisonLandingPage" },
        { key = "GarrisonMissionFrame", frame = "GarrisonMissionFrame" },
        { key = "OrderHallMissionFrame", frame = "OrderHallMissionFrame" },
        { key = "GarrisonCapacitiveDisplayFrame", frame = "GarrisonCapacitiveDisplayFrame" },
        { key = "GarrisonRecruiterFrame", frame = "GarrisonRecruiterFrame" },
        { key = "GarrisonBuildingFrame", frame = "GarrisonBuildingFrame" },
        { key = "GarrisonShipyardFrame", frame = "GarrisonShipyardFrame" },
    },
    Blizzard_GuildBankUI = {
        { key = "GuildBankFrame", frame = "GuildBankFrame" },
    },
    Blizzard_GuildControlUI = {
        { key = "GuildControlUI", frame = "GuildControlUI" },
    },
    Blizzard_GuildUI = {
        { key = "GuildFrame", frame = "GuildFrame" },
    },
    Blizzard_HouseList = {
        { key = "HouseListFrame", frame = "HouseListFrame" },
    },
    Blizzard_HousingBulletinBoard = {
        { key = "HousingBulletinBoardFrame", frame = "HousingBulletinBoardFrame" },
        { key = "HousingInviteResidentFrame", frame = "HousingInviteResidentFrame" },
        { key = "NeighborhoodChangeNameDialog", frame = "NeighborhoodChangeNameDialog" },
    },
    Blizzard_HousingCharter = {
        { key = "HousingCharterRequestSignatureDialog", frame = "HousingCharterRequestSignatureDialog" },
    },
    Blizzard_HousingCornerstone = {
        { key = "HousingCornerstoneFrame", frame = "HousingCornerstoneFrame" },
        { key = "HousingCornerstoneHouseInfoFrame", frame = "HousingCornerstoneHouseInfoFrame" },
        { key = "HousingCornerstonePurchaseFrame", frame = "HousingCornerstonePurchaseFrame" },
        { key = "HousingCornerstoneVisitorFrame", frame = "HousingCornerstoneVisitorFrame" },
        { key = "ImportHouseConfirmationDialog", frame = "ImportHouseConfirmationDialog" },
        { key = "MoveHouseConfirmationDialog", frame = "MoveHouseConfirmationDialog" },
    },
    Blizzard_HousingCreateNeighborhood = {
        { key = "HousingCreateCharterNeighborhoodConfirmationFrame", frame = "HousingCreateCharterNeighborhoodConfirmationFrame" },
        { key = "HousingCreateNeighborhoodCharterFrame", frame = "HousingCreateNeighborhoodCharterFrame" },
    },
    Blizzard_HousingDashboard = {
        { key = "HousingDashboardFrame", frame = "HousingDashboardFrame" },
    },
    Blizzard_HousingHouseFinder = {
        { key = "HouseFinderFrame", frame = "HouseFinderFrame" },
    },
    Blizzard_HousingHouseSettings = {
        { key = "AbandonHouseConfirmationDialog", frame = "AbandonHouseConfirmationDialog" },
        { key = "HousingHouseSettingsFrame", frame = "HousingHouseSettingsFrame" },
    },
    Blizzard_HousingModelPreview = {
        { key = "HousingModelPreviewFrame", frame = "HousingModelPreviewFrame" },
    },
    Blizzard_InspectUI = {
        { key = "InspectFrame", frame = "InspectFrame" },
    },
    Blizzard_IslandsQueueUI = {
        { key = "IslandsQueueFrame", frame = "IslandsQueueFrame" },
    },
    Blizzard_ItemInteractionUI = {
        { key = "ItemInteractionFrame", frame = "ItemInteractionFrame" },
    },
    Blizzard_ItemSocketingUI = {
        { key = "ItemSocketingFrame", frame = "ItemSocketingFrame" },
    },
    Blizzard_ItemUpgradeUI = {
        { key = "ItemUpgradeFrame", frame = "ItemUpgradeFrame" },
    },
    Blizzard_MacroUI = {
        { key = "MacroFrame", frame = "MacroFrame" },
    },
    Blizzard_MajorFactions = {
        { key = "MajorFactionRenownFrame", frame = "MajorFactionRenownFrame" },
    },
    Blizzard_ObliterumUI = {
        { key = "ObliterumForgeFrame", frame = "ObliterumForgeFrame" },
    },
    Blizzard_PerksProgram = {
        { key = "PerksProgramFrame", frame = "PerksProgramFrame" },
    },
    Blizzard_PlayerChoiceUI = {
        { key = "PlayerChoiceFrame", frame = "PlayerChoiceFrame" },
    },
    Blizzard_PlayerSpells = {
        { key = "PlayerSpellsFrame", frame = "PlayerSpellsFrame" },
    },
    Blizzard_Professions = {
        { key = "ProfessionsFrame", frame = "ProfessionsFrame" },
        { key = "InspectRecipeFrame", frame = "InspectRecipeFrame" },
    },
    Blizzard_ProfessionsBook = {
        { key = "ProfessionsBookFrame", frame = "ProfessionsBookFrame" },
    },
    Blizzard_ProfessionsCustomerOrders = {
        { key = "ProfessionsCustomerOrdersFrame", frame = "ProfessionsCustomerOrdersFrame" },
    },
    Blizzard_PVPMatch = {
        { key = "PVPMatchResults", frame = "PVPMatchResults" },
    },
    Blizzard_PVPUI = {
        { key = "PVPMatchScoreboard", frame = "PVPMatchScoreboard" },
    },
    Blizzard_RemixArtifactUI = {
        { key = "RemixArtifactFrame", frame = "RemixArtifactFrame" },
    },
    Blizzard_RuneforgeUI = {
        { key = "RuneforgeFrame", frame = "RuneforgeFrame" },
    },
    Blizzard_ScrappingMachineUI = {
        { key = "ScrappingMachineFrame", frame = "ScrappingMachineFrame" },
    },
    Blizzard_Soulbinds = {
        { key = "SoulbindViewer", frame = "SoulbindViewer" },
    },
    Blizzard_StableUI = {
        { key = "StableFrame", frame = "StableFrame" },
    },
    Blizzard_SubscriptionInterstitialUI = {
        { key = "SubscriptionInterstitialFrame", frame = "SubscriptionInterstitialFrame" },
    },
    Blizzard_TalentUI = {
        { key = "PlayerTalentFrame", frame = "PlayerTalentFrame" },
        { key = "TalentFrame", frame = "TalentFrame" },
    },
    Blizzard_TimeManager = {
        { key = "TimeManagerFrame", frame = "TimeManagerFrame" },
    },
    Blizzard_TokenUI = {
        { key = "CurrencyTransferMenu", frame = "CurrencyTransferMenu" },
    },
    Blizzard_TorghastLevelPicker = {
        { key = "TorghastLevelPickerFrame", frame = "TorghastLevelPickerFrame" },
    },
    Blizzard_TrainerUI = {
        { key = "ClassTrainerFrame", frame = "ClassTrainerFrame" },
    },
    Blizzard_TradeSkillUI = {
        { key = "TradeSkillFrame", frame = "TradeSkillFrame" },
    },
    Blizzard_Transmog = {
        { key = "TransmogFrame", frame = "TransmogFrame" },
    },
    Blizzard_VoidStorageUI = {
        { key = "VoidStorageFrame", frame = "VoidStorageFrame" },
    },
    Blizzard_WarfrontsPartyPoseUI = {
        { key = "WarfrontsPartyPoseFrame", frame = "WarfrontsPartyPoseFrame" },
    },
    Blizzard_WeeklyRewards = {
        { key = "WeeklyRewardsFrame", frame = "WeeklyRewardsFrame" },
    },
    Blizzard_WorldMap = {
        { key = "WorldMapFrame", frame = "WorldMapFrame" },
    },
}

local CONTAINER_WINDOWS = {
    { key = "ContainerFrameCombinedBags", frame = "ContainerFrameCombinedBags" },
}

for index = 1, 13 do
    CONTAINER_WINDOWS[#CONTAINER_WINDOWS + 1] = {
        key = "ContainerFrame" .. index,
        frame = "ContainerFrame" .. index,
    }
end

local Runtime = {
    enabled = false,
    warnedCompat = false,
    containerHooked = false,
    panelShowHooked = false,
    records = {},
    recordsByFrame = {},
    sessionPositions = {},
    sessionScales = {},
}

local function safeFrameCall(frame, methodName, ...)
    local method = frame and frame[methodName]
    if type(method) ~= "function" then
        return nil
    end

    local ok, a, b, c, d, e = pcall(method, frame, ...)
    if ok then
        return a, b, c, d, e
    end
    return nil
end

local function isFrame(frame)
    if not frame then
        return false
    end

    local isObjectType = frame.IsObjectType
    if type(isObjectType) ~= "function" then
        return false
    end

    local ok, result = pcall(isObjectType, frame, "Frame")
    return ok and result == true
end

local function isForbidden(frame)
    return safeFrameCall(frame, "IsForbidden") == true
end

local function isProtected(frame)
    return safeFrameCall(frame, "IsProtected") == true
end

local function isCombatBlocked(frame)
    return InCombatLockdown and InCombatLockdown() and isProtected(frame)
end

local function clampScale(value)
    value = tonumber(value) or 1
    if value < MIN_SCALE then
        return MIN_SCALE
    end
    if value > MAX_SCALE then
        return MAX_SCALE
    end
    return value
end

local function roundOne(value)
    value = tonumber(value) or 0
    if value >= 0 then
        return math.floor((value * 10) + 0.5) / 10
    end
    return math.ceil((value * 10) - 0.5) / 10
end

local function isAddonLoaded(addonName)
    if C_AddOns and type(C_AddOns.IsAddOnLoaded) == "function" then
        return C_AddOns.IsAddOnLoaded(addonName) == true
    end
    if type(IsAddOnLoaded) == "function" then
        return IsAddOnLoaded(addonName) == true
    end
    return false
end

local function resolveFrame(framePath)
    if type(framePath) ~= "string" or framePath == "" then
        return nil
    end

    local current = _G
    for token in string.gmatch(framePath, "[^%.]+") do
        current = current and current[token]
    end

    if isFrame(current) and not isForbidden(current) then
        return current
    end
    return nil
end

local function resolveChildFrame(parent, framePath)
    if not parent or type(framePath) ~= "string" or framePath == "" then
        return nil
    end

    local current = parent
    for token in string.gmatch(framePath, "[^%.]+") do
        current = current and current[token]
    end

    if isFrame(current) and not isForbidden(current) then
        return current
    end
    return nil
end

local function isFrameVisible(frame)
    local visible = safeFrameCall(frame, "IsVisible")
    if visible ~= nil then
        return visible == true
    end
    return safeFrameCall(frame, "IsShown") == true
end

local function isMouseOverFrame(frame)
    if not frame or not isFrameVisible(frame) then
        return false
    end

    if type(MouseIsOver) == "function" then
        local ok, result = pcall(MouseIsOver, frame)
        if ok then
            return result == true
        end
    end

    return safeFrameCall(frame, "IsMouseOver") == true
end

local function isScaleBlockedKey(frameKey)
    return type(frameKey) == "string" and NO_SCALE_FRAME_KEYS[frameKey] == true
end

local function isSkippedFrameKey(frameKey)
    return type(frameKey) == "string" and SKIP_FRAME_KEYS[frameKey] == true
end

local function getFrameStackRank(frame)
    local strata = safeFrameCall(frame, "GetFrameStrata")
    local strataRank = FRAME_STRATA_RANK[strata] or 0
    local level = tonumber(safeFrameCall(frame, "GetFrameLevel")) or 0
    return (strataRank * 100000) + level
end

local function capturePoints(frame)
    local count = tonumber(safeFrameCall(frame, "GetNumPoints")) or 0
    if count <= 0 then
        return nil
    end

    local points = {}
    for index = 1, count do
        local point, relativeTo, relativePoint, x, y = safeFrameCall(frame, "GetPoint", index)
        if point then
            points[#points + 1] = {
                point = point,
                relativeTo = relativeTo,
                relativePoint = relativePoint or point,
                x = tonumber(x) or 0,
                y = tonumber(y) or 0,
            }
        end
    end

    return #points > 0 and points or nil
end

local function applyPoints(frame, points)
    if not frame or type(points) ~= "table" or isCombatBlocked(frame) then
        return false
    end

    safeFrameCall(frame, "ClearAllPoints")
    for _, point in ipairs(points) do
        safeFrameCall(
            frame,
            "SetPoint",
            point.point or "CENTER",
            point.relativeTo or UIParent,
            point.relativePoint or point.point or "CENTER",
            tonumber(point.x) or 0,
            tonumber(point.y) or 0
        )
    end

    return true
end

local function hookScript(frame, scriptName, handler)
    if not frame or type(frame.HookScript) ~= "function" then
        return false
    end

    if type(frame.HasScript) == "function" and safeFrameCall(frame, "HasScript", scriptName) ~= true then
        return false
    end

    local ok = pcall(frame.HookScript, frame, scriptName, handler)
    return ok == true
end

function Runtime:Notify(message)
    if type(message) ~= "string" or message == "" then
        return
    end

    if ns.ShowFeatureError then
        ns:ShowFeatureError(message)
    elseif UIErrorsFrame and type(UIErrorsFrame.AddMessage) == "function" then
        UIErrorsFrame:AddMessage(message, 1, 0.82, 0.22)
    elseif ns.Print then
        ns:Print(message)
    end
end

function Runtime:GetSaveMode()
    if ns.GetFeatureOptionValue then
        return ns:GetFeatureOptionValue(FEATURE_ID, "save_mode") or "session"
    end
    return "session"
end

function Runtime:IsScaleEnabled()
    if ns.GetFeatureOptionValue then
        return ns:GetFeatureOptionValue(FEATURE_ID, "scale_mode") ~= "off"
    end
    return true
end

function Runtime:CanScaleRecord(record)
    return type(record) == "table" and record.allowScale ~= false and not isScaleBlockedKey(record.key)
end

function Runtime:GetStorage()
    if not ns.EnsureFeatureDB then
        return nil
    end

    local db = ns:EnsureFeatureDB()
    if type(db) ~= "table" then
        return nil
    end

    db.blizzardWindowMover = type(db.blizzardWindowMover) == "table" and db.blizzardWindowMover or {}
    db.blizzardWindowMover.positions = type(db.blizzardWindowMover.positions) == "table" and db.blizzardWindowMover.positions or {}
    db.blizzardWindowMover.scales = type(db.blizzardWindowMover.scales) == "table" and db.blizzardWindowMover.scales or {}
    return db.blizzardWindowMover
end

function Runtime:GetPosition(frameKey)
    if self:GetSaveMode() == "permanent" then
        local storage = self:GetStorage()
        return storage and storage.positions and storage.positions[frameKey] or nil
    end
    return self.sessionPositions[frameKey]
end

function Runtime:GetScale(frameKey)
    if self:GetSaveMode() == "permanent" then
        local storage = self:GetStorage()
        return storage and storage.scales and storage.scales[frameKey] or nil
    end
    return self.sessionScales[frameKey]
end

function Runtime:SavePosition(record)
    if type(record) ~= "table" or not record.frame or not record.key then
        return
    end

    local centerX, centerY = safeFrameCall(record.frame, "GetCenter")
    if not centerX or not centerY then
        return
    end

    local position = {
        point = "CENTER",
        relativePoint = "BOTTOMLEFT",
        x = roundOne(centerX),
        y = roundOne(centerY),
    }

    if self:GetSaveMode() == "permanent" then
        local storage = self:GetStorage()
        if storage then
            storage.positions[record.key] = position
        end
    else
        self.sessionPositions[record.key] = position
    end
end

function Runtime:SaveScale(record, scale)
    if type(record) ~= "table" or not record.key then
        return
    end
    if not self:CanScaleRecord(record) then
        self:ClearStoredScale(record)
        return
    end

    scale = clampScale(scale)
    if self:GetSaveMode() == "permanent" then
        local storage = self:GetStorage()
        if storage then
            storage.scales[record.key] = scale
        end
    else
        self.sessionScales[record.key] = scale
    end
end

function Runtime:ClearStoredPosition(record)
    if type(record) ~= "table" or not record.key then
        return
    end

    self.sessionPositions[record.key] = nil
    local storage = self:GetStorage()
    if storage and storage.positions then
        storage.positions[record.key] = nil
    end
end

function Runtime:ClearStoredScale(record)
    if type(record) ~= "table" or not record.key then
        return
    end

    self.sessionScales[record.key] = nil
    local storage = self:GetStorage()
    if storage and storage.scales then
        storage.scales[record.key] = nil
    end
end

function Runtime:ClearStoredFrameKey(frameKey)
    if type(frameKey) ~= "string" or frameKey == "" then
        return
    end

    self.sessionPositions[frameKey] = nil
    self.sessionScales[frameKey] = nil

    local storage = self:GetStorage()
    if storage then
        if storage.positions then
            storage.positions[frameKey] = nil
        end
        if storage.scales then
            storage.scales[frameKey] = nil
        end
    end
end

function Runtime:ClearStoredScaleKey(frameKey)
    if type(frameKey) ~= "string" or frameKey == "" then
        return
    end

    self.sessionScales[frameKey] = nil

    local storage = self:GetStorage()
    if storage and storage.scales then
        storage.scales[frameKey] = nil
    end
end

function Runtime:ClearNoScaleFrameStorage()
    for frameKey in pairs(NO_SCALE_FRAME_KEYS) do
        self:ClearStoredScaleKey(frameKey)
    end

    for frameKey in pairs(SKIP_FRAME_KEYS) do
        self:ClearStoredFrameKey(frameKey)
    end
end

function Runtime:EnsureNoScaleFrameHardReset()
    local storage = self:GetStorage()
    if storage and storage.noScaleHardResetVersion == NO_SCALE_HARD_RESET_VERSION then
        return
    end

    for frameKey in pairs(NO_SCALE_FRAME_KEYS) do
        self:ClearStoredFrameKey(frameKey)
    end
    for frameKey in pairs(SKIP_FRAME_KEYS) do
        self:ClearStoredFrameKey(frameKey)
    end

    if storage then
        storage.noScaleHardResetVersion = NO_SCALE_HARD_RESET_VERSION
    end
end

function Runtime:ApplyStoredState(record)
    if type(record) ~= "table" or not record.frame or isCombatBlocked(record.frame) then
        return false
    end

    if self:CanScaleRecord(record) then
        local scale = tonumber(self:GetScale(record.key))
        if scale then
            safeFrameCall(record.frame, "SetScale", clampScale(scale))
        end
    else
        self:ClearStoredScale(record)
        local originalScale = record.original and tonumber(record.original.scale) or 1
        safeFrameCall(record.frame, "SetScale", clampScale(originalScale or 1))
    end

    local position = self:GetPosition(record.key)
    if type(position) == "table" then
        safeFrameCall(record.frame, "ClearAllPoints")
        safeFrameCall(
            record.frame,
            "SetPoint",
            position.point or "CENTER",
            UIParent,
            position.relativePoint or "BOTTOMLEFT",
            tonumber(position.x) or 0,
            tonumber(position.y) or 0
        )
    end

    return true
end

function Runtime:ResetPosition(record)
    if type(record) ~= "table" or not record.frame or isCombatBlocked(record.frame) then
        return
    end

    self:ClearStoredPosition(record)
    if record.original and record.original.points then
        applyPoints(record.frame, record.original.points)
    end
    if record.original and record.original.userPlaced ~= nil then
        safeFrameCall(record.frame, "SetUserPlaced", record.original.userPlaced == true)
    end
    self:Notify(L.FEATURE_BLIZZARD_WINDOW_MOVER_RESET_POSITION or "Vaultloom: window position reset.")
end

function Runtime:ResetScale(record)
    if type(record) ~= "table" or not record.frame or isCombatBlocked(record.frame) then
        return
    end

    self:ClearStoredScale(record)
    local scale = record.original and tonumber(record.original.scale) or 1
    safeFrameCall(record.frame, "SetScale", clampScale(scale or 1))
    self:Notify(L.FEATURE_BLIZZARD_WINDOW_MOVER_RESET_SCALE or "Vaultloom: window scale reset.")
end

function Runtime:ScaleRecord(record, delta)
    if not self:IsRecordActive(record) or not self:IsScaleEnabled() or not self:CanScaleRecord(record) then
        return false
    end

    local target = record.frame
    if isCombatBlocked(target) then
        return false
    end

    local currentScale = tonumber(safeFrameCall(target, "GetScale")) or 1
    local newScale = clampScale(currentScale + ((tonumber(delta) or 0) * SCALE_STEP))
    safeFrameCall(target, "SetScale", newScale)
    self:SaveScale(record, newScale)
    return true
end

function Runtime:GetRecord(frame)
    return frame and self.recordsByFrame[frame] or nil
end

function Runtime:IsRecordActive(record)
    return self.enabled == true and type(record) == "table" and record.active == true and record.frame and not isForbidden(record.frame)
end

function Runtime:OnMouseDown(frame, button)
    local record = self:GetRecord(frame)
    if not self:IsRecordActive(record) or button ~= "LeftButton" then
        return
    end

    local target = record.frame or frame
    if isCombatBlocked(target) then
        return
    end

    safeFrameCall(target, "SetMovable", true)
    safeFrameCall(target, "SetClampedToScreen", true)
    if type(target.SetDontSavePosition) == "function" then
        safeFrameCall(target, "SetDontSavePosition", true)
    end

    record.wasUserPlaced = safeFrameCall(target, "IsUserPlaced")
    local ok = pcall(target.StartMoving, target)
    if ok then
        if record.wasUserPlaced ~= nil then
            safeFrameCall(target, "SetUserPlaced", record.wasUserPlaced == true)
        end
        record.moving = true
    end
end

function Runtime:OnMouseUp(frame, button)
    local record = self:GetRecord(frame)
    if not self:IsRecordActive(record) then
        return
    end
    local target = record.frame or frame

    if button == "LeftButton" and record.moving then
        safeFrameCall(target, "StopMovingOrSizing")
        record.moving = false
        self:SavePosition(record)
        return
    end

    if button ~= "RightButton" then
        return
    end

    local shiftDown = IsShiftKeyDown and IsShiftKeyDown()
    local ctrlDown = IsControlKeyDown and IsControlKeyDown()
    if shiftDown and ctrlDown then
        self:ResetPosition(record)
        self:ResetScale(record)
    elseif shiftDown then
        self:ResetPosition(record)
    elseif ctrlDown then
        self:ResetScale(record)
    end
end

function Runtime:OnMouseWheel(frame, delta)
    local record = self:GetRecord(frame)
    if not IsControlKeyDown or not IsControlKeyDown() then
        return
    end
    self:ScaleRecord(record, delta)
end

function Runtime:FindHoveredScaleRecord()
    local bestRecord = nil
    local bestRank = -1

    for _, record in pairs(self.records) do
        if self:IsRecordActive(record) and self:CanScaleRecord(record) and record.frame and isMouseOverFrame(record.frame) then
            local rank = getFrameStackRank(record.frame)
            if rank > bestRank then
                bestRank = rank
                bestRecord = record
            end
        end
    end

    return bestRecord
end

function Runtime:RefreshScaleCapture()
    local capture = self.scaleCaptureFrame
    if not capture then
        return
    end

    if not self.enabled or not self:IsScaleEnabled() or not IsControlKeyDown or not IsControlKeyDown() then
        capture:EnableMouseWheel(false)
        capture:SetScript("OnUpdate", nil)
        capture.updating = false
        self.hoveredScaleRecord = nil
        return
    end

    local record = self:FindHoveredScaleRecord()
    self.hoveredScaleRecord = record
    capture:EnableMouseWheel(record ~= nil)
    if not capture.updating then
        capture:SetScript("OnUpdate", function()
            Runtime:RefreshScaleCapture()
        end)
        capture.updating = true
    end
end

function Runtime:EnsureScaleCaptureFrame()
    if self.scaleCaptureFrame then
        return
    end

    local capture = CreateFrame("Frame", nil, UIParent)
    capture:SetAllPoints(UIParent)
    capture:SetFrameStrata(SCALE_CAPTURE_STRATA)
    capture:SetFrameLevel(SCALE_CAPTURE_LEVEL)
    capture:EnableMouseWheel(false)
    capture:SetScript("OnMouseWheel", function(_, delta)
        local record = Runtime.hoveredScaleRecord or Runtime:FindHoveredScaleRecord()
        if Runtime:ScaleRecord(record, delta) then
            Runtime:RefreshScaleCapture()
        end
    end)
    capture:SetScript("OnEvent", function()
        Runtime:RefreshScaleCapture()
    end)
    capture:RegisterEvent("MODIFIER_STATE_CHANGED")
    capture:Show()

    self.scaleCaptureFrame = capture
end

function Runtime:OnShow(frame)
    local record = self:GetRecord(frame)
    if not self:IsRecordActive(record) then
        return
    end

    if isCombatBlocked(frame) then
        self:ScheduleRetry()
        return
    end

    self:ApplyStoredState(record)
end

function Runtime:OnHide(frame)
    local record = self:GetRecord(frame)
    if record and record.moving then
        safeFrameCall(frame, "StopMovingOrSizing")
        record.moving = false
    end
end

function Runtime:CaptureOriginal(frame)
    return {
        points = capturePoints(frame),
        scale = tonumber(safeFrameCall(frame, "GetScale")) or 1,
        mouse = safeFrameCall(frame, "IsMouseEnabled"),
        mouseWheel = safeFrameCall(frame, "IsMouseWheelEnabled"),
        movable = safeFrameCall(frame, "IsMovable"),
        clamped = safeFrameCall(frame, "IsClampedToScreen"),
        userPlaced = safeFrameCall(frame, "IsUserPlaced"),
    }
end

function Runtime:ApplyFrameAccess(record)
    if type(record) ~= "table" or not record.frame or isCombatBlocked(record.frame) then
        self:ScheduleRetry()
        return false
    end

    safeFrameCall(record.frame, "SetMovable", true)
    safeFrameCall(record.frame, "SetClampedToScreen", true)
    safeFrameCall(record.frame, "EnableMouse", true)
    safeFrameCall(record.frame, "EnableMouseWheel", self:CanScaleRecord(record))
    if type(record.frame.SetDontSavePosition) == "function" then
        safeFrameCall(record.frame, "SetDontSavePosition", true)
    end

    self:EnsureMoveGrip(record)
    self:ApplyStoredState(record)
    return true
end

function Runtime:RestoreFrameAccess(record)
    if type(record) ~= "table" or not record.frame or not record.original then
        return
    end
    if isCombatBlocked(record.frame) then
        return
    end

    if record.moving then
        safeFrameCall(record.frame, "StopMovingOrSizing")
        record.moving = false
    end

    if record.original.mouse ~= nil then
        safeFrameCall(record.frame, "EnableMouse", record.original.mouse == true)
    end
    if record.original.mouseWheel ~= nil then
        safeFrameCall(record.frame, "EnableMouseWheel", record.original.mouseWheel == true)
    end
    if record.original.movable ~= nil then
        safeFrameCall(record.frame, "SetMovable", record.original.movable == true)
    end
    if record.original.clamped ~= nil then
        safeFrameCall(record.frame, "SetClampedToScreen", record.original.clamped == true)
    end
    if record.grips then
        for _, grip in ipairs(record.grips) do
            grip:Hide()
        end
    elseif record.grip then
        record.grip:Hide()
    end
end

function Runtime:CreateMoveGrip(record, parentFrame, fullFrame)
    if type(record) ~= "table" or not record.frame or not parentFrame then
        return nil
    end

    local grip = CreateFrame("Frame", nil, parentFrame)
    if type(grip.SetPropagateMouseClicks) ~= "function" then
        grip:Hide()
        return nil
    end

    if fullFrame then
        grip:SetAllPoints(parentFrame)
    else
        grip:SetPoint("TOPLEFT", record.frame, "TOPLEFT", 8, -4)
        grip:SetPoint("TOPRIGHT", record.frame, "TOPRIGHT", -52, -4)
        grip:SetHeight(42)
    end

    grip:SetFrameLevel((tonumber(safeFrameCall(parentFrame, "GetFrameLevel")) or 0) + 1000)
    grip:EnableMouse(true)
    grip:EnableMouseWheel(self:CanScaleRecord(record))
    grip:SetPropagateMouseClicks(true)
    if type(grip.SetPropagateMouseMotion) == "function" then
        grip:SetPropagateMouseMotion(true)
    end

    hookScript(grip, "OnMouseDown", function(selfFrame, button)
        Runtime:OnMouseDown(selfFrame, button)
    end)
    hookScript(grip, "OnMouseUp", function(selfFrame, button)
        Runtime:OnMouseUp(selfFrame, button)
    end)
    hookScript(grip, "OnMouseWheel", function(selfFrame, delta)
        Runtime:OnMouseWheel(selfFrame, delta)
    end)

    self.recordsByFrame[grip] = record
    return grip
end

function Runtime:EnsureMoveGrip(record)
    if type(record) ~= "table" or not record.frame then
        return
    end

    if record.grips then
        for _, grip in ipairs(record.grips) do
            grip:Show()
            self.recordsByFrame[grip] = record
        end
        return
    end

    record.grips = {}

    local mainGrip = self:CreateMoveGrip(record, record.frame, false)
    if mainGrip then
        record.grip = mainGrip
        record.grips[#record.grips + 1] = mainGrip
    end

    local seen = {}
    for _, childPath in ipairs(COMMON_GRIP_CHILD_PATHS) do
        local child = resolveChildFrame(record.frame, childPath)
        if child and child ~= record.frame and not seen[child] then
            seen[child] = true
            local childGrip = self:CreateMoveGrip(record, child, true)
            if childGrip then
                record.grips[#record.grips + 1] = childGrip
            end
        end
    end
end

function Runtime:RegisterWindow(descriptor)
    if type(descriptor) ~= "table" or type(descriptor.key) ~= "string" then
        return false
    end
    if isSkippedFrameKey(descriptor.key) then
        self:ClearStoredFrameKey(descriptor.key)
        local record = self.records[descriptor.key]
        if record then
            record.active = false
            self:RestoreFrameAccess(record)
        end
        return false
    end

    local frame = descriptor.frameRef or resolveFrame(descriptor.frame)
    if not frame then
        return false
    end
    if isCombatBlocked(frame) then
        self:ScheduleRetry()
        return false
    end

    local allowScale = descriptor.allowScale ~= false and not isScaleBlockedKey(descriptor.key)
    local record = self.records[descriptor.key]
    if record and record.frame == frame then
        record.active = true
        record.allowScale = allowScale
        self.recordsByFrame[frame] = record
        self:ApplyFrameAccess(record)
        return true
    end

    record = {
        key = descriptor.key,
        frame = frame,
        active = true,
        allowScale = allowScale,
        original = self:CaptureOriginal(frame),
    }
    self.records[descriptor.key] = record
    self.recordsByFrame[frame] = record

    hookScript(frame, "OnMouseDown", function(selfFrame, button)
        Runtime:OnMouseDown(selfFrame, button)
    end)
    hookScript(frame, "OnMouseUp", function(selfFrame, button)
        Runtime:OnMouseUp(selfFrame, button)
    end)
    hookScript(frame, "OnMouseWheel", function(selfFrame, delta)
        Runtime:OnMouseWheel(selfFrame, delta)
    end)
    hookScript(frame, "OnShow", function(selfFrame)
        Runtime:OnShow(selfFrame)
    end)
    hookScript(frame, "OnHide", function(selfFrame)
        Runtime:OnHide(selfFrame)
    end)

    self:ApplyFrameAccess(record)
    return true
end

function Runtime:DeactivateAll()
    for _, record in pairs(self.records) do
        record.active = false
        self:RestoreFrameAccess(record)
    end
end

function Runtime:EnsureEventFrame()
    if self.eventFrame then
        return
    end

    self.eventFrame = CreateFrame("Frame")
    self.eventFrame:SetScript("OnEvent", function(_, event, addonName)
        if event == "ADDON_LOADED" then
            Runtime:OnAddonLoaded(addonName)
        elseif event == "PLAYER_ENTERING_WORLD" then
            Runtime:RegisterAllWindows()
        elseif event == "PLAYER_REGEN_ENABLED" then
            Runtime:OnPlayerRegenEnabled()
        end
    end)
end

function Runtime:IsCompatBlocked()
    if isAddonLoaded("BlizzMove") then
        if not self.warnedCompat then
            self.warnedCompat = true
            self:Notify(L.FEATURE_BLIZZARD_WINDOW_MOVER_COMPAT or "Vaultloom: BlizzMove is loaded, so Vaultloom Window Mover stays paused to avoid double-moving frames.")
        end
        return true
    end
    return false
end

function Runtime:ScheduleRetry()
    if not self.enabled then
        return
    end

    self:EnsureEventFrame()
    self.eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
end

function Runtime:ScheduleRegisterAllWindows(delay)
    if not self.enabled then
        return
    end

    if C_Timer and type(C_Timer.After) == "function" then
        C_Timer.After(tonumber(delay) or 0, function()
            if Runtime.enabled then
                Runtime:RegisterAllWindows()
            end
        end)
        return
    end

    self:RegisterAllWindows()
end

function Runtime:RegisterCoreWindows()
    for _, descriptor in ipairs(CORE_WINDOWS) do
        self:RegisterWindow(descriptor)
    end
end

function Runtime:RegisterContainerWindows()
    for _, descriptor in ipairs(CONTAINER_WINDOWS) do
        self:RegisterWindow(descriptor)
    end
end

function Runtime:RegisterContainerFrame(frame)
    local frameName = safeFrameCall(frame, "GetName")
    if type(frameName) ~= "string" or frameName == "" then
        return
    end

    self:RegisterWindow({ key = frameName, frameRef = frame })
end

function Runtime:EnsureContainerHooks()
    if self.containerHooked then
        return
    end

    if type(hooksecurefunc) ~= "function" or type(ContainerFrameMixin) ~= "table" or type(ContainerFrameMixin.OnLoad) ~= "function" then
        return
    end

    local ok = pcall(hooksecurefunc, ContainerFrameMixin, "OnLoad", function(frame)
        if Runtime.enabled then
            Runtime:RegisterContainerFrame(frame)
        end
    end)
    self.containerHooked = ok == true
end

function Runtime:EnsurePanelShowHooks()
    if self.panelShowHooked then
        return
    end
    if type(hooksecurefunc) ~= "function" then
        return
    end

    local hooked = false
    if type(ShowUIPanel) == "function" then
        local ok = pcall(hooksecurefunc, "ShowUIPanel", function()
            Runtime:ScheduleRegisterAllWindows(0)
            Runtime:ScheduleRegisterAllWindows(0.1)
        end)
        hooked = hooked or ok == true
    end
    if type(ToggleFrame) == "function" then
        local ok = pcall(hooksecurefunc, "ToggleFrame", function()
            Runtime:ScheduleRegisterAllWindows(0)
            Runtime:ScheduleRegisterAllWindows(0.1)
        end)
        hooked = hooked or ok == true
    end

    self.panelShowHooked = hooked
end

function Runtime:RegisterUIPanelWindows()
    if type(UIPanelWindows) ~= "table" then
        return
    end

    for frameName in pairs(UIPanelWindows) do
        if type(frameName) == "string" and frameName ~= "" then
            self:RegisterWindow({ key = frameName, frame = frameName })
        end
    end
end

function Runtime:RegisterAddonWindows(addonName)
    local descriptors = ADDON_WINDOWS[addonName]
    if type(descriptors) ~= "table" then
        return
    end

    for _, descriptor in ipairs(descriptors) do
        self:RegisterWindow(descriptor)
    end
end

function Runtime:RegisterLoadedAddonWindows()
    for addonName in pairs(ADDON_WINDOWS) do
        if isAddonLoaded(addonName) then
            self:RegisterAddonWindows(addonName)
        end
    end
end

function Runtime:RegisterAllWindows()
    self:EnsureNoScaleFrameHardReset()
    self:ClearNoScaleFrameStorage()
    if not self.enabled or self:IsCompatBlocked() then
        self:DeactivateAll()
        self:RefreshScaleCapture()
        return
    end

    self:RegisterCoreWindows()
    self:EnsureContainerHooks()
    self:EnsurePanelShowHooks()
    self:EnsureScaleCaptureFrame()
    self:RegisterContainerWindows()
    self:RegisterLoadedAddonWindows()
    self:RegisterUIPanelWindows()
    self:RefreshScaleCapture()
end

function Runtime:OnAddonLoaded(addonName)
    if not self.enabled then
        return
    end

    self:EnsureNoScaleFrameHardReset()
    self:ClearNoScaleFrameStorage()
    if addonName == "BlizzMove" then
        self:DeactivateAll()
        self:IsCompatBlocked()
        self:RefreshScaleCapture()
        return
    end

    if self:IsCompatBlocked() then
        self:DeactivateAll()
        self:RefreshScaleCapture()
        return
    end

    self:RegisterCoreWindows()
    self:EnsureContainerHooks()
    self:EnsurePanelShowHooks()
    self:EnsureScaleCaptureFrame()
    self:RegisterContainerWindows()
    self:RegisterAddonWindows(addonName)
    self:RegisterUIPanelWindows()
    self:ScheduleRegisterAllWindows(0)
    self:ScheduleRegisterAllWindows(0.15)
    self:ScheduleRegisterAllWindows(0.75)
    self:ScheduleRegisterAllWindows(2)
    self:RefreshScaleCapture()
end

function Runtime:OnPlayerRegenEnabled()
    if self.eventFrame then
        self.eventFrame:UnregisterEvent("PLAYER_REGEN_ENABLED")
    end
    self:RegisterAllWindows()
end

function Runtime:SetEnabled(enabled)
    self.enabled = enabled == true
    self:EnsureEventFrame()
    self.eventFrame:UnregisterAllEvents()

    self:EnsureNoScaleFrameHardReset()
    self:ClearNoScaleFrameStorage()
    if not self.enabled then
        self:DeactivateAll()
        self:RefreshScaleCapture()
        return
    end

    self.eventFrame:RegisterEvent("ADDON_LOADED")
    self.eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
    self:EnsureContainerHooks()
    self:EnsurePanelShowHooks()
    self:EnsureScaleCaptureFrame()
    self:RegisterAllWindows()
    self:ScheduleRegisterAllWindows(0.25)
    self:ScheduleRegisterAllWindows(1)
end

function Runtime:RefreshOptions()
    if not self.enabled then
        return
    end
    self:RegisterAllWindows()
    self:RefreshScaleCapture()
end

ns.BlizzardWindowMoverRuntime = Runtime

ns:RegisterFeatureDefinition(FEATURE_ID, {
    id = FEATURE_ID,
    order = 10,
    category = "blizzard",
    title = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_TITLE or "Blizzard Window Mover" end,
    description = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_DESCRIPTION or "Drag supported Blizzard windows with left mouse and scale them with Ctrl + mouse wheel." end,
    note = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_NOTE or "Uses a safe Blizzard window list plus UI panel registration, no background scanning, and ignores protected frames during combat." end,
    previewLabel = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_PREVIEW_LABEL or "Window Control" end,
    previewNote = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_PREVIEW_NOTE or "Drag to move. Ctrl + mouse wheel to scale. Shift/Ctrl + right click resets position or scale." end,
    optionToggle = {
        key = "save_mode",
        label = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_SAVE_LABEL or "Save" end,
        values = { "session", "permanent" },
        defaultValue = "session",
        valueLabels = {
            session = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_SAVE_SESSION or "Session" end,
            permanent = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_SAVE_PERMANENT or "Permanent" end,
        },
    },
    secondaryOptionToggle = {
        key = "scale_mode",
        label = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_SCALE_LABEL or "Scale" end,
        values = { "on", "off" },
        defaultValue = "on",
        valueLabels = {
            on = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_SCALE_ON or "Scale On" end,
            off = function() return L.FEATURE_BLIZZARD_WINDOW_MOVER_SCALE_OFF or "Scale Off" end,
        },
    },
    requiresReload = false,
    apply = function(isEnabled)
        Runtime:SetEnabled(isEnabled == true)
    end,
    onOptionChanged = function()
        Runtime:RefreshOptions()
    end,
})
