local core = require("core")
local command = require("core.command")
-- Text cursor translation helpers are scoped under core.text
local translate = require("core.text.translate")
local native_text = require("text") -- Split normalized table rows without a Lua pattern loop
local utf8help = require("core.text.utf8help")

--- Split table text while keeping each original LF attached to its row
-- Normal LF documents use native splitting while CR/CRLF text keeps the exact
-- fallback needed to preserve its original bytes
-- @param text string selected table text
-- @return table lines rows with their original LF attached
local function split_lines_keep_lf(text)
	if text:find("\r", 1, true) then
		local lines = {}

		-- Pattern breakdown:
		-- '[^\n]*' captures row bytes and '\n?' keeps its optional LF attached
		for line in text:gmatch("[^\n]*\n?") do
			lines[#lines + 1] = line
		end

		return lines
	end

	local fragments = native_text.split_lines(text, { keep_eol = false })
	local has_final_newline = text:sub(-1) == "\n"
	local line_count = #fragments - (has_final_newline and 1 or 0)
	local lines = {}

	for index = 1, line_count do
		local has_eol = index < line_count or has_final_newline
		lines[index] = fragments[index] .. (has_eol and "\n" or "")
	end

	return lines
end

--- Splits one line on a literal delimiter while retaining empty fields
-- @param line string source line
-- @param delimiter string literal delimiter
-- @return table fields
local function split_fields(line, delimiter)
	local fields = {}
	local start = 1

	while true do
		local delimiter_start, delimiter_end = line:find(delimiter, start, true)

		if not delimiter_start then
			fields[#fields + 1] = line:sub(start)

			return fields
		end

		fields[#fields + 1] = line:sub(start, delimiter_start - 1)
		start = delimiter_end + 1
	end
end

--- Align literal-delimited rows by displayed column width
-- @param lines table rows with original line endings
-- @param delim string literal delimiter
local function tabularize_lines(lines, delim)
	local rows = {}
	local cols = {}

	-- Split literal delimiters and retain leading, repeated, and trailing empty fields
	for i, line in ipairs(lines) do
		rows[i] = split_fields(line, delim)
		for j, col in ipairs(rows[i]) do
			cols[j] = math.max(utf8help.width(col), cols[j] or 0)
		end
	end

	-- pad columns with space
	for _, row in ipairs(rows) do
		for i = 1, #row - 1 do
			row[i] = row[i] .. string.rep(" ", cols[i] - utf8help.width(row[i]))
		end
	end

	-- write columns back to lines array
	for i, line in ipairs(lines) do
		lines[i] = table.concat(rows[i], delim)
	end
end

command.register({
	["tabularize:tabularize"] = {
		requires = { "doc", "edit" },
		perform = function()
		core.command_view:enter({
			caption = "Tabularize On Delimiter",
			submit = function(delim)
				delim = delim ~= "" and delim or " "

			local doc = core.active_view.doc
			local line1, col1, line2, col2, swap = doc:get_selection(true)
			line1, col1 = doc:position_offset(line1, col1, translate.start_of_line)
			line2, col2 = doc:position_offset(line2, col2, translate.end_of_line)
			doc:set_selection(line1, col1, line2, col2, swap)

			doc:replace(function(text)
				local lines = split_lines_keep_lf(text)
				tabularize_lines(lines, delim)
				return table.concat(lines)
			end)
			end,
		})
		end,
	},
})
