-- laundering/sv_npc.lua
-- Gestion du cycle de vie des entités laundering_npc :
-- spawn, chargement depuis la DB au démarrage, référencement interne.

Laundering = Laundering or {}
Laundering._shutting = false

hook.Add("ShutDown", "Laundering.MarkShutdown", function()
    Laundering._shutting = true
end)

-- ============================================================================
-- SpawnNPC — crée et positionne une entité laundering_npc
-- pos    : Vector
-- angY   : number (yaw en degrés)
-- npcID  : number|nil (id SQLite, nil si non persisté)
-- Retourne l'entité ou nil en cas d'échec.
-- ============================================================================

function Laundering.SpawnNPC(pos, angY, npcID)
    local ent = ents.Create("laundering_npc")
    if not IsValid(ent) then
        ErrorNoHalt("[Laundering] SpawnNPC : ents.Create(laundering_npc) a échoué.\n")
        return nil
    end

    ent:SetPos(pos)
    ent:SetAngles(Angle(0, angY or 0, 0))
    ent:Spawn()
    ent:Activate()

    if npcID then
        ent.LaunderingNPCID = npcID
    end

    Laundering._npcs[ent] = true
    return ent
end

-- ============================================================================
-- InitPostEntity — charge les NPCs de la map courante depuis la DB
-- ============================================================================

hook.Add("InitPostEntity", "Laundering.LoadNPCs", function()
    -- S'assurer que le storage est prêt (doit avoir été include avant sv_npc)
    if not Laundering.Storage or not Laundering.Storage.LoadNPCsForMap then
        ErrorNoHalt("[Laundering] Storage non initialisé avant LoadNPCs.\n")
        return
    end

    local mapName = game.GetMap()
    local rows    = Laundering.Storage.LoadNPCsForMap(mapName)

    for _, row in ipairs(rows) do
        Laundering.SpawnNPC(
            Vector(row.x, row.y, row.z),
            row.angle_y,
            row.id
        )
    end

    print(string.format(
        "[Laundering] %d NPC(s) chargé(s) pour la map '%s'.",
        #rows, mapName
    ))
end)

-- ============================================================================
-- Cleanup — retire du registry quand une entité est supprimée
-- ============================================================================

-- EntityRemoved : auto-respawn si le NPC est viré par un cleanup global
-- (Sandbox `cleanup`, lua_run, etc.). Respawn UNIQUEMENT si la suppression
-- n'est pas marquée _intentionalRemove et si la row existe encore en DB.
hook.Add("EntityRemoved", "Laundering.NPCRemoved", function(ent)
    if not IsValid(ent) then return end
    if ent:GetClass() ~= "laundering_npc" then return end

    -- Toujours retirer de la table interne
    if Laundering._npcs[ent] then
        Laundering._npcs[ent] = nil
    end

    -- Skip si suppression volontaire ou shutdown serveur
    if ent._intentionalRemove then return end
    if Laundering._shutting then return end

    local id = ent.LaunderingNPCID
    if not id then return end

    timer.Simple(0.1, function()
        if Laundering._shutting then return end

        local rows = Laundering.Storage.LoadNPCsForMap(game.GetMap())
        for _, row in ipairs(rows) do
            if row.id == id then
                Laundering.SpawnNPC(Vector(row.x, row.y, row.z), row.angle_y, row.id)
                print(string.format("[Laundering] NPC #%d respawné (cleanup externe détecté).", id))
                return
            end
        end
    end)
end)
