AddCSLuaFile()

PackUwUs = PackUwUs or {}

--- Hash convar that should be set serverside
PackUwUs.packuwus_hash = CreateConVar("packuwus_hash", "", FCVAR_REPLICATED)

file.CreateDir("packuwus")

if SERVER then
    file.CreateDir("serve_packuwus")
end

PackUwUs.LogFileHandle = file.Open("packuwus/log.txt", "w", "DATA") --[[@as File]]

if not PackUwUs.LogFileHandle then
    print("!!! PackUwUs failed to open \"packuwus/log.txt\" !!!")
end

local logFileHandle = PackUwUs.LogFileHandle
local packuwus_debug = CreateConVar("packuwus_debug", "1", FCVAR_ARCHIVE) --[[@as ConVar]]
local packuwus_console_debug = CreateConVar("packuwus_console_debug", "0", FCVAR_ARCHIVE) --[[@as ConVar]]

---Returns `true` if convar `packuwus_debug` is not zero
---@return boolean
function PackUwUs.IsDebugEnabled()
    return packuwus_debug:GetBool()
end

local CowoR_CUTE    = { r = 225, g = 150, b = 255, a = 255 }
local COLOR_DEBUG   = { r = 155, g = 230, b = 255, a = 255 }
local COLOR_DEFAULT = { r = 255, g = 255, b = 255, a = 255 }
local COLOR_OK      = { r = 0,   g = 255, b = 150, a = 255 }
local COLOR_WARNING = { r = 255, g = 150, b = 0,   a = 255 }
local COLOR_ERROR   = { r = 255, g = 150, b = 150, a = 255 }

---Logs message into console and to log file
---@param level string
---@param color { r: integer, g: integer, b: integer, a: integer }
---@param fmt string
---@param ... any
function PackUwUs.LogEx(level, color, fmt, ...)
    xpcall(function(...)
        if logFileHandle then
            logFileHandle:Write(string.format("%s\t%.5f\t" .. fmt .. "\n", level, SysTime(), ...))

            logFileHandle:Flush()
        end

        if level == "D" and not packuwus_console_debug:GetBool() then
            return
        end

        MsgC(CowoR_CUTE, "[PackUwUs] ", color, string.format(fmt .. "\n", ...))
    end, ErrorNoHaltWithStack, ...)
end

---Logs debug message
---@param fmt string
---@param ... any
function PackUwUs.Debug(fmt, ...)
    if not PackUwUs.IsDebugEnabled() then return end

    PackUwUs.LogEx("D", COLOR_DEBUG, fmt, ...)
end

---Logs default message
---@param fmt string
---@param ... any
function PackUwUs.Log(fmt, ...)
    PackUwUs.LogEx("LOG", COLOR_DEFAULT, fmt, ...)
end

---Logs success message
---@param fmt string
---@param ... any
function PackUwUs.Ok(fmt, ...)
    PackUwUs.LogEx("OK", COLOR_OK, fmt, ...)
end

---Logs warning message
---@param fmt string
---@param ... any
function PackUwUs.Warn(fmt, ...)
    PackUwUs.LogEx("W", COLOR_WARNING, fmt, ...)
end

---Logs error message
---@param fmt string
---@param ... any
function PackUwUs.Error(fmt, ...)
    PackUwUs.LogEx("E", COLOR_ERROR, fmt, ...)
end

---Makes `path` relative to non-addon/non-gamemode path
---
---addons/myaddon/lua/autorun/test.lua => lua/autorun/test.lua
---gamemodes/base/entities/someent.lua => lua/entities/someent.lua
---
---@param path string
---@return string
function PackUwUs.FixPath(path)
    path = string.lower(path)
    path = string.gsub(path, "\\", "/")
    path = string.gsub(path, "/+", "/")

    local parts = {}

    for part in string.gmatch(path, "([^/]+)") do
        if part == ".." then
            table.remove(parts)
        elseif part ~= "." then
            table.insert(parts, part)
        end
    end

    local partPath = table.concat(parts, "/")

    local addonlessPath = string.match(partPath, "^addons/[^/]+/(.+)$")

    if addonlessPath then
        PackUwUs.Debug("FixPath(\"%s\"): removing addon prefix = \"%s\"", path, partPath)

        partPath = addonlessPath
    end

    local prefixlessPath = string.match(partPath, "^lua/(.+)$")

    if prefixlessPath then
        PackUwUs.Debug("FixPath(\"%s\") hit lua = \"%s\"", path, prefixlessPath)

        return prefixlessPath
    end

    prefixlessPath = string.match(partPath, "^gamemodes/[^/]+/entities/(.+)$")

    if prefixlessPath then
        PackUwUs.Debug("FixPath(\"%s\") hit gm ents = \"%s\"", path, prefixlessPath)

        return prefixlessPath
    end

    prefixlessPath = string.match(partPath, "^gamemodes/([^/]+/gamemode/.+)$")

    if prefixlessPath then
        PackUwUs.Debug("FixPath(\"%s\") hit gm gm = \"%s\"", path, prefixlessPath)

        return prefixlessPath
    end

    PackUwUs.Debug("FixPath(\"%s\") did not hit any pattern, returning \"%s\"", path, partPath)

    return partPath
end

require("hook")

AddCSLuaFile("packuwus/sh_utils.lua")
AddCSLuaFile("packuwus/cl_main.lua")
AddCSLuaFile("packuwus/cl_impl.lua")
AddCSLuaFile("packuwus/cl_startup.lua")

include("packuwus/sh_utils.lua")

if CLIENT then
    include("packuwus/cl_main.lua")
    include("packuwus/cl_impl.lua")
    include("packuwus/cl_startup.lua")
end

if SERVER then
    include("packuwus/sv_utils.lua")
    include("packuwus/sv_main.lua")
    include("packuwus/sv_impl.lua")
    include("packuwus/sv_startup.lua")
end

-- adding them cus we want them pack
AddCSLuaFile("packuwus/cl_debug_helpers.lua")
