--- Build strict file-reference rules for Python source files
-- @module formats.python.references

local python_references = {}

-- Pattern breakdown: import statements are line-anchored; the comma form reaches later modules without matching 'from' symbols
local import_first_prefix = "^%s*import%s+"
local import_later_prefix = "^%s*import%s+.-,%s*"
local from_prefix = "^%s*from%s+"

--- Return whether one Python module name contains valid dotted identifiers
-- @param reference table parsed module reference
-- @return boolean accepted
local function is_module(reference)
	local module = reference.path:gsub("^%.+", "")
	if module == "" or module:find("..", 1, true) then
		return false
	end

	for part in module:gmatch("[^%.]+") do
		if not part:match("^[%a_][%w_]*$") then
			return false
		end
	end

	return true
end

--- Convert a dotted Python module into its module and package filenames
-- @param reference table parsed module reference
-- @return table candidates
local function module_candidates(reference)
	local leading_dots = reference.path:match("^(%.+)") or ""
	local module = reference.path:sub(#leading_dots + 1):gsub("%.", PATHSEP)
	local parents = {}

	-- One leading dot means this package; every additional dot moves to one parent package
	for _ = 2, #leading_dots do
		parents[#parents + 1] = ".."
	end
	if #parents > 0 then
		module = table.concat(parents, PATHSEP) .. PATHSEP .. module
	end

	return {
		module .. ".py",
		module .. PATHSEP .. "__init__.py",
	}
end

--- Return direct Python import references
-- @return table rules
function python_references.new()
	return {
		{ prefix = import_first_prefix, chars = "[%w_%.]", validate = is_module, candidates = module_candidates },
		{ prefix = import_later_prefix, chars = "[%w_%.]", validate = is_module, candidates = module_candidates },
		{ prefix = from_prefix, chars = "[%w_%.]", validate = is_module, candidates = module_candidates },
	}
end

return python_references
