-- init.lua — Logique serveur du SENT sdf_trash.
-- Gère Use (lancement fouille), Think throttlé (vérifications), animation, son.

AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")

include("shared.lua")

-- ─── Helper format durée ─────────────────────────────────────────────────
local function FormatSecs(secs)
    secs = math.max(0, math.floor(secs))
    if secs >= 60 then
        local m = math.floor(secs / 60)
        local s = secs - m * 60
        return m .. "m " .. s .. "s"
    end
    return secs .. "s"
end

-- ─── Initialisation physique ─────────────────────────────────────────────
function ENT:Initialize()
    local mdl = self:GetModel()
    if not mdl or mdl == "" then
        self:SetModel("models/props_junk/trashbin01a.mdl")
    end

    self:PhysicsInit(SOLID_VPHYSICS)
    self:SetMoveType(MOVETYPE_VPHYSICS)
    self:SetSolid(SOLID_VPHYSICS)
    self:SetUseType(SIMPLE_USE)

    local phys = self:GetPhysicsObject()
    if IsValid(phys) then
        phys:EnableMotion(false)
        phys:Wake()
    end

    -- Initialisation des NetworkVars additionnelles.
    self:SetSearchStart(0)
    self:SetLastRarity(0)
    self:SetRarityFlashTime(0)
end

-- ─── Interdire le physgun aux non-admins ──────────────────────────────────
function ENT:PhysgunPickup(ply)
    return IsValid(ply) and (ply:IsAdmin() or ply:IsSuperAdmin())
end

-- ─── Son de fouille ──────────────────────────────────────────────────────
-- Émet le son configurable en boucle via un timer nommé unique à l'entité.
function ENT:StartSearchSound()
    local cfg  = SDFSystem.Config.Immersion
    local snd  = cfg.SearchSound or ""
    if snd == "" then return end

    local timerName = "SDFSystem.SearchSound." .. self:EntIndex()
    local vol   = tonumber(cfg.SearchSoundVolume) or 0.8
    local pitch = tonumber(cfg.SearchSoundPitch)  or 100

    -- Émettre immédiatement, puis boucler toutes les 1.5s.
    self:EmitSound(snd, 75, pitch, vol)
    timer.Create(timerName, 1.5, 0, function()
        if not IsValid(self) or not IsValid(self.searcher) then
            timer.Remove(timerName)
            return
        end
        self:EmitSound(snd, 75, pitch, vol)
    end)
end

function ENT:StopSearchSound()
    local cfg = SDFSystem.Config.Immersion
    local snd = cfg.SearchSound or ""
    if snd ~= "" then
        self:StopSound(snd)
    end
    timer.Remove("SDFSystem.SearchSound." .. self:EntIndex())
end

-- ─── Animation (geste de fouille) ────────────────────────────────────────
-- Rejoue le geste périodiquement via un timer pour simuler une boucle.
function ENT:StartGestureLoop()
    local cfg = SDFSystem.Config.Immersion
    local act = cfg.GestureAct
    local interval = tonumber(cfg.GestureInterval) or 2.0

    if not act then return end

    local timerName = "SDFSystem.Gesture." .. self:EntIndex()
    local ply = self.searcher

    local function doGesture()
        if not IsValid(self) or not IsValid(self.searcher) then
            timer.Remove(timerName)
            return
        end
        local p = self.searcher
        if not IsValid(p) or not p:Alive() then return end
        -- AnimRestartGesture est robuste : ne crash pas si l'ACT n'existe pas.
        local ok, _ = pcall(function()
            p:AnimRestartGesture(GESTURE_SLOT_CUSTOM, act, true)
        end)
        if not ok then
            -- L'ACT n'existe pas sur ce modèle → stopper silencieusement.
            timer.Remove(timerName)
        end
    end

    doGesture()  -- premier geste immédiat
    timer.Create(timerName, interval, 0, doGesture)
end

function ENT:StopGestureLoop()
    timer.Remove("SDFSystem.Gesture." .. self:EntIndex())
    -- Retirer le geste proprement.
    if IsValid(self.searcher) then
        pcall(function()
            self.searcher:AnimRemoveGesture(GESTURE_SLOT_CUSTOM)
        end)
    end
end

-- ─── Use (touche E) ───────────────────────────────────────────────────────
function ENT:Use(activator, _)
    if not IsValid(activator) or not activator:IsPlayer() then return end

    if activator:Team() ~= TEAM_HOBO then
        DarkRP.notify(activator, 1, 3, "Seuls les SDF peuvent fouiller les poubelles.")
        return
    end

    local now = CurTime()
    if (activator.sdfNextSearch or 0) > now then return end
    activator.sdfNextSearch = now + SDFSystem.SvConfig.SearchStartCooldown

    if self.cooldownEnd and self.cooldownEnd > now then
        local remaining = math.ceil(self.cooldownEnd - now)
        DarkRP.notify(activator, 1, 3,
            "Cette poubelle a déjà été fouillée (encore " ..
            FormatSecs(remaining) .. ").")
        return
    end

    if IsValid(self.searcher) and self.searcher ~= activator then
        DarkRP.notify(activator, 1, 3, "Cette poubelle est déjà en cours de fouille.")
        return
    end

    if IsValid(self.searcher) and self.searcher == activator then return end

    for _, ent in ipairs(ents.FindByClass("sdf_trash")) do
        if IsValid(ent) and ent ~= self and ent.searcher == activator then
            DarkRP.notify(activator, 1, 3, "Vous fouilllez déjà une poubelle.")
            return
        end
    end

    -- ─── Démarrage de la fouille ──────────────────────────────────────────
    local duration    = SDFSystem.Config.SearchDuration
    self.searcher     = activator
    self.searchStart  = now
    self.searchEnd    = now + duration
    self.searchCompleted = nil
    activator.sdfSearching = true  -- flag pour l'accroupissement (SetupMove)

    -- NetworkVars
    self:SetSearchEnd(self.searchEnd)
    self:SetSearchStart(self.searchStart)
    self:SetSearcher(activator)
    self:SetCooldownEnd(0)

    self.nextThinkCheck   = now + 0.25
    self.nextGestureCheck = now  -- déclenchement immédiat du timer geste

    -- Immersion
    self:StartSearchSound()
    self:StartGestureLoop()

    if SDFSystem.SvConfig.Debug then
        print(string.format("[SDFSystem] %s commence à fouiller %s (durée %ds)",
            activator:Nick(), tostring(self), duration))
    end
end

-- ─── Think (throttlé) ────────────────────────────────────────────────────
function ENT:Think()
    if not IsValid(self.searcher) then
        self:NextThink(CurTime() + 1)
        return true
    end

    local now = CurTime()
    if now < (self.nextThinkCheck or 0) then
        self:NextThink(self.nextThinkCheck)
        return true
    end
    self.nextThinkCheck = now + 0.25

    local ply = self.searcher

    if not IsValid(ply) or not ply:Alive() then
        SDFSystem.CancelSearch(self, "joueur invalide")
        self:NextThink(CurTime() + 1)
        return true
    end

    local maxDist = SDFSystem.Config.MaxSearchDistance
    if self:GetPos():DistToSqr(ply:GetPos()) > maxDist * maxDist then
        SDFSystem.CancelSearch(self, "éloignement")
        DarkRP.notify(ply, 1, 3, "Fouille interrompue : vous vous êtes éloigné.")
        self:NextThink(CurTime() + 1)
        return true
    end

    if ply:Team() ~= TEAM_HOBO then
        SDFSystem.CancelSearch(self, "changement de job")
        self:NextThink(CurTime() + 1)
        return true
    end

    if self.searchEnd and now >= self.searchEnd then
        SDFSystem.CompletSearch(ply, self)
        self:NextThink(CurTime() + 1)
        return true
    end

    self:NextThink(self.nextThinkCheck)
    return true
end

-- ─── Cleanup quand l'entité est supprimée ────────────────────────────────
function ENT:OnRemove()
    self:StopSearchSound()
    self:StopGestureLoop()
    if IsValid(self.searcher) then
        SDFSystem.CancelSearch(self, "entité supprimée")
    end
end
