--[[
    SocietixM Plugin - Core Utilities
    Provides core functionality and utilities for the SocietixM plugin
]]

local Core = {}
Core.__index = Core

-- Utility functions
Core.Utils = {}

function Core.Utils.deepCopy(original)
    local copy = {}
    for key, value in pairs(original) do
        if type(value) == "table" then
            copy[key] = Core.Utils.deepCopy(value)
        else
            copy[key] = value
        end
    end
    return copy
end

function Core.Utils.mergeTables(target, source)
    for key, value in pairs(source) do
        if type(value) == "table" and type(target[key]) == "table" then
            Core.Utils.mergeTables(target[key], value)
        else
            target[key] = value
        end
    end
    return target
end

function Core.Utils.validateConfig(config, schema)
    for key, expectedType in pairs(schema) do
        if config[key] == nil then
            return false, "Missing required field: " .. key
        end
        
        if type(config[key]) ~= expectedType then
            return false, "Invalid type for field " .. key .. ": expected " .. expectedType .. ", got " .. type(config[key])
        end
    end
    
    return true
end

function Core.Utils.generateId(length)
    length = length or 8
    local chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    local id = ""
    
    for i = 1, length do
        local rand = math.random(1, #chars)
        id = id .. string.sub(chars, rand, rand)
    end
    
    return id
end

function Core.Utils.generateHash(input)
    local hash = 0
    for i = 1, #input do
        hash = hash + string.byte(input, i)
    end
    return tostring(hash % 1000000)
end

function Core.Utils.formatTime(seconds)
    local hours = math.floor(seconds / 3600)
    local minutes = math.floor((seconds % 3600) / 60)
    local secs = seconds % 60
    
    if hours > 0 then
        return string.format("%02d:%02d:%02d", hours, minutes, secs)
    else
        return string.format("%02d:%02d", minutes, secs)
    end
end

function Core.Utils.formatBytes(bytes)
    local units = {"B", "KB", "MB", "GB", "TB"}
    local unitIndex = 1
    local size = bytes
    
    while size >= 1024 and unitIndex < #units do
        size = size / 1024
        unitIndex = unitIndex + 1
    end
    
    return string.format("%.2f %s", size, units[unitIndex])
end

function Core.Utils.sanitizeString(str)
    if type(str) ~= "string" then
        return ""
    end
    
    -- Remove control characters and limit length
    str = string.gsub(str, "[%c]", "")
    str = string.sub(str, 1, 1000)
    
    return str
end

function Core.Utils.validateRobloxId(id)
    if type(id) ~= "string" and type(id) ~= "number" then
        return false
    end
    
    local strId = tostring(id)
    if #strId == 0 or #strId > 20 then
        return false
    end
    
    -- Check if all characters are digits
    for i = 1, #strId do
        local char = string.sub(strId, i, i)
        if char < "0" or char > "9" then
            return false
        end
    end
    
    return true
end

function Core.Utils.validateSocietixId(id)
    if type(id) ~= "string" then
        return false
    end
    
    if #id < 8 or #id > 32 then
        return false
    end
    
    -- Check if it contains only valid characters
    for i = 1, #id do
        local char = string.sub(id, i, i)
        if not ((char >= "a" and char <= "z") or 
                (char >= "A" and char <= "Z") or 
                (char >= "0" and char <= "9") or 
                char == "_" or char == "-") then
            return false
        end
    end
    
    return true
end

-- Configuration schemas
Core.Schemas = {
    ServerConfig = {
        Name = "string",
        Description = "string",
        RobloxGameId = "string",
        MaxPlayers = "number",
        IsPrivate = "boolean",
        AllowFriends = "boolean",
        AllowSpectators = "boolean",
        Region = "string",
        EnableMods = "boolean",
        Password = "string",
        MapName = "string",
        GameMode = "string"
    },
    
    ServerInfo = {
        Id = "string",
        Name = "string",
        Description = "string",
        Owner = "string",
        PlayerCount = "number",
        MaxPlayers = "number",
        RobloxGameId = "string",
        SocietixId = "string",
        IsPrivate = "boolean",
        MapName = "string",
        GameMode = "string",
        Version = "string"
    }
}

-- Event system
Core.Events = {}

function Core.Events.create()
    local events = {}
    
    function events:connect(callback)
        if type(callback) ~= "function" then
            error("Callback must be a function")
        end
        
        local connection = {
            Callback = callback,
            Connected = true
        }
        
        table.insert(events, connection)
        return connection
    end
    
    function events:fire(...)
        for _, connection in pairs(events) do
            if connection.Connected and type(connection.Callback) == "function" then
                local success, result = pcall(connection.Callback, ...)
                if not success then
                    warn("[SocietixM] Event callback error:", result)
                end
            end
        end
    end
    
    function events:disconnect(connection)
        if connection and connection.Connected then
            connection.Connected = false
        end
    end
    
    return events
end

-- Logger system
Core.Logger = {}

function Core.Logger.log(level, message, ...)
    local timestamp = os.date("%Y-%m-%d %H:%M:%S")
    local formattedMessage = string.format(message, ...)
    print(string.format("[%s] [%s] %s", timestamp, level, formattedMessage))
end

function Core.Logger.info(message, ...)
    Core.Logger.log("INFO", message, ...)
end

function Core.Logger.warn(message, ...)
    Core.Logger.log("WARN", message, ...)
end

function Core.Logger.error(message, ...)
    Core.Logger.log("ERROR", message, ...)
end

function Core.Logger.debug(message, ...)
    Core.Logger.log("DEBUG", message, ...)
end

-- Settings management
Core.Settings = {}

function Core.Settings.load()
    local success, settings = pcall(function()
        return game:GetService("HttpService"):JSONDecode(game:GetService("DataStoreService"):GetDataStore("SocietixMSettings"):GetAsync("settings") or "{}")
    end)
    
    if success then
        return settings
    else
        warn("[SocietixM] Failed to load settings:", settings)
        return {}
    end
end

function Core.Settings.save(settings)
    local success, result = pcall(function()
        local json = game:GetService("HttpService"):JSONEncode(settings)
        game:GetService("DataStoreService"):GetDataStore("SocietixMSettings"):SetAsync("settings", json)
    end)
    
    if not success then
        warn("[SocietixM] Failed to save settings:", result)
    end
    
    return success
end

function Core.Settings.get(key, defaultValue)
    local settings = Core.Settings.load()
    return settings[key] or defaultValue
end

function Core.Settings.set(key, value)
    local settings = Core.Settings.load()
    settings[key] = value
    return Core.Settings.save(settings)
end

-- Performance monitoring
Core.Performance = {}

function Core.Performance.measure(func, name)
    local startTime = tick()
    local success, result = pcall(func)
    local endTime = tick()
    local duration = endTime - startTime
    
    Core.Logger.debug("Performance: %s took %.3f seconds", name or "function", duration)
    
    return success, result, duration
end

function Core.Performance.createTimer()
    local startTime = tick()
    
    return {
        elapsed = function()
            return tick() - startTime
        end,
        
        reset = function()
            startTime = tick()
        end
    }
end

-- Error handling
Core.ErrorHandler = {}

function Core.ErrorHandler.handle(error, context)
    local errorMessage = tostring(error)
    local contextMessage = context or "Unknown context"
    
    Core.Logger.error("Error in %s: %s", contextMessage, errorMessage)
end

function Core.ErrorHandler.wrap(func, context)
    return function(...)
        local success, result = pcall(func, ...)
        if not success then
            Core.ErrorHandler.handle(result, context)
        end
        return success, result
    end
end

return Core
