Source: server/authorization/authorization.service.js

import Winston from '../../logging/app.logger';
import mongoose from 'mongoose';
import { User } from '../../data/schema/user';
import * as UserRegistration from '../../vivid/user-registrations/user-registrations.client';
import { prettyJson } from '../../logging/format';

/**
 * __Attempts to register a Zevere account with a Slack account.__
 * @param {string} zevereId The ID of a Zevere account.
 * @param {string} slackId The ID of a Slack account.
 * @returns {Promise<boolean>} A promise containing true if the user is registered successfully, or false if they were already registered before.
 */
export async function registerUser(zevereId: string, slackId: string): Promise<boolean> {
    try {
        const isRegistered = await userIsRegistered(slackId);
        if (isRegistered) {
            Winston.info(`Cannot register user. Account is already registered with Zevere. Zevere ID: '${zevereId}', Slack ID: '${slackId}'`);
            return false;
        }
        const registration = await UserRegistration.registerUser(zevereId, slackId);
        if (registration) {
            Winston.info('User was added to Vivid successfully. Attempting to save User in database.');
            await createAccount(zevereId, slackId);
            Winston.info(`Successfully registered User in both Vivid and Database. Zevere ID: '${zevereId}', Slack ID: '${slackId}'`);
        }
        return true;
    } catch (err) {
        throw err;
    }
}

/**
 * __Attempts to unregister a Zevere account from a Slack account.__
 * @param {string} slackId The ID of the user's Slack account.
 * @returns {Promise<boolean>} A promise containing true if the user was successfully unregistered, or false if they were already unregistered.
 */
export async function unregisterUser(slackId: string): Promise<boolean> {
    const isRegistered = await userIsRegistered(slackId);
    if (!isRegistered) {
        Winston.info(`Cannot unregister user. Account is not registered with Zevere. Slack ID: '${slackId}'`);
        return isRegistered;
    }

    const zevereId = await getUserBySlackId(slackId).zevereId;
    await UserRegistration.unregisterUserByUsername(zevereId);
    await removeAccount(slackId);
    return true;
}


/**
 * __Checks if user is registered on Vivid by scouring for a registration with Slack as well as checking the database.__
 *
 * @param {string} slackId The ID of a Slack account.
 * @returns {Promise<boolean>} A promise containing true if the user is registered with Slack, false if not.
 */
export async function userIsRegistered(slackId: string): Promise<boolean> {
    let zevereId; 
    try {
        const user = await getUserBySlackId(slackId);
        zevereId = user.zevereId;
    } catch (err) {
        Winston.error('Could not get user by Slack ID. Slack ID: ' + slackId);
        Winston.error(JSON.stringify(err));
    }
    
    if (!zevereId) {
        Winston.info(`User not found in database. Slack ID: '${slackId}'`);
        return false;
    }

    try {
        const acc = await UserRegistration.getUserRegistrationsByUsername(zevereId);
        Winston.info('Got registrations for: ' + acc.username);
        acc.registrations |> prettyJson |> Winston.info;

        for(const reg of acc.registrations) {
            if (reg.platform.toLowerCase() === 'slack') {
                return true;
            }
        }
    } catch (err) {
        err |> prettyJson |> Winston.error;
    }

    Winston.info(`No registrations found for '${slackId}' in Vivid.`);
    return false;
}


/**
 * __Attempts to save a link a Zevere account to a Slack account.__
 * @param {string} zevereId The ID of a Zevere account.
 * @param {string} slackId The ID of a Slack account.
 * @throws Error when there is an issue creating the document in the database.
 */
async function createAccount(zevereId: string, slackId: string): Promise<void> {
    const UserModel = mongoose.model('User');
    const user = new User();
    user.zevereId = zevereId;
    user.slackId = slackId;

    Winston.info('Linking accounts:');
    Winston.info(`\tZevere ID: ${zevereId}`);
    Winston.info(`\tSlack ID: ${slackId}`);

    await UserModel.create(user);
}

async function removeAccount(slackId: string): Promise<void> {
    const UserModel = mongoose.model('User');
    Winston.info('Removing account:');
    Winston.info(`\tSlack ID: ${slackId}`);

    await UserModel.deleteOne({ slackId });    
}

export async function getUserBySlackId(slackId: string): Promise<User> {
    const UserModel = mongoose.model('User');
    const userModel = await UserModel.findOne({ slackId });
    return userModel;
}