#!/usr/bin/env lua

-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
--
-- SPDX-License-Identifier: GPL-3.0-or-later

--[[
Bundle script for wt - concatenates Lua modules into a single distributable file.

The bundled output embeds each module's source and provides a custom require()
that loads from the embedded sources first, falling back to the real require()
for standard library modules.

Usage: lua scripts/bundle.lua > wt
]]

--- Read entire file contents
---@param path string
---@return string|nil content
---@return string|nil error
local function read_file(path)
	local f, err = io.open(path, "r")
	if not f then
		return nil, err
	end
	local content = f:read("*a")
	f:close()
	return content
end

--- Check if file exists
---@param path string
---@return boolean
local function file_exists(path)
	local f = io.open(path, "r")
	if f then
		f:close()
		return true
	end
	return false
end

--- Determine appropriate long string delimiters for embedding
---@param s string
---@return string open delimiter
---@return string close delimiter
---@return string content (unchanged)
local function escape_for_longstring(s)
	local level = 0
	while s:find("]" .. string.rep("=", level) .. "]", 1, true) do
		level = level + 1
	end
	local open = "[" .. string.rep("=", level) .. "["
	local close = "]" .. string.rep("=", level) .. "]"
	return open, close, s
end

-- Module loading order matters: dependencies must come before dependents
-- This will be populated as modules are extracted
local MODULE_ORDER = {
	-- Core utilities (no internal dependencies)
	"wt.exit",    -- exit codes
	"wt.shell",   -- run_cmd, run_cmd_silent
	"wt.path",    -- path manipulation utilities
	"wt.git",     -- git utilities
	"wt.config",  -- config loading
	"wt.hooks",   -- hook system
	"wt.help",    -- help text
	-- Commands (depend on utilities)
	"wt.cmd.clone",
	"wt.cmd.new",
	"wt.cmd.add",
	"wt.cmd.remove",
	"wt.cmd.list",
	"wt.cmd.fetch",
	"wt.cmd.init",
}

-- Map module names to file paths
local MODULE_PATHS = {
	["wt.exit"]       = "src/wt/exit.lua",
	["wt.shell"]      = "src/wt/shell.lua",
	["wt.path"]       = "src/wt/path.lua",
	["wt.git"]        = "src/wt/git.lua",
	["wt.config"]     = "src/wt/config.lua",
	["wt.hooks"]      = "src/wt/hooks.lua",
	["wt.help"]       = "src/wt/help.lua",
	["wt.cmd.clone"]  = "src/wt/cmd/clone.lua",
	["wt.cmd.new"]    = "src/wt/cmd/new.lua",
	["wt.cmd.add"]    = "src/wt/cmd/add.lua",
	["wt.cmd.remove"] = "src/wt/cmd/remove.lua",
	["wt.cmd.list"]   = "src/wt/cmd/list.lua",
	["wt.cmd.fetch"]  = "src/wt/cmd/fetch.lua",
	["wt.cmd.init"]   = "src/wt/cmd/init.lua",
}

local function main()
	-- Collect all modules that exist
	local modules = {}
	for _, mod_name in ipairs(MODULE_ORDER) do
		local path = MODULE_PATHS[mod_name]
		if path and file_exists(path) then
			local content, err = read_file(path)
			if content then
				modules[mod_name] = content
			else
				io.stderr:write("warning: failed to read " .. path .. ": " .. (err or "unknown error") .. "\n")
			end
		end
	end

	-- Read main entry point
	local main_content, err = read_file("src/main.lua")
	if not main_content then
		io.stderr:write("error: failed to read src/main.lua: " .. (err or "unknown error") .. "\n")
		os.exit(1)
	end

	-- Start output
	io.write([[#!/usr/bin/env lua

-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
--
-- SPDX-License-Identifier: GPL-3.0-or-later

-- AUTO-GENERATED FILE - Do not edit directly
-- Edit src/*.lua and run 'make dist' to regenerate

]])

	-- If we have modules to embed, set up the custom require system
	local has_modules = next(modules) ~= nil

	if has_modules then
		io.write([[-- Embedded module loader
local _EMBEDDED_MODULES = {}
local _LOADED_MODULES = {}
local _real_require = require

local function _embedded_require(name)
	if _LOADED_MODULES[name] then
		return _LOADED_MODULES[name]
	end
	if _EMBEDDED_MODULES[name] then
		local loader = load(_EMBEDDED_MODULES[name], name)
		if loader then
			local result = loader()
			_LOADED_MODULES[name] = result or true
			return _LOADED_MODULES[name]
		end
	end
	return _real_require(name)
end
require = _embedded_require

]])

		-- Embed each module
		for _, mod_name in ipairs(MODULE_ORDER) do
			local content = modules[mod_name]
			if content then
				local open, close, escaped = escape_for_longstring(content)
				io.write("_EMBEDDED_MODULES[\"" .. mod_name .. "\"] = " .. open .. escaped .. close .. "\n\n")
			end
		end
	end

	-- Write main content (strip shebang if present, we already wrote one)
	if main_content:sub(1, 2) == "#!" then
		main_content = main_content:gsub("^#![^\n]*\n", "")
	end

	-- Strip SPDX header if present (we already wrote one)
	main_content = main_content:gsub("^%s*%-%-[^\n]*SPDX%-FileCopyrightText[^\n]*\n", "")
	main_content = main_content:gsub("^%s*%-%-[^\n]*\n", "") -- blank comment
	main_content = main_content:gsub("^%s*%-%-[^\n]*SPDX%-License%-Identifier[^\n]*\n", "")

	io.write(main_content)
end

main()
