local _ = require "mason-core.functional"

local M = {}

---@param registry_id string
---@return fun(): RegistrySource # Thunk to instantiate provider.
local function parse(registry_id)
    local type, id = registry_id:match "^(.+):(.+)$"
    if type == "github" then
        local namespace, name = id:match "^(.+)/(.+)$"
        if not namespace or not name then
            error(("Failed to parse repository from GitHub registry: %q."):format(registry_id), 0)
        end
        local name, version = unpack(vim.split(name, "@"))
        return function()
            local GitHubRegistrySource = require "mason-registry.sources.github"
            return GitHubRegistrySource.new {
                id = registry_id,
                repo = ("%s/%s"):format(namespace, name),
                namespace = namespace,
                name = name,
                version = version,
            }
        end
    elseif type == "lua" then
        return function()
            local LuaRegistrySource = require "mason-registry.sources.lua"
            return LuaRegistrySource.new {
                id = registry_id,
                mod = id,
            }
        end
    elseif type ~= nil then
        error(("Unknown registry type %q: %q."):format(type, registry_id), 0)
    end
    error(("Malformed registry id: %q."):format(registry_id), 0)
end

---@type ((fun(): RegistrySource) | RegistrySource)[]
local registries = {}

---@param registry_ids string[]
function M.set_registries(registry_ids)
    for _, registry in ipairs(registry_ids) do
        local ok, err = pcall(function()
            table.insert(registries, parse(registry))
        end)
        if not ok then
            local log = require "mason-core.log"
            local notify = require "mason-core.notify"
            log.fmt_error("Failed to parse registry %q: %s", registry, err)
            notify(err, vim.log.levels.ERROR)
        end
    end
end

---@param opts? { include_uninstalled?: boolean }
function M.iter(opts)
    opts = opts or {}
    local i = 1
    return function()
        while i <= #registries do
            local registry = registries[i]
            if type(registry) == "function" then
                -- unwrap thunk
                registry = registry()
                registries[i] = registry
            end
            i = i + 1
            if opts.include_uninstalled or registry:is_installed() then
                return registry
            end
        end
    end
end

---@return boolean #Returns true if all sources are installed.
function M.is_installed()
    for source in M.iter { include_uninstalled = true } do
        if not source:is_installed() then
            return false
        end
    end
    return true
end

---@return string # The sha256 checksum of the currently registered sources.
function M.checksum()
    ---@type string[]
    local registry_ids = {}
    for source in M.iter { include_uninstalled = true } do
        table.insert(registry_ids, source.id)
    end
    local checksum = _.compose(vim.fn.sha256, _.join "", _.sort_by(_.identity))
    return checksum(registry_ids)
end

return M
