--[[
    Parallax Framework
    Copyright (c) 2025 Parallax Framework Contributors

    This file is part of the Parallax Framework and is licensed under the MIT License.
    You may use, copy, modify, merge, publish, distribute, and sublicense this file
    under the terms of the LICENSE file included with this project.

    Attribution is required. If you use or modify this file, you must retain this notice.
]]

ax.command:Add("SetGravity", {
    description = "Set world gravity scale.",
    adminOnly = true,
    arguments = {
        { name = "scale", type = ax.type.number, min = 0.1, max = 3, decimals = 2 }
    },
    OnRun = function(client, scale)
        RunConsoleCommand("sv_gravity", tostring(600 * scale))
        return "Gravity set to " .. scale .. "x"
    end
})

ax.command:Add("PM", {
    alias = {"tell", "whisper"},
    description = "Send a private message to another player.",
    arguments = {
        { name = "target", type = ax.type.player },
        { name = "message", type = ax.type.text }
    },
    OnRun = function(client, target, message)
        if ( !IsValid(target) ) then
            return "Target player not found."
        end

        target:ChatPrint("[PM from " .. client:Nick() .. "] " .. message)
        client:ChatPrint("[PM to " .. target:Nick() .. "] " .. message)

        return ""
    end
})

ax.command:Add("CharSetModel", {
    description = "Set the model of a character.",
    arguments = {
        { name = "target", type = ax.type.character },
        { name = "model", type = ax.type.text }
    },
    OnRun = function(client, target, model)
        if ( !target) then return "Invalid character." end

        target:SetModel(model, true)
        target:Save()

        return "Model set to " .. model
    end
})

ax.command:Add("CharSetSkin", {
    description = "Set the skin of a character.",
    arguments = {
        { name = "target", type = ax.type.character },
        { name = "skin", type = ax.type.number }
    },
    OnRun = function(client, target, skin)
        if ( !target ) then return "Invalid character." end

        target:SetData("skin", skin)
        target:Save()

        local targetPlayer = target:GetOwner()
        if ( IsValid(targetPlayer) ) then
            targetPlayer:SetSkin(skin)
        end

        return "Skin set to " .. skin
    end
})

ax.command:Add("CharSetName", {
    description = "Set the name of a character.",
    arguments = {
        { name = "target", type = ax.type.character, optional = true },
        { name = "name", type = ax.type.string, optional = true }
    },
    OnRun = function(client, target, name)
        if ( !target ) then
            target = client:GetCharacter()
        end

        if ( !name or name == "" ) then
            net.Start("ax.character.setnameprompt")
                net.WriteUInt(target.id, 32)
                net.WriteString(target:GetName())
            net.Send(client)

            return
        end

        target:SetName(name, true)
        target:Save()

        return "Name set to " .. name
    end
})

ax.command:Add("CharGiveFlags", {
    description = "Give flags to a character.",
    arguments = {
        { name = "target", type = ax.type.character },
        { name = "flags", type = ax.type.string }
    },
    OnRun = function(client, target, flags)
        if ( !target ) then target = client:GetCharacter() end

        target:GiveFlags(flags)
        target:Save()

        return "Flags given to " .. target:GetName() .. ": " .. flags
    end
})

ax.command:Add("CharTakeFlags", {
    description = "Take flags from a character.",
    arguments = {
        { name = "target", type = ax.type.character },
        { name = "flags", type = ax.type.string }
    },
    OnRun = function(client, target, flags)
        if ( !target ) then target = client:GetCharacter() end

        target:TakeFlags(flags)
        target:Save()

        return "Flags taken from " .. target:GetName() .. ": " .. flags
    end
})

ax.command:Add("CharSetFlags", {
    description = "Set flags for a character.",
    arguments = {
        { name = "target", type = ax.type.character, optional = true },
        { name = "flags", type = ax.type.string }
    },
    OnRun = function(client, target, flags)
        if ( !target ) then target = client:GetCharacter() end

        target:SetFlags(flags)
        target:Save()

        return "Flags set for " .. target:GetName() .. ": " .. flags
    end
})

ax.command:Add("CharSetFaction", {
    description = "Set the faction of a character.",
    arguments = {
        { name = "target", type = ax.type.character, optional = true },
        { name = "faction", type = ax.type.string }
    },
    OnRun = function(client, target, faction)
        if ( !target ) then
            target = client:GetCharacter()
        end

        if ( !target ) then
            return "Invalid character."
        end

        local factionTable = ax.faction:Get(faction)
        if ( !factionTable ) then
            return "Invalid faction."
        end

        -- Get the old faction before changing
        local oldFaction = target:GetFaction()
        local oldFactionTable = ax.faction:Get(oldFaction)

        -- Set the new faction
        target:SetFaction(factionTable.index, true)
        target:Save()

        -- Call OnTransferred callback if it exists on the new faction
        if ( isfunction(factionTable.OnTransferred) ) then
            local player = target:GetOwner()
            if ( IsValid(player) ) then
                local success, err = pcall(factionTable.OnTransferred, factionTable, player, oldFaction)
                if ( !success ) then
                    ax.util:PrintError("Error in faction OnTransferred callback: " .. tostring(err))
                end
            end
        end

        return "Faction set to " .. factionTable.name
    end
})

ax.command:Add("CharSetClass", {
    description = "Set the class of a character.",
    arguments = {
        { name = "target", type = ax.type.character, optional = true },
        { name = "class", type = ax.type.string }
    },
    OnRun = function(client, target, class)
        if ( !target ) then
            target = client:GetCharacter()
        end

        if ( !target ) then
            return "Invalid character."
        end

        local classTable = ax.class:Get(class)
        if ( !classTable ) then
            return "Invalid class."
        end

        target:SetClass(classTable.index, true)
        target:Save()

        -- Call OnTransferred callback if it exists on the new class
        if ( isfunction(classTable.OnTransferred) ) then
            local player = target:GetOwner()
            if ( IsValid(player) ) then
                local success, err = pcall(classTable.OnTransferred, classTable, player)
                if ( !success ) then
                    ax.util:PrintError("Error in class OnTransferred callback: " .. tostring(err))
                end
            end
        end

        return "Class set to " .. classTable.name
    end
})

ax.command:Add("PlyWhitelist", {
    description = "Whitelist a player for a faction.",
    arguments = {
        { name = "target", type = ax.type.player },
        { name = "faction", type = ax.type.string }
    },
    OnRun = function(client, target, faction)
        if ( !IsValid(target) ) then return "Invalid player." end

        local factionTable = ax.faction:Get(faction)
        if ( !factionTable ) then return "Invalid faction." end
        if ( factionTable.isDefault ) then return "This faction does not require whitelisting." end

        local whitelists = target:GetData("whitelists", {})
        whitelists[factionTable.index] = true
        target:SetData("whitelists", whitelists)
        target:Save()

        return target:Nick() .. "( " .. target:SteamName() .. " ) has been whitelisted for " .. factionTable.name .. "."
    end
})

ax.command:Add("PlyUnWhitelist", {
    description = "Remove a player's whitelist for a faction.",
    arguments = {
        { name = "target", type = ax.type.player },
        { name = "faction", type = ax.type.string }
    },
    OnRun = function(client, target, faction)
        if ( !IsValid(target) ) then return "Invalid player." end

        local factionTable = ax.faction:Get(faction)
        if ( !factionTable ) then return "Invalid faction." end
        if ( factionTable.isDefault ) then return "This faction does not require whitelisting." end

        local whitelists = target:GetData("whitelists", {})
        whitelists[factionTable.index] = nil
        target:SetData("whitelists", whitelists)
        target:Save()

        return target:Nick() .. "( " .. target:SteamName() .. " ) has been unwhitelisted for " .. factionTable.name .. "."
    end
})

ax.command:Add("PlyRespawn", {
    description = "Respawn a player.",
    arguments = {
        { name = "target", type = ax.type.player }
    },
    OnRun = function(client, target)
        if ( !IsValid(target) ) then return "Invalid player." end

        target:Spawn()

        return "Player " .. target:Nick() .. " has been respawned."
    end
})

ax.command:Add("BotAdd", {
    description = "Add a bot to the server for testing purposes.",
    adminOnly = true,
    arguments = {
        { name = "name", type = ax.type.string, optional = true }
    },
    OnRun = function(client, name)
        if ( SERVER ) then
            local botName = name or "TestBot"
            RunConsoleCommand("bot")
            
            return "Adding bot: " .. botName
        else
            return "This command can only be run on the server."
        end
    end
})

ax.command:Add("BotKick", {
    description = "Remove all bots from the server.",
    adminOnly = true,
    OnRun = function(client)
        if ( SERVER ) then
            local count = 0
            for _, target in player.Iterator() do
                if ( target:IsBot() ) then
                    target:Kick("Removed by admin")
                    count = count + 1
                end
            end
            
            return "Removed " .. count .. " bot(s) from the server."
        else
            return "This command can only be run on the server."
        end
    end
})

ax.command:Add("BotSupport", {
    description = "Enable or disable automatic bot character creation.",
    adminOnly = true,
    arguments = {
        { name = "enabled", type = ax.type.bool }
    },
    OnRun = function(client, enabled)
        if ( SERVER ) then
            ax.config:Set("botSupport", enabled)
            ax.config:Save()
            
            return "Bot support " .. (enabled and "enabled" or "disabled") .. "."
        else
            return "This command can only be run on the server."
        end
    end
})

ax.command:Add("BotList", {
    description = "List all bots currently on the server.",
    adminOnly = true,
    OnRun = function(client)
        if ( SERVER ) then
            local bots = {}
            for _, ply in player.Iterator() do
                if ( ply:IsBot() ) then
                    local character = ply:GetCharacter()
                    local charName = character and character:GetName() or "No Character"
                    local faction = character and ax.faction:Get(character:GetFaction()) 
                    local factionName = faction and faction.name or "No Faction"
                    
                    table.insert(bots, {
                        name = ply:SteamName(),
                        character = charName,
                        faction = factionName
                    })
                end
            end
            
            if ( #bots == 0 ) then
                return "No bots currently on the server."
            end
            
            local result = "Bots on server (" .. #bots .. "):\n"
            for i, bot in ipairs(bots) do
                result = result .. "  " .. bot.name .. " (" .. bot.character .. " - " .. bot.faction .. ")\n"
            end
            
            return result
        else
            return "This command can only be run on the server."
        end
    end
})
