-- laundromat/sv_commands.lua
-- Concommands admin : laundromat_spawn, laundromat_remove, laundromat_list, laundromat_cleanup.
-- Permission : SuperAdmin in-game, ou console RCON (ply == NULL).

Laundromat = Laundromat or {}

-- ============================================================================
-- Helper : check permission
-- ============================================================================

local function isSuperAdminOrRcon(ply)
    if not IsValid(ply) then return true end  -- console RCON
    return ply:IsSuperAdmin()
end

-- ============================================================================
-- Helper : feedback (console + notify client via Laundromat.Error)
-- ============================================================================

local function feedback(ply, msg)
    if IsValid(ply) then
        net.Start("Laundromat.Error")
            net.WriteString(string.sub(tostring(msg), 1, 128))
        net.Send(ply)
    end
    print("[Laundromat] " .. tostring(msg))
end

-- ============================================================================
-- Helper : résolution de coordonnées
-- In-game  → trace POV du joueur
-- Console  → args {x, y, z, yaw}
-- ============================================================================

local function resolveCoords(ply, args)
    if IsValid(ply) then
        local tr = ply:GetEyeTrace()
        if not tr or not tr.Hit then
            return nil, nil, "Trace invalide — regardez une surface."
        end
        return tr.HitPos, ply:EyeAngles().y, nil
    else
        if #args < 4 then
            return nil, nil, "Usage console : laundromat_spawn <x> <y> <z> <yaw>"
        end
        local x   = tonumber(args[1])
        local y   = tonumber(args[2])
        local z   = tonumber(args[3])
        local yaw = tonumber(args[4])
        if not x or not y or not z or not yaw then
            return nil, nil, "Coordonnées invalides (doivent être des nombres)."
        end
        return Vector(x, y, z), yaw, nil
    end
end

-- ============================================================================
-- laundromat_spawn
-- In-game : regarde une surface → spawn machine à cet endroit
-- Console  : laundromat_spawn <x> <y> <z> <yaw>
-- ============================================================================

concommand.Add("laundromat_spawn", function(ply, cmd, args)
    if not isSuperAdminOrRcon(ply) then
        if IsValid(ply) then
            DarkRP.notify(ply, 4, 5, "Permission refusée — SuperAdmin requis.")
        end
        return
    end

    local pos, yaw, err = resolveCoords(ply, args)
    if err then
        feedback(ply, "Erreur : " .. err)
        return
    end

    local mapName = game.GetMap()
    local id      = Laundromat.Storage.SaveMachine(mapName, pos, yaw)
    if not id then
        feedback(ply, "Erreur SQL lors de la sauvegarde de la machine.")
        return
    end

    local ent = Laundromat.SpawnMachine(pos, yaw, id)
    if not IsValid(ent) then
        feedback(ply, "Erreur lors du spawn de la machine (entité invalide).")
        Laundromat.Storage.RemoveMachine(id)
        return
    end

    feedback(ply, string.format(
        "Machine #%d spawée à (%.0f, %.0f, %.0f) yaw=%.1f sur map '%s'.",
        id, pos.x, pos.y, pos.z, yaw, mapName
    ))
end)

-- ============================================================================
-- laundromat_remove
-- In-game : regarde la machine → la supprime
-- Console  : non supporté (pas de trace disponible)
-- ============================================================================

concommand.Add("laundromat_remove", function(ply, cmd, args)
    if not isSuperAdminOrRcon(ply) then
        if IsValid(ply) then
            DarkRP.notify(ply, 4, 5, "Permission refusée — SuperAdmin requis.")
        end
        return
    end

    if not IsValid(ply) then
        print("[Laundromat] laundromat_remove requiert un joueur in-game (trace POV).")
        return
    end

    local tr = ply:GetEyeTrace()
    if not IsValid(tr.Entity) then
        feedback(ply, "Aucune entité visée.")
        return
    end

    local ent = tr.Entity
    if ent:GetClass() ~= "laundromat_machine" then
        feedback(ply, "L'entité visée n'est pas une machine de laverie.")
        return
    end

    -- Annuler la transaction en cours sur cette machine
    for sid, m in pairs(Laundromat._activeMachines) do
        if m == ent then
            if Laundering._transactions and Laundering._transactions[sid] then
                Laundering._cancelTransaction(sid, "entity_lost")
            end
            Laundromat._activeMachines[sid] = nil
            break
        end
    end

    local machineID = ent.LaundromatMachineID
    if machineID then
        local ok = Laundromat.Storage.RemoveMachine(machineID)
        if not ok then
            feedback(ply, string.format("Erreur SQL lors de la suppression de la machine #%d.", machineID))
            return
        end
    else
        feedback(ply, "Attention : machine sans ID (non persistée). Suppression en mémoire seulement.")
    end

    Laundromat._machines[ent] = nil
    ent._intentionalRemove = true  -- évite que EntityRemoved hook respawn
    ent:Remove()

    feedback(ply, string.format(
        "Machine %s supprimée.",
        machineID and ("#" .. machineID) or "(non persistée)"
    ))
end)

-- ============================================================================
-- laundromat_list
-- Affiche toutes les machines de la map courante
-- ============================================================================

concommand.Add("laundromat_list", function(ply, cmd, args)
    if not isSuperAdminOrRcon(ply) then
        if IsValid(ply) then
            DarkRP.notify(ply, 4, 5, "Permission refusée — SuperAdmin requis.")
        end
        return
    end

    local mapName = game.GetMap()
    local rows    = Laundromat.Storage.LoadMachinesForMap(mapName)

    print(string.format("[Laundromat] Machines sur la map '%s' (%d):", mapName, #rows))
    for _, row in ipairs(rows) do
        print(string.format(
            "  #%d  pos=(%.0f, %.0f, %.0f)  yaw=%.1f",
            row.id, row.x, row.y, row.z, row.angle_y
        ))
    end

    if IsValid(ply) then
        feedback(ply, string.format(
            "%d machine(s) sur '%s' — voir console serveur pour détails.",
            #rows, mapName
        ))
    end
end)

-- ============================================================================
-- laundromat_cleanup
-- Retire les entités laundromat_machine orphelines (non tracées dans _machines)
-- ============================================================================

concommand.Add("laundromat_cleanup", function(ply, cmd, args)
    if not isSuperAdminOrRcon(ply) then
        if IsValid(ply) then
            DarkRP.notify(ply, 4, 5, "Permission refusée — SuperAdmin requis.")
        end
        return
    end

    -- Refresh : annule les transactions en cours, supprime TOUTES les machines
    -- en jeu (registered + orphelines), puis recharge depuis la DB pour la map courante.

    -- 1) Annuler toutes les transactions actives sur des laverie
    if Laundering and Laundering._cancelTransaction then
        for sid, _ in pairs(Laundromat._activeMachines) do
            if Laundering._transactions and Laundering._transactions[sid] then
                Laundering._cancelTransaction(sid, "entity_lost")
            end
        end
    end
    Laundromat._activeMachines = {}

    -- 2) Supprimer toutes les machines en jeu (marquées intentionnel pour éviter
    --    le respawn auto via EntityRemoved hook)
    local existing = ents.FindByClass("laundromat_machine")
    local removed  = 0
    for _, ent in ipairs(existing) do
        if IsValid(ent) then
            ent._intentionalRemove = true
            ent:Remove()
            removed = removed + 1
        end
    end
    Laundromat._machines = {}

    -- 3) Recharger depuis la DB pour la map courante
    local mapName = game.GetMap()
    local rows    = Laundromat.Storage.LoadMachinesForMap(mapName)
    local spawned = 0
    for _, row in ipairs(rows) do
        local pos = Vector(row.x, row.y, row.z)
        local ent = Laundromat.SpawnMachine(pos, row.angle_y, row.id)
        if IsValid(ent) then spawned = spawned + 1 end
    end

    feedback(ply, string.format(
        "Cleanup terminé : %d machine(s) supprimée(s), %d machine(s) rechargée(s) depuis la DB.",
        removed, spawned
    ))
end)
