PlayerOutlines = {}
local PO = PlayerOutlines
PO.dmf_mod = get_mod("PlayerOutlines")

local PlayerUnitStatus = require("scripts/utilities/attack/player_unit_status")

PO.UPDATE_INTERVAL = 0.5
PO.dmf_mod.settings = {}
PO.instances = {}

-- ==================== Utils Functions ====================

PO.nil_check = function(root, ...)
    local parts = {...}
    local o = root

    for i = 1, #parts do
        if not o[parts[i]] then return nil end
        o = o[parts[i]]
    end

    return o
end

PO.is_in_hub = function()
    return Managers and Managers.state and Managers.state.game_mode and
        Managers.state.game_mode.game_mode_name and
        Managers.state.game_mode:game_mode_name() == "hub"
end

-- ==================== Core Functions ====================

PO.get_outline_system = function()
    local ext = PO.nil_check(Managers, "state", "extension")
    if not ext then return end
    return ext:system("outline_system")
end

PO.spawn_hologram = function(world, resources, parent_unit, state_name)
    if not world then return end

    local resource = state_name == "consumed" and resources[state_name] or
        resources.default
    local hologram_unit = World.spawn_unit_ex(world, resource)

    World.link_unit(world, hologram_unit, 1, parent_unit, 1, true)
    Unit.set_unit_culling(hologram_unit, false, true)

    local player_visibility_system = ScriptUnit.has_extension(parent_unit,
        "player_visibility_system")

    if player_visibility_system and not player_visibility_system:visible() then
        Unit.set_unit_visibility(hologram_unit, false, true)
    end

    return hologram_unit
end

PO.despawn_hologram = function(world, hologram_unit)
    if not world or not hologram_unit then return end
    World.unlink_unit(world, hologram_unit, true)
    World.destroy_unit(world, hologram_unit)
end

PO.selected_outline_name = function()
    local show_outline = PO.dmf_mod.settings["show_outline"]
    local show_mesh = PO.dmf_mod.settings["show_mesh"]

    if show_outline then
        if show_mesh then return "default_both_always" end
        return "default_outlines_always"
    end

    return show_mesh and "default_mesh_always" or nil
end

PO.clear_outlines = function(unit)
    PO.get_outline_system():remove_all_outlines(unit)
end

PO.update_outline = function(ext, unit)
    local added_outline = ext._added_disabled_outline
    local is_disabled = PlayerUnitStatus.is_disabled(
        ext._character_state_component)

    if is_disabled and not added_outline then
        ext._outline_system:add_outline(unit, "knocked_down")
        ext._added_disabled_outline = true
    elseif added_outline and not is_disabled then
        ext._outline_system:remove_outline(unit, "knocked_down")
        ext._added_disabled_outline = false
    end

    if not ext._is_local_human then
        local outline_name = PO.selected_outline_name()

        if outline_name then
            ext._outline_system:add_outline(unit, outline_name)
        end
    end
end

PO.clear_all_outlines = function()
    if not Managers.player then return end

    for _, player in pairs(Managers.player:players()) do
        PO.clear_outlines(player.player_unit)
    end
end

PO.update_settings_cache = function()
    PO.dmf_mod.settings["assist_marker_max_distance"] = PO.dmf_mod:get("assist_marker_max_distance")
    PO.dmf_mod.settings["mission_nameplates_max_distance"] = PO.dmf_mod:get("mission_nameplates_max_distance")
    PO.dmf_mod.settings["show_hologram"] = PO.dmf_mod:get("show_hologram")
    PO.dmf_mod.settings["show_in_hub"] = PO.dmf_mod:get("show_in_hub")
    PO.dmf_mod.settings["show_mesh"] = PO.dmf_mod:get("show_mesh")
    PO.dmf_mod.settings["show_outline"] = PO.dmf_mod:get("show_outline")
end

PO.update_settings_cache()

-- ==================== Hooks ====================

-- Remove control of hologram visibility so we can manage it.
PO.dmf_mod:hook_origin("GameModeManager", "disable_hologram", function()
    return false
end)

-- Integrate outline update logic into PlayerUnitHologramExtension.update hook
-- Removed original PlayerUnitOutlineExtension.update override since the class has no update method
PO.dmf_mod:hook("PlayerUnitHologramExtension", "update", function(func, self, unit, dt, t)
    if t < self._next_update_t then
        return
    end

    self._next_update_t = t + PO.UPDATE_INTERVAL
    local state_name = self._character_state_component.state_name

    -- Handle outline update (migrated from original PlayerUnitOutlineExtension.update logic)
    local outline_ext = ScriptUnit.has_extension(unit, "outline_system")
    if outline_ext then
        if PO.is_in_hub() and not PO.dmf_mod.settings["show_in_hub"] then
            PO.clear_outlines(unit)
        else
            PO.update_outline(outline_ext, unit)
        end
    end

    if state_name == "dead" or
            not PO.dmf_mod.settings["show_hologram"] or
            (PO.is_in_hub() and not PO.dmf_mod.settings["show_in_hub"]) then

        if self._hologram_unit then
            PO.despawn_hologram(self._world, self._hologram_unit)
            self._hologram_unit = nil
        end

        return
    else
        if not self._hologram_unit then
            self._hologram_unit = PO.spawn_hologram(self._world,
                self._unit_resources, self._unit, state_name)
            self._hologram_unit = self._hologram_unit
            self._current_spawned_state = state_name
        end

        local health_percent = self._target_health_extension:current_health_percent()
        local is_disabled = PlayerUnitStatus.is_disabled(self._character_state_component)

        if health_percent == self._health and is_disabled == self._was_disabled then
            return
        end

        Unit.set_scalar_for_materials(self._hologram_unit,
            "health_value", is_disabled and 1 or 1 - health_percent, false)

        self._health = health_percent
        self._was_disabled = is_disabled
    end
end)

PO.dmf_mod:hook_require("scripts/ui/hud/elements/world_markers/templates/world_marker_template_player_assistance", function(instance)
    PO.instances.wmt_player_assistance = instance
    PO.dmf_mod.on_setting_changed("assist_marker_max_distance")
end)

PO.dmf_mod:hook_require("scripts/ui/hud/elements/world_markers/templates/world_marker_template_nameplate_combat", function(instance)
    PO.instances.wmt_nameplate_combat = instance
    PO.dmf_mod.on_setting_changed("mission_nameplates_max_distance")
end)

-- Fixed: use correct variable name _is_local_human
PO.dmf_mod:hook_safe("PlayerUnitOutlineExtension", "extensions_ready", function(self, world, unit)
    if self._is_local_human then return end
    self._outline_system:add_outline(unit, PO.selected_outline_name())
end)

-- ==================== Event Handlers ====================

PO.dmf_mod.on_setting_changed = function(setting_id)
    PO.update_settings_cache()

    -- Update visuals when a setting changes.
    if setting_id == "show_hologram"
            or setting_id == "show_mesh"
            or setting_id == "show_outline"
            or setting_id == "show_in_hub" then
        PlayerOutlines.clear_all_outlines()
    elseif setting_id == "mission_nameplates_max_distance" and
            PlayerOutlines.instances.wmt_nameplate_combat then
        PlayerOutlines.instances.wmt_nameplate_combat.max_distance =
            PO.dmf_mod.settings["mission_nameplates_max_distance"]
    elseif setting_id == "assist_marker_max_distance" and
            PlayerOutlines.instances.wmt_player_assistance then
        PlayerOutlines.instances.wmt_player_assistance.max_distance =
            PO.dmf_mod.settings["assist_marker_max_distance"]
    end
end

PO.dmf_mod.on_unload = PlayerOutlines.clear_all_outlines
