Version = Version or {}

local function parseVersion(versionString)
    return versionString and versionString:match('%d+%.%d+%.%d+') or versionString
end

local function padVersion(version)
    local parts = {}
    for num in version:gmatch("%d+") do
        table.insert(parts, tonumber(num))
    end
    while #parts < 3 do
        table.insert(parts, 0)
    end
    return parts
end

local function compareVersions(v1, v2)
    local a, b = padVersion(v1), padVersion(v2)
    for i = 1, 3 do
        if a[i] < b[i] then
            return -1
        elseif a[i] > b[i] then
            return 1
        end
    end
    return 0
end

local function buildPatchNotesUrl(branch, repoPath)
    local username, reponame = repoPath:match("([^/]+)/([^/]+)")
    return ('https://raw.githubusercontent.com/%s/%s/%s/resources.json'):format(username, reponame, branch)
end

local function getDownloadUrl(repoPath, isTebex, actualRepoPath)
    if isTebex then
        return "https://portal.cfx.re/assets/granted-assets"
    elseif actualRepoPath then
        local username, reponame = actualRepoPath:match("([^/]+)/([^/]+)")
        if username and reponame then return ('https://github.com/%s/%s/releases/latest'):format(username, reponame) end
    end
    return nil
end

local function printUpdateAvailable(resource, localVersion, downloadUrl)
    print(('^1An update is available for %s (current version: %s)\r\n - ^5Please download the latest version from %s^7'):format(resource, localVersion, downloadUrl))
end

local function printPatchNotes(notesToPrint)
    print('^3Patch Notes:^7')
    for _, entry in ipairs(notesToPrint) do
        print(('^2Version %s:^7'):format(entry.version))
        for _, line in ipairs(entry.notes) do
            print((' - %s'):format(line))
        end
    end
end

local function validateRepoPath(repoPath)
    local username, reponame = repoPath:match("([^/]+)/([^/]+)")
    if not username or not reponame then return false, print('Invalid repository format. Expected format: username/reponame') end
    return true, username, reponame
end

local function getLocalVersion(resource)
    local localVersionRaw = GetResourceMetadata(resource, "version", 0)
    if not localVersionRaw then
        return nil, print('No version metadata found for resource "%s". Ensure fxmanifest.lua contains a valid version string.'):format(resource)
    end
    local version = parseVersion(localVersionRaw)
    if not version then return nil, print('Invalid version format for resource "%s". Expected format: "x.x.x"'):format(resource) end
    return version
end

local function processVersions(allVersions, localVersion)
    local notesToPrint = {}

    for versionStr, notes in pairs(allVersions) do
        if compareVersions(versionStr, localVersion) > 0 and type(notes) == "table" then
            table.insert(notesToPrint, { version = versionStr, notes = notes })
        end
    end

    table.sort(notesToPrint, function(a, b)
        return compareVersions(a.version, b.version) < 0
    end)

    return notesToPrint
end

local function fetchPatchNotes(branch, resource, repoPath, localVersion)
    local url = buildPatchNotesUrl(branch, repoPath)

    PerformHttpRequest(url, function(noteStatus, noteBody)
        if noteStatus == 200 then
            local patchData = json.decode(noteBody)
            if not patchData then return end

            local resourceData = patchData[resource]
            if resourceData and type(resourceData) == "table" then
                local actualRepoPath = resourceData.repo or resourceData.actualRepoPath
                local isTebex = not actualRepoPath -- If no repo path, it's Tebex
                local allVersions = resourceData.versions or resourceData -- Support both new and old format
                local notesToPrint = processVersions(allVersions, localVersion)

                if #notesToPrint > 0 then
                    local downloadUrl = getDownloadUrl(repoPath, isTebex, actualRepoPath) or url
                    printUpdateAvailable(resource, localVersion, downloadUrl)
                    printPatchNotes(notesToPrint)
                end
            end
        elseif branch == "main" then
            -- Fallback to master branch if main doesn't exist
            fetchPatchNotes("master", resource, repoPath, localVersion)
        end
    end, 'GET')
end

--- Pass a github username and repo name to check for the latest version of the resource.
--- Tebex is a optional boolean to determine if the message should be for a escrowed resource or not.
--- Example: Version.VersionChecker("TheOrderFivem/community_bridge", false)
--- @param repoPath string
--- @param tebex boolean | nil
--- @return nil
function Version.VersionChecker(repoPath, tebex)
    local username, reponame = repoPath:match("([^/]+)/([^/]+)")
    local resource = reponame and repoPath:match("([^/]+)$")
    if not username or not reponame or not resource then return print('^1Invalid repository format. Expected format: "username/reponame"^0') end

    local version = GetResourceMetadata(resource, "version", 0)
    if not version then return end

    version = version:match('%d+%.%d+%.%d+')
    if not version then return end

    PerformHttpRequest(('https://api.github.com/repos/%s/%s/releases/latest'):format(username, reponame),
        function(status, response)
            if status ~= 200 then return end
            local latest = json.decode(response).tag_name:match('%d+%.%d+%.%d+')
            if not latest or latest == version or version:gsub('%D', '') >= latest:gsub('%D', '') then return end

            local url = tebex and "https://portal.cfx.re/assets/granted-assets" or json.decode(response).html_url
            print(('^1An update is available for %s (current version: %s)\r\n - ^5Please download the latest version from %s^7'):format(resource, version, url))
        end,
    'GET')
end

--- Advanced version checker with patch notes support
--- Reads Tebex information and repo path directly from the resources.json file
--- @param repoPath string GitHub repository path for patch notes in format "username/reponame"
--- @param resourceName string The actual resource name to check
--- @return nil
function Version.AdvancedVersionChecker(repoPath, resourceName)
    local isValid, username, reponame = validateRepoPath(repoPath)
    if not isValid then return print(('^1[ERROR]^7 %s'):format(username)) end

    local resource = resourceName
    local localVersion, errorMsg = getLocalVersion(resource)
    if not localVersion then return print(('^1[ERROR]^7 %s'):format(username)) end
    fetchPatchNotes("main", resource, repoPath, localVersion)
end

AddEventHandler('onResourceStart', function(resourceName)
    if GetCurrentResourceName() ~= resourceName then return end
    -- Version.VersionChecker("TheOrderFivem/community_bridge", false)
    Version.AdvancedVersionChecker("TheOrderFivem/patchnotes", resourceName)
end)

return Version