-- sv_sdf_system_hooks.lua — Hooks DarkRP/GMod côté serveur.
-- Gère l'annulation de fouille sur événements (dégâts, mort, job change, deco).
-- NE PAS AddCSLuaFile ce fichier.

SDFSystem = SDFSystem or {}

-- ─── Helper : retrouver l'entité en cours de fouille d'un joueur ─────────
local function GetActiveTrash(ply)
    for _, ent in ipairs(ents.FindByClass("sdf_trash")) do
        if IsValid(ent) and ent.searcher == ply then
            return ent
        end
    end
    return nil
end

-- ─── Annulation si le joueur prend des dégâts ────────────────────────────
hook.Add("EntityTakeDamage", "SDFSystem.CancelOnDamage", function(target, dmg)
    -- On ne s'intéresse qu'aux joueurs.
    if not target:IsPlayer() then return end
    if not IsValid(target) then return end
    if dmg:GetDamage() <= 0 then return end

    local trashEnt = GetActiveTrash(target)
    if trashEnt then
        SDFSystem.CancelSearch(trashEnt, "dégâts")
    end
end)

-- ─── Annulation si le joueur meurt ───────────────────────────────────────
hook.Add("PlayerDeath", "SDFSystem.CancelOnDeath", function(victim, _, _)
    if not IsValid(victim) then return end
    local trashEnt = GetActiveTrash(victim)
    if trashEnt then
        SDFSystem.CancelSearch(trashEnt, "mort")
    end
end)

-- ─── Annulation si le joueur change de job ───────────────────────────────
-- DarkRP émet "playerChangedTeam" (ancien nom) ou "OnPlayerChangedTeam" selon la version.
hook.Add("OnPlayerChangedTeam", "SDFSystem.CancelOnJobChange", function(ply, _, newTeam)
    if not IsValid(ply) then return end
    local trashEnt = GetActiveTrash(ply)
    if trashEnt then
        SDFSystem.CancelSearch(trashEnt, "changement de job")
    end
end)

-- Fallback pour les versions DarkRP plus anciennes.
hook.Add("playerChangedTeam", "SDFSystem.CancelOnJobChangeLegacy", function(ply, _, newTeam)
    if not IsValid(ply) then return end
    local trashEnt = GetActiveTrash(ply)
    if trashEnt then
        SDFSystem.CancelSearch(trashEnt, "changement de job")
    end
end)

-- ─── Annulation si le joueur se déconnecte ────────────────────────────────
hook.Add("PlayerDisconnected", "SDFSystem.CancelOnDisconnect", function(ply)
    -- À la déco, le joueur est encore IsValid() dans ce hook.
    local trashEnt = GetActiveTrash(ply)
    if trashEnt then
        -- Stopper son et geste avant de réinitialiser l'état.
        trashEnt:StopSearchSound()
        trashEnt:StopGestureLoop()
        -- Annuler sans envoyer de net message (joueur parti).
        trashEnt.searcher        = nil
        trashEnt.searchEnd       = nil
        trashEnt.searchCompleted = nil
        trashEnt:SetSearchEnd(0)
        trashEnt:SetSearcher(NULL)
        trashEnt:SetSearchStart(0)
    end
    if IsValid(ply) then ply.sdfSearching = nil end
end)

-- ─── Re-spawn des poubelles depuis la DB ──────────────────────────────────
-- Réutilisable : appelé au boot (InitPostEntity) ET après un cleanup serveur
-- (PostCleanupMap), car un cleanup ("gmod_admin_cleanup", "Nettoyer tout", etc.)
-- supprime les SENT. On retire d'abord les poubelles encore présentes pour
-- éviter les doublons, puis on re-spawn depuis la DB.
function SDFSystem.RestoreAll()
    if not SDFSystem.DB or not SDFSystem.DB.GetAll then return end

    -- Purge des poubelles existantes (annule toute fouille en cours au passage).
    for _, ent in ipairs(ents.FindByClass("sdf_trash")) do
        if IsValid(ent) then ent:Remove() end
    end

    local rows  = SDFSystem.DB.GetAll()
    local count = 0

    for _, row in ipairs(rows) do
        local model = tostring(row.model or "")
        -- Valider que le model est toujours dans la whitelist (protection si la config a changé).
        if not SDFSystem.ModelIndex[model] then
            print("[SDFSystem] WARN : model '" .. model .. "' (id=" .. tostring(row.id) .. ") hors whitelist — ignoré.")
        else
            local pos = Vector(
                tonumber(row.x) or 0,
                tonumber(row.y) or 0,
                tonumber(row.z) or 0
            )
            local ang = Angle(
                tonumber(row.pitch) or 0,
                tonumber(row.yaw) or 0,
                tonumber(row.roll) or 0
            )

            local ent = ents.Create("sdf_trash")
            if IsValid(ent) then
                ent:SetModel(model)
                ent:SetPos(pos)
                ent:SetAngles(ang)
                ent:Spawn()
                ent.dbId = math.floor(tonumber(row.id) or 0)
                ent:SetDbId(ent.dbId)
                count = count + 1
            end
        end
    end

    print(string.format("[SDFSystem] %d poubelle(s) restaurée(s) depuis la DB.", count))
end

-- Boot serveur.
hook.Add("InitPostEntity", "SDFSystem.RestoreFromDB", function()
    SDFSystem.RestoreAll()
end)

-- Après un cleanup serveur : re-spawn (les SENT ont été supprimés par le cleanup).
-- timer.Simple(0) pour laisser le cleanup terminer entièrement avant de re-créer.
hook.Add("PostCleanupMap", "SDFSystem.RestoreAfterCleanup", function()
    timer.Simple(0, function()
        SDFSystem.RestoreAll()
    end)
end)

-- ─── Commande chat !sdf ───────────────────────────────────────────────────
hook.Add("PlayerSay", "SDFSystem.ChatCommand", function(ply, text, _)
    if not IsValid(ply) then return end
    -- Comparer en minuscules pour tolérer les variations de casse.
    if string.lower(string.Trim(text)) ~= string.lower(SDFSystem.Config.ChatCommand) then return end

    if not SDFSystem.SvConfig.CanManage(ply) then
        DarkRP.notify(ply, 1, 4, "[SDFSystem] Accès refusé.")
        return ""  -- supprimer le message chat
    end

    net.Start("SDFSystem.OpenMenu")
    net.Send(ply)

    return ""  -- supprimer le message chat (le joueur ne voit pas "!sdf" dans le chat)
end)

-- ─── Sync liste des entités lors de l'ouverture du menu ──────────────────
-- Le serveur envoie la liste au client qui vient d'ouvrir le menu.
-- (Déclenché depuis sv_sdf_system_net lors du net.Receive "SDFSystem.RequestMenuSync")
function SDFSystem.SendMenuSync(ply)
    if not IsValid(ply) then return end

    -- Construire d'abord la liste des entités valides, PUIS écrire le compteur :
    -- garantit que le nombre annoncé == le nombre réellement sérialisé.
    local valid = {}
    for _, ent in ipairs(ents.FindByClass("sdf_trash")) do
        if IsValid(ent) then valid[#valid + 1] = ent end
    end

    net.Start("SDFSystem.MenuSync")
        net.WriteUInt(#valid, SDFSystem.DB_ID_BITS)  -- nombre d'entités (max 65535)
        for _, ent in ipairs(valid) do
            local pos = ent:GetPos()
            net.WriteUInt(math.floor(ent.dbId or 0), SDFSystem.DB_ID_BITS)
            net.WriteString(ent:GetModel() or "")
            net.WriteFloat(pos.x)
            net.WriteFloat(pos.y)
            net.WriteFloat(pos.z)
        end
    net.Send(ply)
end
