-- armurier/sv_caisse.lua
-- Gestion des caisses persistantes (placées par un admin, sauvegardées par map).
--
-- Cycle de vie :
--   • Un admin spawn une caisse (spawnmenu AdminOnly OU commande armurier_spawncaisse)
--     → PlayerSpawnedSENT enregistre la position en DB et tague l'entité.
--   • Au démarrage (InitPostEntity) et après un cleanup (PostCleanupMap) :
--     RestoreAll() recrée toutes les caisses de la map depuis la DB.
--   • Un superadmin peut déplacer une caisse au physgun → PhysgunDrop met à jour
--     la position en DB.
--   • Suppression DÉFINITIVE uniquement via armurier_removecaisse (efface la
--     ligne DB). Un cleanup/refresh n'efface JAMAIS la DB → la caisse revient.
--
-- JAMAIS AddCSLuaFile de ce fichier (sv_ = serveur uniquement).

Armurier        = Armurier        or {}
Armurier.Caisse = Armurier.Caisse or {}

local CAISSE_CLASS = "armurier_caisse"

-- ============================================================================
-- Spawn — crée une caisse à une position/angle donnés.
-- dbId : id DB existant (restauration) OU nil (nouvelle, sera enregistrée par
--        l'appelant). drop : si true, DropToFloor (placement manuel).
-- Retourne l'entité ou nil.
-- ============================================================================

function Armurier.Caisse.Spawn(pos, ang, dbId, drop)
    if not isvector(pos) then return nil end
    ang = isangle(ang) and ang or Angle(0, 0, 0)

    local ent = ents.Create(CAISSE_CLASS)
    if not IsValid(ent) then
        ErrorNoHalt("[Armurier] Caisse.Spawn : impossible de créer " .. CAISSE_CLASS .. "\n")
        return nil
    end

    -- Tag AVANT Spawn pour que Initialize sache si c'est une restauration
    ent.armurierIsRestored = (dbId ~= nil)

    ent:SetPos(pos)
    ent:SetAngles(ang)
    ent:Spawn()
    ent:Activate()

    if dbId then
        ent.armurierDBId = math.floor(tonumber(dbId) or 0)
    end

    if drop then
        ent:DropToFloor()
    end

    -- Geler la physique (caisse statique)
    local phys = ent:GetPhysicsObject()
    if IsValid(phys) then
        phys:EnableMotion(false)
        phys:Sleep()
    end

    return ent
end

-- ============================================================================
-- RestoreAll — supprime les caisses présentes (anti-doublon) puis recrée
-- toutes celles de la map depuis la DB. Marquées comme restaurées (pas de
-- ré-enregistrement, pas d'effacement DB sur Remove).
-- ============================================================================

function Armurier.Caisse.RestoreAll()
    -- Retirer les caisses actuelles sans toucher la DB
    for _, e in ipairs(ents.FindByClass(CAISSE_CLASS)) do
        if IsValid(e) then
            e.armurierNoDBDelete = true
            e:Remove()
        end
    end

    local rows  = Armurier.Storage.LoadCaisses(game.GetMap())
    local count = 0
    for _, row in ipairs(rows) do
        local ent = Armurier.Caisse.Spawn(row.pos, row.ang, row.id, false)
        if ent then
            -- Poser le nom de boutique networké dès la restauration
            ent:SetNW2String("armurier_name", row.name or "")
            count = count + 1
        end
    end

    if Armurier.Config and Armurier.Config.Logs then
        print(string.format("[Armurier] %d caisse(s) restaurée(s) sur %s.", count, game.GetMap()))
    end
end

-- ============================================================================
-- Hooks de cycle de vie
-- ============================================================================

-- Démarrage serveur : restaurer après chargement complet de la map + DarkRP
hook.Add("InitPostEntity", "Armurier.RestoreCaisses", function()
    timer.Simple(1, function()
        Armurier.Caisse.RestoreAll()
    end)
end)

-- Après un cleanup map (gmod_admin_cleanup, game.CleanUpMap, ulx cleanup…)
-- les SENT sont supprimés → on les recrée.
hook.Add("PostCleanupMap", "Armurier.RestoreCaissesAfterCleanup", function()
    -- Léger délai pour laisser le cleanup terminer
    timer.Simple(0.5, function()
        Armurier.Caisse.RestoreAll()
    end)
end)

-- Enregistrement quand un admin spawn la caisse via le spawnmenu (AdminOnly)
hook.Add("PlayerSpawnedSENT", "Armurier.SaveSpawnedCaisse", function(ply, ent)
    if not IsValid(ent) or ent:GetClass() ~= CAISSE_CLASS then return end

    -- Restauration code-side → ne pas ré-enregistrer
    if ent.armurierIsRestored or ent.armurierDBId then return end

    -- Sécurité : seul un admin peut en poser une persistante
    if not IsValid(ply) or not ply:IsAdmin() then
        ent.armurierNoDBDelete = true
        ent:Remove()
        if IsValid(ply) then DarkRP.notify(ply, 4, 4, "Seul un admin peut poser une caisse d'armurier.") end
        return
    end

    local id = Armurier.Storage.AddCaisse(game.GetMap(), ent:GetPos(), ent:GetAngles())
    if id then
        ent.armurierDBId = id
        DarkRP.notify(ply, 0, 4, "Caisse d'armurier posée et sauvegardée (#" .. id .. ").")
    end
end)

-- Déplacement au physgun (superadmin) → mettre à jour la position en DB
hook.Add("PhysgunDrop", "Armurier.UpdateCaissePosOnDrop", function(ply, ent)
    if not IsValid(ent) or ent:GetClass() ~= CAISSE_CLASS then return end
    if not ent.armurierDBId then return end

    Armurier.Storage.UpdateCaissePos(ent.armurierDBId, ent:GetPos(), ent:GetAngles())

    -- Re-geler après déplacement
    local phys = ent:GetPhysicsObject()
    if IsValid(phys) then
        phys:EnableMotion(false)
        phys:Sleep()
    end
end)
