---@type Orbit
local Orbit = Orbit
local L = Orbit.L
local OrbitEngine = Orbit.Engine

-- [ PLUGIN REGISTRATION ]----------------------------------------------------------------------------
-- Using a custom ID to denote we are replacing the native bar behavior fully
local SYSTEM_ID = "Orbit_MicroMenu"
local DEFAULT_POSITION_X = -50
local DEFAULT_POSITION_Y = 40
local OFFSCREEN_OFFSET = 9000
-- Hook EditModeSystem to hide the native selection but expose this as an Orbit Frame with our own ID/label.
local Plugin = Orbit:RegisterPlugin("Menu Bar", SYSTEM_ID, {
    displayName = L.PLG_NAME_MENU_BAR,
    defaults = {
        Padding = -5,
        Rows = 1,
        Scale = 100,
        Opacity = 100,
    },
})

-- Apply NativeBarMixin for mouseOver helpers
Mixin(Plugin, Orbit.NativeBarMixin)

-- [ HELPERS ]----------------------------------------------------------------------------------------
local function IsMicroButton(frame)
    if not frame then
        return false
    end
    if frame.layoutIndex then
        return true
    end
    local name = frame:GetName()
    return name and name:find("MicroButton") or false
end

-- [ SETTINGS UI ]------------------------------------------------------------------------------------
function Plugin:AddSettings(dialog, systemFrame)
    local systemIndex = systemFrame.systemIndex or SYSTEM_ID
    local SB = OrbitEngine.SchemaBuilder

    local schema = {
        hideNativeSettings = true,
        controls = {},
    }

    -- 1. Scale
    SB:AddSizeSettings(self, schema, systemIndex, systemFrame, nil, nil, {
        key = "Scale",
        label = L.PLU_MENU_SIZE,
        default = 100,
        min = 50,
        max = 150,
    })

    -- 2. Layout (Rows)
    local numButtons = self:CountButtons()
    if numButtons == 0 then
        numButtons = 12
    end -- Fallback

    local factors = {}
    for i = 1, numButtons do
        if numButtons % i == 0 then
            table.insert(factors, i)
        end
    end

    local currentRows = self:GetSetting(systemIndex, "Rows") or 1
    local currentIndex = 1
    for i, v in ipairs(factors) do
        if v == currentRows then
            currentIndex = i
            break
        end
    end

    if #factors > 1 then
        table.insert(schema.controls, {
            type = "slider",
            key = "Layout_Slider",
            label = L.PLU_MENU_LAYOUT,
            min = 1,
            max = #factors,
            step = 1,
            default = currentIndex,
            formatter = function(v)
                local r = factors[v]
                if not r then
                    return ""
                end
                local cols = numButtons / r
                return L.PLU_MENU_LAYOUT_VALUE_F:format(r, cols)
            end,
            onChange = function(val)
                local r = factors[val]
                if r then
                    self:SetSetting(systemIndex, "Rows", r)
                    if self.ApplySettings then
                        self:ApplySettings()
                    end
                end
            end,
        })
    end

    -- 3. Padding
    table.insert(schema.controls, {
        type = "slider",
        key = "Padding",
        label = L.PLU_MENU_NAV_PADDING,
        min = -10,
        max = 10,
        step = 1,
        default = 0,
    })

    OrbitEngine.Config:Render(dialog, systemFrame, self, schema)
end

-- [ LIFECYCLE ]--------------------------------------------------------------------------------------
function Plugin:OnLoad()
    -- Create Container
    self.frame = CreateFrame("Frame", "OrbitMicroMenuContainer", UIParent)
    self.frame.systemIndex = SYSTEM_ID
    self.frame.editModeName = self.displayName
    self.frame:SetSize(300, 40) -- Initial size
    self.frame:SetClampedToScreen(true) -- Prevent dragging off-screen

    -- Anchor Options: Allow anchoring but disable property sync
    self.frame.anchorOptions = {
        horizontal = true,
        vertical = true,
    }

    -- Default Position
    self.frame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", DEFAULT_POSITION_X, DEFAULT_POSITION_Y)

    -- Register to Orbit
    OrbitEngine.Frame:AttachSettingsListener(self.frame, self, SYSTEM_ID)
    OrbitEngine.Frame:RestorePosition(self.frame, self, SYSTEM_ID)

    self:RegisterStandardEvents()
    self:RegisterVisibilityEvents()
    self.mountedConfig = { frame = self.frame }
    if Orbit.OOCFadeMixin then Orbit.OOCFadeMixin:ApplyOOCFade(self.frame, self, SYSTEM_ID) end

    -- Hook AddButton to catch late additions
    if MicroMenu and MicroMenu.AddButton then
        hooksecurefunc(MicroMenu, "AddButton", function(f, button)
            self:CaptureButton(button)
        end)
    end

    -- Initial Capture
    self:ReparentAll()

    -- Listen for Main Menu updates
    hooksecurefunc("UpdateMicroButtons", function()
        if self.updateTimer then
            self.updateTimer:Cancel()
        end
        self.updateTimer = C_Timer.NewTimer(0.1, function()
            if InCombatLockdown() then
                return
            end
            local currentCount = self:CountButtons()
            if currentCount ~= self.lastButtonCount then
                self:ApplySettings()
                self.lastButtonCount = currentCount
            end
        end)
    end)
end

-- [ LOGIC ]------------------------------------------------------------------------------------------
local NATIVE_PARENTS = {}
function Plugin:CaptureButton(button)
    if not button or InCombatLockdown() or button == QueueStatusButton then return end
    NATIVE_PARENTS[1], NATIVE_PARENTS[2], NATIVE_PARENTS[3] = MicroMenu, MicroMenuContainer, UIParent
    if self:CaptureFromNativeParent(button, NATIVE_PARENTS) then button:Show() end
end

function Plugin:ReparentAll()
    if not MicroMenu then
        return
    end

    local children = { MicroMenu:GetChildren() }
    for _, child in ipairs(children) do
        if IsMicroButton(child) then
            self:CaptureButton(child)
        end
    end

    local standardButtons = {
        "CharacterMicroButton",
        "ProfessionMicroButton",
        "PlayerSpellsMicroButton",
        "AchievementMicroButton",
        "QuestLogMicroButton",
        "GuildMicroButton",
        "LFDMicroButton",
        "CollectionsMicroButton",
        "EJMicroButton",
        "StoreMicroButton",
        "MainMenuMicroButton",
    }

    for _, name in ipairs(standardButtons) do
        local btn = _G[name]
        if btn then
            self:CaptureButton(btn)
        end
    end
end

function Plugin:CountButtons()
    if not self.frame then
        return 12
    end
    local count = 0
    for _, child in ipairs({ self.frame:GetChildren() }) do
        if child:IsShown() and IsMicroButton(child) and child ~= QueueStatusButton then
            count = count + 1
        end
    end
    return count
end

-- [ SETTINGS APPLICATION ]---------------------------------------------------------------------------
function Plugin:ApplySettings()
    local frame = self.frame
    if not frame or InCombatLockdown() then
        return
    end
    if (C_PetBattles and C_PetBattles.IsInBattle()) or (UnitHasVehicleUI and UnitHasVehicleUI("player")) then
        frame:Hide()
        return
    end

    local scale, padding, rows =
        self:GetSetting(SYSTEM_ID, "Scale") or 100, self:GetSetting(SYSTEM_ID, "Padding") or 0, self:GetSetting(SYSTEM_ID, "Rows") or 1
    self:ReparentAll()
    local buttons = {}
    for _, child in ipairs({ frame:GetChildren() }) do
        if child:IsShown() and IsMicroButton(child) and child ~= QueueStatusButton then
            table.insert(buttons, child)
        end
    end
    table.sort(buttons, function(a, b)
        return (a.layoutIndex or 99) < (b.layoutIndex or 99)
    end)

    local numButtons = #buttons
    if numButtons == 0 then
        frame:Hide()
        return
    end

    frame:Show()
    frame:SetScale(scale / 100)

    local w, h = 28, 36
    if buttons[1] then
        w, h = buttons[1]:GetWidth(), buttons[1]:GetHeight()
    end
    local cols = math.ceil(numButtons / rows)
    for i, button in ipairs(buttons) do
        button:ClearAllPoints()
        local row, col = math.ceil(i / cols), (i - 1) % cols + 1
        local x = OrbitEngine.Pixel:Snap((col - 1) * (w + padding), frame:GetEffectiveScale())
        local y = OrbitEngine.Pixel:Snap((row - 1) * (h + padding) * -1, frame:GetEffectiveScale())
        button:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y)
    end

    local frameScale = frame:GetEffectiveScale()
    local finalW = OrbitEngine.Pixel:Snap((cols * w) + ((cols - 1) * padding), frameScale)
    local finalH = OrbitEngine.Pixel:Snap((rows * h) + ((rows - 1) * padding), frameScale)
    frame:SetSize(math.max(finalW, 1), math.max(finalH, 1))
    self:ApplyMouseOver(frame, SYSTEM_ID)

    for _, f in ipairs({ MicroMenu, MicroMenuContainer }) do
        if f then
            OrbitEngine.NativeFrame:SecureHide(f)
            f:ClearAllPoints()
            f:SetPoint("TOPLEFT", UIParent, "TOPLEFT", -OFFSCREEN_OFFSET, OFFSCREEN_OFFSET)
        end
    end
end

-- [ BLIZZARD HIDER ]---------------------------------------------------------------------------------
Orbit:RegisterBlizzardHider("Menu Bar", function()
    for _, f in ipairs({ MicroMenu, MicroMenuContainer }) do
        if f then OrbitEngine.NativeFrame:SecureHide(f) end
    end
end)
