local M = {}

function M.msg(job, s) ya.preview_widget(job, ui.Text(ui.Line(s):reverse()):area(job.area):wrap(ui.Wrap.YES)) end

function M:peek(job)
	local url = job.file.url
	if not url:starts_with("/sys/") and not url:starts_with("/proc/") then
		return self.msg(job, "Empty file")
	end

	local fd, err = fs.access():read(true):open(url)
	if not fd then
		return self.msg(job, "Failed to open file: " .. err)
	end

	local lines, err = M.read_up_to(fd, job.skip, job.area.h)
	ya.drop(fd)

	if not lines then
		self.msg(job, tostring(err))
	elseif lines.n == 0 then
		self.msg(job, "Empty file")
	elseif job.skip > 0 and lines.n < job.skip + job.area.h then
		ya.emit("peek", { math.max(0, lines.n - job.area.h), only_if = url, upper_bound = true })
	else
		ya.preview_widget(job, ui.Text(lines):area(job.area))
	end
end

function M:seek(job) require("code"):seek(job) end

--- @param fd Fd
--- @param skip integer
--- @param limit integer
--- @return { [integer]: string, n: integer }?
--- @return Error?
function M.read_up_to(fd, skip, limit)
	local seen, lines = 0, { n = 0 }
	while true do
		local chunk, err = fd:read(4096)
		if not chunk then
			return nil, Err("Failed to read file: %s", err)
		elseif chunk == "" then
			break
		end

		seen = seen + #chunk
		if seen > 5242880 then
			return nil, Err("File too large")
		end

		for line in chunk:gmatch("[^\n]*\n?") do
			lines.n = lines.n + 1
			if lines.n > skip + limit then
				break
			elseif lines.n > skip then
				lines[#lines + 1] = line
			end
		end
	end
	return lines
end

return M
