-- lx-bundle:true

--- DOCX extraction commands for binary-viewer documents.
-- @module plugins.docxutils

local core = require("core")
local command = require("core.command")
local Doc = require("core.doc")

local reader

--- Load the DOCX reader only when extraction needs it
-- @return table reader_module
local function get_reader()
	if not reader then
		reader = require("plugins.docxutils.reader")
	end

	return reader
end

--- Return the active binary DOCX document by declared format or filename
-- @return table|nil doc
local function active_docx_doc()
	local view = core.active_view
	local doc = view and view.doc
	local filename = doc and doc.filename
	local declared_type = doc and type(doc.format) == "table" and doc.format.office_type or nil

	local format_matches = declared_type == "docx"
		or declared_type == nil and type(filename) == "string" and filename:lower():sub(-5) == ".docx"

	if doc and doc.is_binary_placeholder == true and format_matches then
		return doc
	end

	return nil
end

--- Return whether the active view contains a supported binary DOCX document
-- @return boolean active
local function is_active_docx()
	return active_docx_doc() ~= nil
end

--- Open or replace one extracted DOCX output
-- @param source_doc table source Office document
-- @param content string extracted content
-- @param suffix string generated filename suffix
-- @param context table|nil command display options
-- @return boolean opened
local function open_output_doc(source_doc, content, suffix, context)
	local doc = Doc.create((source_doc.filename or "untitled") .. suffix)
	local last_line = #doc.lines
	local last_col = #(doc.lines[last_line] or "") + 1
	doc:remove(1, 1, last_line, last_col)
	doc:insert(1, 1, content)
	doc:set_selection(1, 1)
	doc.save_as_required = true
	doc:clean()

	local view_options = {
		open_mode = context and context.open_mode or nil,
	}
	local view

	if context and context.replace_view then
		view = core.workspace_view:replace_doc_view(context.replace_view, doc, view_options)
	else
		view = core.workspace_view:open_doc(doc, view_options)
	end

	return view ~= nil
end

--- Extract the active DOCX document into one generated document
-- @param mode string text or markdown
-- @param context table|nil command display options
-- @return boolean extracted
local function extract(mode, context)
	local doc = active_docx_doc()
	if not doc then
		core.error("DOCX extraction requires an active binary DOCX document")
		return false
	end

	local content, err
	if mode == "markdown" then
		content, err = get_reader().extract_markdown(doc.filename)
	else
		content, err = get_reader().extract_text(doc.filename)
	end

	if not content then
		core.error("DOCX extraction failed: %s", tostring(err))
		return false
	end

	local suffix = mode == "markdown" and ".extracted.md" or ".extracted.txt"
	if not open_output_doc(doc, content, suffix, context) then
		core.error("DOCX extraction succeeded, but its document could not be displayed")
		return false
	end
	core.log('Extracted "%s" as %s', doc.filename, mode == "markdown" and "Markdown" or "text")
	return true
end

command.register({
	["docx:extract-text"] = {
		predicate = is_active_docx,
		perform = function(context)
			return extract("text", context)
		end,
	},

	["docx:extract-md"] = {
		predicate = is_active_docx,
		perform = function(context)
			return extract("markdown", context)
		end,
	},
})

--- Read one DOCX file on demand
-- @param ... any reader arguments
-- @return ... any reader results
local function read(...)
	return get_reader().read(...)
end

--- Extract plain text from one DOCX file on demand
-- @param ... any reader arguments
-- @return ... any reader results
local function extract_text(...)
	return get_reader().extract_text(...)
end

--- Extract Markdown from one DOCX file on demand
-- @param ... any reader arguments
-- @return ... any reader results
local function extract_markdown(...)
	return get_reader().extract_markdown(...)
end

return {
	read = read,
	extract_text = extract_text,
	extract_markdown = extract_markdown,
}
