local M = {}
local uv = vim.loop
local api = vim.api
local function read_file(path)
	local fd = io.open(path, "r")
	if not fd then
		return nil
	end
	local content = fd:read("*a")
	fd:close()
	return content
end
local function write_file(path, content)
	local dir = path:match("(.*/)[^/]+$")
	if dir then
		vim.fn.mkdir(dir, "p")
	end
	local fd = io.open(path, "w")
	if not fd then
		error("Cannot write " .. path)
	end
	fd:write(content)
	fd:close()
end
-- Minimal XML attribute extractor for a tag like:  
local function extract_attributes(tag)
	local attrs = {}
	for k, v in tag:gmatch('(%w+)%s*=\\s*"([^"]*)"') do
		attrs[k] = v
	end
	return attrs
end
-- Given the whole xml content, return table:
-- { type = "Application", name = "MyApp", options = { [name] = value, ... }, env = {k=v}, method = {...} }
local function parse_run_configuration(xml)
	-- find  ...  
	local cfg_tag, body = xml:match("]*)>(.-) ")
	if not cfg_tag then
		-- sometimes configuration is self-closing:  
		cfg_tag = xml:match(" ]+)/>")
		body = ""
	end
	if not cfg_tag then
		return nil
	end
	local cfg_attrs = extract_attributes(cfg_tag)
	local result = { _attrs = cfg_attrs, options = {}, env = {}, method = {} }
	-- options:  
	for option in body:gmatch("/]-)/>") do
		local a = extract_attributes(option)
		if a.name and a.value then
			result.options[a.name] = a.value
		end
	end
	-- envs:  
	local envs_block = body:match("(.-) ")
	if envs_block then
		for envtag in envs_block:gmatch("/]-)/>") do
			local a = extract_attributes(envtag)
			if a.name and a.value then
				result.env[a.name] = a.value
			end
		end
	end
	-- method (for JUnit):     
	local method_block = body:match("]*)>(.-) ")
	if method_block then
		local method_tag, method_body = body:match("]*)>(.-) ")
		if method_tag then
			result.method._attrs = extract_attributes(method_tag)
			result.method.options = {}
			for opt in method_body:gmatch("/]-)/>") do
				local a = extract_attributes(opt)
				if a.name and a.value then
					result.method.options[a.name] = a.value
				end
			end
		end
	end
	return result
end
-- Map a single parsed config to a VSCode launch configuration (as Lua table)
local function map_to_vscode(name, parsed)
	local t = parsed._attrs.type or parsed._attrs.factoryName or ""
	local vscode = {
		name = name or parsed._attrs.name or "Converted",
		request = "launch",
	}
	-- Common Application type
	if t:match("[Aa]pplication") or parsed.options.MAIN_CLASS_NAME then
		vscode.type = "java"
		vscode.mainClass = parsed.options.MAIN_CLASS_NAME or parsed.options.MAIN_CLASS
		if parsed.options.PROGRAM_PARAMETERS and parsed.options.PROGRAM_PARAMETERS ~= "" then
			-- split by space respecting simple quotes (basic)
			local args = {}
			for a in parsed.options.PROGRAM_PARAMETERS:gmatch("%S+") do
				table.insert(args, a)
			end
			vscode.args = args
		end
		if parsed.options.VM_PARAMETERS and parsed.options.VM_PARAMETERS ~= "" then
			vscode.vmArgs = parsed.options.VM_PARAMETERS
		end
		if parsed.options.WORKING_DIRECTORY and parsed.options.WORKING_DIRECTORY ~= "" then
			vscode.cwd = parsed.options.WORKING_DIRECTORY:gsub("%$PROJECT_DIR%$", "${workspaceFolder}")
		else
			vscode.cwd = "${workspaceFolder}"
		end
		if next(parsed.env) then
			vscode.env = parsed.env
		end
		-- If module present, attach projectName (useful for java extension)
		if parsed.options.MODULE_NAME then
			vscode.projectName = parsed.options.MODULE_NAME
		end
		return vscode
	end
	-- JUnit test configuration
	if
		t:match("[Jj]Unit")
		or parsed.method.options and (parsed.method.options.CLASS_NAME or parsed.method.options.METHOD_NAME)
	then
		vscode.type = "java"
		-- Use test runner config via mainClass (JUnit runner) or use builtin test adapters
		if parsed.method.options.CLASS_NAME then
			vscode.name = (parsed._attrs.name or "JUnit:") .. " " .. parsed.method.options.CLASS_NAME
			-- map to a launch that runs the single test class
			vscode.mainClass = parsed.method.options.CLASS_NAME
			vscode.args = {}
		end
		if next(parsed.env) then
			vscode.env = parsed.env
		end
		vscode.cwd = parsed.options.WORKING_DIRECTORY
				and parsed.options.WORKING_DIRECTORY:gsub("%$PROJECT_DIR%$", "${workspaceFolder}")
			or "${workspaceFolder}"
		return vscode
	end
	-- Gradle run config
	if t:match("[Gg]radle") or parsed._attrs.type == "Gradle" or parsed.options.TASK_NAME then
		-- Represent as a 'command' launch using terminal.integrated (fallback)
		vscode.type = "pwa-node" -- generic terminal-ish; user may change
		vscode.name = parsed._attrs.name or "Gradle Task"
		local task = parsed.options.TASK_NAME or parsed.options.GRADLE_TASK
		vscode.request = "launch"
		vscode.runtimeExecutable = "gradle"
		vscode.args = {}
		if task and task ~= "" then
			for tkn in (task .. ""):gmatch("%S+") do
				table.insert(vscode.args, tkn)
			end
		end
		vscode.cwd = parsed.options.WORKING_DIRECTORY
				and parsed.options.WORKING_DIRECTORY:gsub("%$PROJECT_DIR%$", "${workspaceFolder}")
			or "${workspaceFolder}"
		if next(parsed.env) then
			vscode.env = parsed.env
		end
		return vscode
	end
	-- Fallback: produce a shell launch that echos an unsupported type
	return {
		type = "pwa-node",
		name = parsed._attrs.name or "Converted (unsupported)",
		request = "launch",
		program = "${file}",
		cwd = "${workspaceFolder}",
	}
end
-- Main: scan .idea/runConfigurations and convert
function M.convert_all(opts)
	opts = opts or {}
	local pattern = ".run/*.xml"
	local files = vim.tbl_filter(function(p)
		return p ~= ""
	end, vim.fn.glob(pattern, false, true))
	if #files == 0 then
		error(".run not found or no xml files present")
	end
	local configs = {}
	for _, f in ipairs(files) do
		local content = read_file(f)
		if not content then
			vim.notify("Cannot read " .. f, vim.log.levels.WARN)
		end
		local name = f:match("([^/]+)%.xml$") or f
		local parsed = parse_run_configuration(content)
		if parsed then
			local vs = map_to_vscode(name, parsed)
			table.insert(configs, vs)
		else
			vim.notify("Skipping (unrecognized) " .. f, vim.log.levels.DEBUG)
		end
	end
	local launch = { version = "0.2.0", configurations = configs }
	local ok, j = pcall(vim.fn.json_encode, launch)
	if not ok then
		error("Failed to encode launch.json")
	end
	write_file(".vscode/launch.json", j)
	return true
end
return M