local utils = require("ghostnotes.utils")
local get_note_headline = require("ghostnotes.note_operations.getters").get_note_headline
local M = {}

local config = require("ghostnotes.config")
local ns = vim.api.nvim_create_namespace(config.opts.namespace)
function M.edit_or_view_note()
	local bufnr = vim.api.nvim_get_current_buf()
	local bufname = vim.api.nvim_buf_get_name(bufnr)
	local row = vim.api.nvim_win_get_cursor(0)[1] - 1

	local git_root = utils.get_git_root()
	local note = nil
	local global_path = utils.get_global_path()

	if git_root then
		local project_path = git_root .. "/.ghostnotes.json"
		for _, n in ipairs(utils.read_json(project_path)) do
			if n.bufname == bufname and n.row == row then
				note = vim.deepcopy(n)
			end
		end
	end
	if not note then
		for _, n in ipairs(utils.read_json(global_path)) do
			if n.bufname == bufname and n.row == row then
				note = vim.deepcopy(n)
				break
			end
		end
	end

	local lines = {}
	if note then
		for line in note.text:gmatch("([^\n]*)\n?") do
			table.insert(lines, line)
		end
	end
	if #lines == 0 then
		lines = { "" }
	end

	local float_buf = vim.api.nvim_create_buf(false, true)
	vim.api.nvim_buf_set_lines(float_buf, 0, -1, false, lines)
	vim.api.nvim_buf_set_option(float_buf, "bufhidden", "wipe")
	vim.api.nvim_buf_set_option(float_buf, "filetype", "markdown")

	-- Implements normal :w / :wq / :x/ZZ etc
	vim.api.nvim_buf_set_option(float_buf, "buftype", "acwrite")
	vim.api.nvim_buf_set_option(float_buf, "swapfile", false)
	pcall(
		vim.api.nvim_buf_set_name,
		float_buf,
		("ghostnote://%s:%d"):format(vim.fn.fnamemodify(bufname, ":t"), row + 1)
	)

	pcall(vim.api.nvim_buf_set_option, float_buf, "modified", false)

	local editor_width = vim.o.columns
	local editor_height = vim.o.lines

	local float_width = math.floor(editor_width * 0.5)
	local float_height = math.max(math.floor(editor_height * 0.75), #lines + 2)

	local win = vim.api.nvim_open_win(float_buf, true, {
		relative = "editor",
		width = float_width,
		height = float_height,
		row = math.floor((editor_height - float_height) / 2),
		col = math.floor((editor_width - float_width) / 2),
		style = "minimal",
		border = { "╭", "─", "╮", "│", "╯", "─", "╰", "│" },
		title = " ⏎ :w/:q save/exit ┃ L1: headline ┃ L2+: body ",
		title_pos = "center",
	})

	vim.api.nvim_win_set_option(win, "wrap", true)

	local function upsert_note(text)
		-- text is a table of lines from the buffer, join with newlines
		text = table.concat(text, "\n")

		local new_note = {
			bufnr = bufnr,
			row = row,
			text = text,
			bufname = bufname,
			timestamp = os.date("!%Y-%m-%dT%H:%M:%SZ"),
		}
		local function upsert(path)
			local notes = utils.read_json(path)
			local found = false
			for i, n in ipairs(notes) do
				if n.bufname == bufname and n.row == row then
					notes[i] = new_note
					found = true
					break
				end
			end
			if not found then
				table.insert(notes, new_note)
			end
			utils.write_json(path, notes)
		end
		if git_root then
			upsert(git_root .. "/.ghostnotes.json")
		end
		upsert(global_path)
		vim.api.nvim_buf_clear_namespace(bufnr, ns, row, row + 1)
		local display_text = get_note_headline(new_note)
		vim.api.nvim_buf_set_extmark(bufnr, ns, row, 0, {
			virt_text = { { config.opts.note_prefix .. display_text, "Comment" } },
			virt_text_pos = "eol",
		})
		vim.notify(note and "Saved ghost note" or "Added ghost note", vim.log.levels.INFO)
	end

	local function delete_note()
		local function remove_note(path)
			local notes = utils.read_json(path)
			local filtered = vim.tbl_filter(function(n)
				return not (n.bufname == bufname and n.row == row)
			end, notes)
			utils.write_json(path, filtered)
		end
		if git_root then
			remove_note(git_root .. "/.ghostnotes.json")
		end
		remove_note(global_path)
		vim.api.nvim_buf_clear_namespace(bufnr, ns, row, row + 1)
		vim.notify("Deleted ghost note", vim.log.levels.INFO)
	end

	-- Handles :w, :wq, :x, ZZ via BufWriteCmd
	vim.api.nvim_create_autocmd("BufWriteCmd", {
		buffer = float_buf,
		callback = function()
			local new_lines = vim.api.nvim_buf_get_lines(float_buf, 0, -1, false)
			local new_text = table.concat(new_lines, "\n")
			if new_text:match("^%s*$") then
				if note then
					delete_note()
				end
			else
				upsert_note(new_lines)
			end
			pcall(vim.api.nvim_buf_set_option, float_buf, "modified", false)
		end,
		desc = "GhostNotes save",
	})

	vim.cmd("stopinsert")
end

return M
