local jid_split = require "util.jid".split;

local st = require "util.stanza";

local mod_groups = module:depends("groups_internal")
local mod_pep = module:depends("pep")

local PUBSUB_NODE_XEP0048 = "storage:bookmarks";
local XMLNS_XEP0048 = "storage:bookmarks";
local XMLNS_XEP0060 = "http://jabber.org/protocol/pubsub";

local default_options = {
	["persist_items"] = true;
	["access_model"] = "whitelist";
};

local function get_current_bookmarks(jid, service)
	local ok, id, item = service:get_last_item(PUBSUB_NODE_XEP0048, jid)
	if not ok or id == nil then
		if id == "item-not-found" or id == nil then
			-- return empty
			return st.stanza("storage", { xmlns = XMLNS_XEP0048 });
		end
		return nil, result
	end
	-- first item is the actual storage element
	local hit = item:get_child("storage", XMLNS_XEP0048);
	if not hit then
		return nil, "internal-server-error"
	end
	return hit
end

local function update_bookmarks(jid, service, storage)
	local item = st.stanza("item", { xmlns = XMLNS_XEP0060, id = "current" }):add_child(storage)
	module:log("debug", "updating bookmarks with %q", item)
	local ok, err = service:publish(
		PUBSUB_NODE_XEP0048,
		jid,
		"current",
		item,
		default_options
	)
	if not ok then
		module:log("error", "failed to update bookmarks: %s", err)
	end
end

local function find_matching_bookmark(storage, room)
	for node in storage:childtags("conference") do
		if node.attr.jid == room then
			return node
		end
	end
	return nil
end

local function inject_bookmark(jid, room, autojoin, name)
	local pep_service = mod_pep.get_pep_service(jid_split(jid))

	autojoin = autojoin or false and true
	local current = get_current_bookmarks(jid, pep_service)
	local existing = find_matching_bookmark(current, room)
	if existing then
		if autojoin ~= nil then
			existing.attr.autojoin = autojoin and "true" or "false"
		end
		if name ~= nil then
			-- do not change already configured names
			if not existing.attr.name then
				existing.attr.name = name
			end
		end
		done = true
		module:log("debug", "found existing matching bookmark, updated")
	else
		module:log("debug", "no existing bookmark found, adding new")
		current:tag("conference", {
			name = name,
			autojoin = autojoin and "true" or "false",
			jid = room,
			xmlns = XMLNS_XEP0048,
		})
	end

	update_bookmarks(jid, pep_service, current)
end

local function remove_bookmark(jid, room, autojoin, name)
	local pep_service = mod_pep.get_pep_service(jid_split(jid))

	autojoin = autojoin or false and true
	local current = get_current_bookmarks(jid, pep_service)
	if not current then
		return
	end
	current:maptags(function (node)
		if node.attr.xmlns and node.attr.xmlns ~= XMLNS_XEP0048 then
			return node
		end
		if node.name ~= "conference" then
			return node
		end
		if node.attr.jid == room then
			-- remove matching bookmark
			return nil
		end
		return node
	end)

	update_bookmarks(jid, pep_service, current)
end

local function handle_user_added(event)
	if not event.group_info.muc_jid then
		module:log("debug", "ignoring user added event on group %s because it has no MUC", event.id)
		return
	end
	local jid = event.user .. "@" .. event.host
	inject_bookmark(jid, event.group_info.muc_jid, true, event.group_info.name)
end

local function handle_user_removed(event)
	if not event.group_info.muc_jid then
		module:log("debug", "ignoring user removed event on group %s because it has no MUC", event.id)
		return
	end
	-- Removing the bookmark is fine as the user just lost any privilege to
	-- be in the MUC (as group MUCs are members-only).
	local jid = event.user .. "@" .. event.host
	remove_bookmark(jid, event.group_info.muc_jid, true, event.group_info.name)
end

module:hook("group-user-added", handle_user_added)
module:hook("group-user-removed", handle_user_removed)
