--[[
    Commander 3.0 - VFS Path Utilities

    Path manipulation for the Virtual Filesystem.
]]

local ADDON_NAME, C3 = ...

local Paths = {}
C3.Paths = Paths

---Normalize a path (resolve . and .., ensure leading /)
---@param path string
---@return string
function Paths.Normalize(path)
    -- Ensure we have a string
    path = tostring(path or "")

    -- Empty path becomes root
    if path == "" then
        return "/"
    end

    -- Split into parts
    local parts = {}
    for part in path:gmatch("[^/]+") do
        if part == ".." then
            -- Go up one level
            if #parts > 0 then
                table.remove(parts)
            end
        elseif part ~= "." and part ~= "" then
            table.insert(parts, part)
        end
    end

    -- Rebuild path
    if #parts == 0 then
        return "/"
    end

    return "/" .. table.concat(parts, "/")
end

---Join path segments
---@param ... string Path segments
---@return string
function Paths.Join(...)
    local parts = { ... }
    local result = ""

    for _, part in ipairs(parts) do
        if part:sub(1, 1) == "/" then
            -- Absolute path, start fresh
            result = part
        elseif result == "" or result == "/" then
            result = "/" .. part
        else
            result = result .. "/" .. part
        end
    end

    return Paths.Normalize(result)
end

---Get the parent directory of a path
---@param path string
---@return string
function Paths.Parent(path)
    path = Paths.Normalize(path)

    if path == "/" then
        return "/"
    end

    local lastSlash = path:match(".*/()")
    if lastSlash and lastSlash > 1 then
        local parent = path:sub(1, lastSlash - 2)
        -- If parent is empty (path was like "/home"), return root
        if parent == "" then
            return "/"
        end
        return parent
    end

    return "/"
end

---Get the filename (last component) of a path
---@param path string
---@return string
function Paths.Filename(path)
    path = Paths.Normalize(path)

    if path == "/" then
        return ""
    end

    return path:match("[^/]+$") or ""
end

---Get the file extension
---@param path string
---@return string?
function Paths.Extension(path)
    local filename = Paths.Filename(path)
    return filename:match("%.([^.]+)$")
end

---Get filename without extension
---@param path string
---@return string
function Paths.Basename(path)
    local filename = Paths.Filename(path)
    return filename:match("(.+)%.[^.]+$") or filename
end

---Check if path is absolute
---@param path string
---@return boolean
function Paths.IsAbsolute(path)
    return path:sub(1, 1) == "/"
end

---Make a path relative to a base path
---@param path string
---@param base string
---@return string
function Paths.Relative(path, base)
    path = Paths.Normalize(path)
    base = Paths.Normalize(base)

    -- Ensure base ends without /
    if base ~= "/" and base:sub(-1) == "/" then
        base = base:sub(1, -2)
    end

    -- Check if path starts with base
    if path:sub(1, #base) == base then
        local relative = path:sub(#base + 1)
        if relative == "" then
            return "."
        elseif relative:sub(1, 1) == "/" then
            return relative:sub(2)
        end
    end

    return path
end

---Split path into directory and filename
---@param path string
---@return string dir
---@return string name
function Paths.Split(path)
    local dir = Paths.Parent(path)
    local name = Paths.Filename(path)
    return dir, name
end

---Check if a path matches a pattern
---@param path string
---@param pattern string (supports * and **)
---@return boolean
function Paths.Match(path, pattern)
    -- Convert glob pattern to Lua pattern
    local luaPattern = pattern
        :gsub("([%.%+%-%[%]%(%)%$%^])", "%%%1") -- Escape special chars
        :gsub("%*%*", "\0")                      -- Temporarily mark **
        :gsub("%*", "[^/]*")                     -- * matches within segment
        :gsub("\0", ".*")                        -- ** matches across segments

    return path:match("^" .. luaPattern .. "$") ~= nil
end

---Iterate over path segments
---@param path string
---@return function iterator
function Paths.Segments(path)
    path = Paths.Normalize(path)
    return path:gmatch("[^/]+")
end

---Get all ancestor paths
---@param path string
---@return table
function Paths.Ancestors(path)
    path = Paths.Normalize(path)
    local ancestors = {}
    local current = path

    while current ~= "/" do
        current = Paths.Parent(current)
        table.insert(ancestors, 1, current)
    end

    return ancestors
end

return Paths
