-- to be enabled under the main virtual host with all required settings
-- short_lived_token = {
--     issuer = 'myissuer';
--     accepted_audiences = { 'file-sharing' };
--     key_path = '/etc/prosody/short_lived_token.key';
--     key_id = 'my_kid';
--     ttl_seconds = 30;
-- };
-- The key in key_path can be generated via: openssl genrsa -out $PRIVATE_KEY_PATH 2048
-- And you can get the public key from it, which can be used ot verify those tokens via:
-- openssl rsa -in $PRIVATE_KEY_PATH -pubout -out $PUBLIC_KEY_PATH

local jid = require 'util.jid';
local st = require 'util.stanza';
local jwt = module:require 'luajwtjitsi';

local util = module:require 'util';
local is_vpaas = util.is_vpaas;
local process_host_module = util.process_host_module;
local table_find = util.table_find;
local create_throttle = require 'prosody.util.throttle'.create;

local SERVICE_TYPE = 'short-lived-token';
local options = module:get_option('short_lived_token');

if not (options.issuer and options.accepted_audiences
    and options.key_path and options.key_id and options.ttl_seconds) then
    module:log('error', 'Missing required options for short_lived_token');
    return;
end

local f = io.open(options.key_path, 'r');
if f then
    options.key = f:read('*all');
    f:close();
end

local accepted_requests = {};
for _, host in pairs(options.accepted_audiences) do
    accepted_requests[string.format('%s:%s:0', SERVICE_TYPE, host)] = host;
end

local server_region_name = module:get_option_string('region_name');

local main_muc_component_host = module:get_option_string('main_muc');
if main_muc_component_host == nil then
    module:log('error', 'main_muc not configured. Cannot proceed.');
    return;
end
local main_muc_service;

function generateToken(session, audience, room, occupant)
    local t = os.time();
    local exp = t + options.ttl_seconds;
    local presence = occupant:get_presence(session.full_jid);
    local _, _, id = extract_subdomain(jid.node(room.jid));
    local sub = module.host;

    if session.jitsi_web_query_prefix and session.jitsi_web_query_prefix ~= '' then
        sub = session.jitsi_web_query_prefix;
    end

    local payload = {
        iss = options.issuer,
        aud = audience,
        nbf = t,
        exp = exp,
        sub = sub,
        context = {
            group = session.jitsi_meet_context_group or session.granted_jitsi_meet_context_group_id,
            user = session.jitsi_meet_context_user or {
                id = session.full_jid,
                name = presence:get_child_text('nick', 'http://jabber.org/protocol/nick'),
                email = presence:get_child_text("email") or nil,
                nick = jid.resource(occupant.nick)
            },
            features = session.jitsi_meet_context_features
        },
        room = session.jitsi_web_query_room,
        meeting_id = room._data.meetingId,
        granted_from = session.granted_jitsi_meet_context_user_id,
        customer_id = id or session.jitsi_meet_context_group or session.granted_jitsi_meet_context_group_id,
        backend_region = server_region_name,
        user_region = session.user_region
    };

    local alg = 'RS256';
    local token, err = jwt.encode(payload, options.key, alg, { kid = options.key_id });
    if not err then
        return token
    else
        module:log('error', 'Error generating token: %s', err);
        return ''
    end
end

module:hook('external_service/credentials', function (event)
    local requested_credentials, services, session, stanza
        = event.requested_credentials, event.services, event.origin, event.stanza;
    local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);

    if not room then
        session.send(st.error_reply(stanza, 'cancel', 'not-allowed'));
        return;
    end

    local occupant = room:get_occupant_by_real_jid(session.full_jid);
    if not occupant then
        session.send(st.error_reply(stanza, 'cancel', 'not-allowed'));
        return;
    end

    for request in requested_credentials do
        local host = accepted_requests[request];
        if host then
            services:push({
                type = SERVICE_TYPE;
                host = host;
                username = 'token';
                password = generateToken(session, host, room, occupant);
                expires = os.time() + options.ttl_seconds;
                restricted = true;
                transport = 'https';
                port = 443;
            });
        end
    end

end);

process_host_module(main_muc_component_host, function(host_module, host)
    local muc_module = prosody.hosts[host].modules.muc;

    if muc_module then
        main_muc_service = muc_module;
    else
        prosody.hosts[host].events.add_handler('module-loaded', function(event)
            if (event.module == 'muc') then
                main_muc_service = prosody.hosts[host].modules.muc;
            end
        end);
    end
end);
