-- [ SECONDARY UNIT FRAME MIXIN ]---------------------------------------------------------------------
-- consumers must Mixin(Plugin, UnitFrameMixin) FIRST, then Mixin(Plugin, SecondaryUnitFrameMixin) — order matters.
---@type Orbit
local Orbit = Orbit
local OrbitEngine = Orbit.Engine
local L = Orbit.L
local LSM = LibStub("LibSharedMedia-3.0")



Orbit.SecondaryUnitFrameMixin = {}
local Mixin = Orbit.SecondaryUnitFrameMixin

Mixin.sharedDefaults = {
    Width = 100, Height = 20,
    DisabledComponents = { "HealthText" },
    ComponentPositions = { Name = { anchorX = "CENTER", offsetX = 0, anchorY = "CENTER", offsetY = 0, justifyH = "CENTER" } },
}

-- [ SETTINGS UI ]------------------------------------------------------------------------------------
function Mixin:AddSecondarySettings(dialog, systemFrame)
    local cfg = self._sufConfig
    if systemFrame.systemIndex ~= cfg.frameIndex then return end
    OrbitEngine.Config:Render(dialog, systemFrame, self, {
        hideNativeSettings = true,
        controls = {
            { type = "slider", key = "Width", label = L.CMN_WIDTH, min = 50, max = 200, step = 1, default = 100 },
            { type = "slider", key = "Height", label = L.CMN_HEIGHT, min = 10, max = 40, step = 1, default = 20 },
        },
    })
end

-- [ LIFECYCLE ]--------------------------------------------------------------------------------------
function Mixin:CreateSecondaryPlugin(config)
    self._sufConfig = config
    self:RegisterStandardEvents()
    self:HideNativeUnitFrame(config.nativeFrame)

    self.frame = OrbitEngine.UnitButton:Create(UIParent, config.unit, config.frameName)
    if config.exposeMountedConfig then self.mountedConfig = { frame = self.frame } end
    self.frame:SetFrameLevel(math.max(1, self.frame:GetFrameLevel() + Orbit.Constants.Levels.SecondaryDemote))
    self.frame.editModeName = config.editModeName
    self.frame.systemIndex = config.frameIndex
    self.frame.anchorOptions = { horizontal = true, vertical = true, mergeBorders = { x = false, y = true } }
    self.frame.orbitResizeBounds = { minW = 50, maxW = 200, minH = 10, maxH = 40 }

    OrbitEngine.Frame:AttachSettingsListener(self.frame, self, config.frameIndex)

    -- Canvas Mode: draggable components
    if OrbitEngine.ComponentDrag and self.frame.Name then
        OrbitEngine.ComponentDrag:Attach(self.frame.Name, self.frame, {
            key = "Name", onPositionChange = OrbitEngine.ComponentDrag:MakePositionCallback(self, config.frameIndex, "Name"),
        })
    end
    if self.frame.HealthText then
        OrbitEngine.ComponentDrag:Attach(self.frame.HealthText, self.frame, {
            key = "HealthText", onPositionChange = OrbitEngine.ComponentDrag:MakePositionCallback(self, config.frameIndex, "HealthText"),
        })
    end

    local plugin = self
    local originalOnEvent = self.frame:GetScript("OnEvent")
    self.frame:SetScript("OnEvent", function(f, event, unit, ...)
        local p = Orbit.Profiler
        local s = p and p:Begin()
        if event == config.changeEvent or (event == "UNIT_TARGET" and unit == config.parentUnit) then
            if plugin:IsEnabled() then f:UpdateAll() end
        elseif event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
            if UnitExists(config.unit) then f:UpdateHealth(); f:UpdateHealthText() end
        elseif event == "PET_BATTLE_OPENING_START" or event == "UNIT_ENTERED_VEHICLE" then
            Orbit:SafeAction(function() f:Hide() end)
        elseif event == "PET_BATTLE_CLOSE" or event == "UNIT_EXITED_VEHICLE" then
            plugin:ApplySettings(f)
        elseif originalOnEvent then
            originalOnEvent(f, event, unit, ...)
        end
        if p then p:End(plugin, event, s) end
    end)

    self:ApplySettings(self.frame)

    OrbitEngine.Frame:RestorePosition(self.frame, self, config.frameIndex)
    if not self.frame:GetPoint() then
        self.frame:SetPoint("CENTER", UIParent, "CENTER", config.defaultX or 0, config.defaultY or -180)
    end
end

-- [ EVENTS ]-----------------------------------------------------------------------------------------
function Mixin:RegisterSecondaryEvents(enabled)
    local frame = self.frame
    local cfg = self._sufConfig
    if not frame then return end
    local events = { "UNIT_TARGET", cfg.changeEvent, "PET_BATTLE_OPENING_START", "PET_BATTLE_CLOSE", "UNIT_ENTERED_VEHICLE", "UNIT_EXITED_VEHICLE" }
    for _, ev in ipairs(events) do
        if enabled then frame:RegisterEvent(ev) else frame:UnregisterEvent(ev) end
    end
    if enabled then RegisterUnitWatch(frame) else UnregisterUnitWatch(frame) end
end

function Mixin:UpdateVisibility() self:ApplySettings() end

-- [ APPLY SETTINGS ]---------------------------------------------------------------------------------
function Mixin:ApplySettings()
    local frame = self.frame
    local cfg = self._sufConfig
    if not frame or InCombatLockdown() then return end

    local systemIndex = cfg.frameIndex
    local enabled = self:IsEnabled()
    local inEditMode = EditModeManagerFrame and EditModeManagerFrame:IsShown()

    if not enabled then
        UnregisterUnitWatch(frame)
        frame:Hide()
        return
    end

    if inEditMode then
        UnregisterUnitWatch(frame)
        if not UnitExists(cfg.unit) then frame.unit = "player" end
        frame:Show()
        frame:UpdateAll()
    else
        frame.unit = cfg.unit
        self:RegisterSecondaryEvents(enabled)
    end

    frame:SetSize(self:GetSetting(systemIndex, "Width") or 100, self:GetSetting(systemIndex, "Height") or 20)
    self:ApplyBaseVisuals(frame, systemIndex)

    local fontPath = LSM:Fetch("font", Orbit.db.GlobalSettings.Font) or "Fonts\\FRIZQT__.TTF"
    if frame.Name then
        local h = frame:GetHeight()
        frame.Name:SetFont(fontPath, 10, Orbit.Skin:GetFontOutline())
        Orbit.Skin:ApplyFontShadow(frame.Name)
        frame.Name:ClearAllPoints()
        frame.Name:SetPoint("CENTER", 0, 0)
        frame.Name:SetJustifyH("CENTER")
    end

    if self:IsComponentDisabled("HealthText") then
        frame.healthTextEnabled = false
        if frame.HealthText then frame.HealthText:Hide() end
    else
        frame.healthTextEnabled = true
        if frame.HealthText then frame.HealthText:Show(); frame:UpdateHealthText() end
    end

    frame.UpdateTextLayout = function() end
    if frame.Power then frame.Power:Hide() end

    frame:SetClassColour(self:GetPlayerSetting("ClassColour"))
    if frame.SetReactionColour then frame:SetReactionColour(true) end
    if enabled then frame:UpdateAll() end

    local savedPositions = self:GetSetting(systemIndex, "ComponentPositions")
    if savedPositions and next(savedPositions) then
        OrbitEngine.ComponentDrag:RestoreFramePositions(frame, savedPositions)
    end
    
    if frame.ApplyComponentPositions then frame:ApplyComponentPositions() end

    local enableHover = self:GetSetting(systemIndex, "ShowOnMouseover") ~= false
    local vePlugin = cfg.vePluginName and Orbit:GetPlugin(cfg.vePluginName) or self
    local veIndex = cfg.veSystemIndex or systemIndex
    if Orbit.OOCFadeMixin then Orbit.OOCFadeMixin:ApplyOOCFade(frame, vePlugin, veIndex, "OutOfCombatFade", enableHover) end
end

if table.freeze then table.freeze(Orbit.SecondaryUnitFrameMixin) end
