-- mod_muc_peertubelivechat_terms
--
-- SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
-- SPDX-License-Identifier: AGPL-3.0-only
--
-- This file is AGPL-v3 licensed.
-- Please see the Peertube livechat plugin copyright information.
-- https://livingston.frama.io/peertube-plugin-livechat/credits/
--

-- Exposed functions:
-- get_muc_terms
-- set_muc_terms

local jid_escape = require "util.jid".escape;
local jid_resource = require "util.jid".resource;
local st = require "util.stanza";
local id = require "util.id";
local datetime = require "util.datetime";

local service_nickname = module:get_option_string("muc_terms_service_nickname", "Service");
local global_terms = module:get_option_string("muc_terms_global", "");

local function create_terms_message(room, type, terms)
  -- Note: we can't send the message from "room.jid": XMPP clients such as Gajim would ignore them.
  -- So we use a service_nickname.
  local from = room.jid .. '/' .. jid_escape(service_nickname);
  module:log("debug", "Creating %s terms message from %s (room %s)", type, from, room);
  local msg = st.message({
    type = "groupchat",
    from = from,
    id = id.medium()
  }, terms)
    :tag('x-livechat-terms', { type = type }):up() -- adding a custom tag to specify that it is a "terms" message, so that frontend can display it with a special template.
    :tag("delay", { xmlns = "urn:xmpp:delay", from = from, stamp = datetime.datetime() }):up() -- adding a delay to trick the moderation bot (see below)
    :tag("no-copy", { xmlns = "urn:xmpp:hints" }):up()
    :tag("no-store", { xmlns = "urn:xmpp:hints" }):up()
    :tag("no-permanent-store", { xmlns = "urn:xmpp:hints" }):up();
  -- concerning the delay tag:
  -- We are sending message to rooms from non-existant occupants.
  -- If the message contains something that should be moderated by the livechat moderation bot,
  -- it could generate some error logs (the bot will not find the user in the occupant list).
  -- At time of writing, the xmppjs-chat-bot ignore delayed message... so we use this hack to trick the bot.
  return msg;
end

-- MUC Getter/Setter
function get_muc_terms(room)
  return room._data.livechat_muc_terms or nil;
end

function set_muc_terms(room, terms)
  if terms == "" then
    terms = nil;
  end

  if get_muc_terms(room) == terms then return false; end

  room._data.livechat_muc_terms = terms;
  if terms ~= nil then
    -- we must send new terms to all occupants.
    local msg = create_terms_message(room, "muc", terms);
    module:log("debug", "Broadcasting terms message to room %s", room);
    room:broadcast_message(msg);
  end
  return true;
end

-- send the terms when joining:
local function send_terms(event)
  local origin = event.origin;
	local room = event.room;
	local occupant = event.occupant;
  if global_terms then
    module:log("debug", "Sending global terms to %s", occupant.jid);
    local msg = create_terms_message(room, "global", global_terms);
    msg.attr.to = occupant.jid;
    origin.send(msg);
  end

  local muc_terms = get_muc_terms(room);
  if muc_terms then
    local from = room.jid .. '/' .. jid_escape(service_nickname);
    module:log("debug", "Sending muc terms to %s", occupant.jid);
    local msg = create_terms_message(room, "muc", muc_terms);
    msg.attr.to = occupant.jid;
    origin.send(msg);
  end
end
-- Note: we could do that on muc-occupant-joined or muc-occupant-session-new.
-- The first will not send it to multiple clients, the second will.
-- After some reflexion, i will try muc-occupant-session-new, and see if it works as expected.
module:hook("muc-occupant-session-new", send_terms);

-- reserve the service_nickname:
local function enforce_nick_policy(event)
  local origin, stanza = event.origin, event.stanza;
  local requested_nick = jid_resource(stanza.attr.to);
  local room = event.room;
	if not room then return; end

  if requested_nick == service_nickname then
    module:log("debug", "Occupant tried to use the %s reserved nickname, blocking it.", service_nickname);
    local reply = st.error_reply(stanza, "cancel", "conflict", nil, room.jid):up();
		origin.send(reply);
    return true;
  end
end
module:hook("muc-occupant-pre-join", enforce_nick_policy);
module:hook("muc-occupant-pre-change", enforce_nick_policy);

-- security check: we must remove all "x-livechat-terms" tag, to be sure nobody tries to spoof terms!
module:hook("muc-occupant-groupchat", function(event)
  event.stanza:maptags(function (child)
    if child.name == 'x-livechat-terms' then
      return nil;
    end
    return child;
  end);
end, 100);

-- don't save terms messages in history
module:hook("muc-message-is-historic", function(event)
  local stanza = event.stanza;
  if (stanza:get_child("x-livechat-terms")) then
    return false, "hint";
  end
end, 1);