import { Option } from 'commander';
import { PrettyPrint } from '../output/pretty_print.js';

export class EndUser {
    constructor(program, logger, services) {
        this.logger = logger;
        this.services = services;
        services["enduser"] = this;

        this.logger.debug('EndUser.constructor');

        program
            .command('whoami')
            .description('Display information about current connected user')
            .action(async () => { 
                PrettyPrint.withSpinner(
                    async () => { return await this.whoami({}); },
                    (json) => { PrettyPrint.display(json); }
                );
            });

        program
            .command('roles')
            .description('Display roles of current user')
            .action(async () => { 
                PrettyPrint.withSpinner(
                    async () => { return await this.myRoles({}); },
                    (json) => { PrettyPrint.display(json); }
                );
            });

        program
            .command('mynetwork')
            .description('Display current user network')
            .addOption(new Option('--format <format>', 'output format: small, medium or full (default: medium)').default('medium'))
            .action(async (options) => { 
                PrettyPrint.withSpinner(
                    async () => { return await this.getMyNetwork(options); },
                    (json) => { PrettyPrint.display(json); }
                );
            });


        program
            .command('myuserid')
            .description('Display current user id')
            .action(async (options) => { 
                PrettyPrint.withSpinner(
                    async () => { return await this.myUserId(options); },
                    (json) => { PrettyPrint.display(json); }
                );
            });


        // Rooms commands
        // -------------- 

        program
            .command('rooms')
            .addOption(new Option('-p, --page <number>', 'display specific page'))
            .addOption(new Option('-l, --limit <number>', 'limit number of results per page (max: 1000)').conflicts('max'))
            .addOption(new Option('-m, --max', 'same as --limit 1000').conflicts('limit'))
            .addOption(new Option('--member', 'only rooms where the current user is a member'))
            .addOption(new Option('--name <name>', 'filter rooms whose name contains a word starting with the given value'))
            .addOption(new Option('--columns <columns...>', 'filter columns to be displayed'))
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .description('Display rooms visible to the current user')
            .action(async (options) => {
                if (options.max) options.limit = 1000;
                const defaultColumns = ['name', 'topic', 'visibility', 'creator', 'id'];
                PrettyPrint.withSpinner(
                    async () => { return await this.getRooms(options); },
                    (json) => { PrettyPrint.display(json, (options.columns ? options.columns : defaultColumns), options.json); }
                );
            });
    

        const roomCommand =  program
            .command('room')
            .argument('<id>', 'id of the room')
            .description('Get information about a specific room')
            .addOption(new Option('--byjid', 'specify that the id is a JID (default is room ID)'))
            .addOption(new Option('--format <format>', 'output format: small, medium or full (default: small)').default('small'))
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (id, options) => {
                PrettyPrint.withSpinner(
                    async () => { return await this.getRoom(id, options); },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });

        roomCommand
            .command('create <name>')
            .description('Create a new room')
            .addOption(new Option('--visibility <visibility>', 'room visibility: public or private (default is private)').default('private'))
            .addOption(new Option('--topic <topic>', 'room topic'))
            .addOption(new Option('--history <history>', 'room history: none, all, number (default is none)').default('none'))
            .addOption(new Option('--number <number>', 'number of messages to keep in history when history is set to number (default is 0)').default(0))
            .addOption(new Option('--disableNotifications', 'disable notifications for the room'))
            .addOption(new Option('--autoRegister <autoregister>', 'auto register users joining the room: unlock, lock (default is unlock)').default('unlock'))
            .addOption(new Option('--autoAcceptInvitation', 'auto accept invitations to join the room'))
            .addOption(new Option('--muteUponEntry', 'mute users upon entry to the room'))
            .addOption(new Option('--playEntryTone', 'play entry notification sound upon entry to the room'))
            .addOption(new Option('--autoClearIMs', 'automatically clear the room IMs when the conference ends'))
            .addOption(new Option('--autoClearPolls', 'automatically clear polls when the conference ends'))
            .addOption(new Option('--autoClearDocuments', 'automatically clear documents when the conference ends'))
            .addOption(new Option('--autoClearMembers', 'automatically remove members except moderator when the conference ends'))
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (name, options) => {
                PrettyPrint.withSpinner(
                    async () => { return await this.createRoom(name, options); },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });


        roomCommand
            .command('delete')
            .description('Delete the room')
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (id, options) => {
                PrettyPrint.withSpinner(
                    async () => { return await this.deleteRoom(id); },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });

        roomCommand
            .command('adduser <roomid> <userid>')
            .description('Add a user to a room')
            .addOption(new Option('--reason <reason>', 'reason for adding the user to the room').default('added by admin'))
            .action(async (roomid, userid, options) => {
                PrettyPrint.withSpinner(
                    async () => { return await this.addUserToRoom(roomid, userid, options); },
                    (json) => { PrettyPrint.display(json); }
                );
            });

        roomCommand
            .command('removeuser <roomid> <userid>')
            .description('Remove a user from a room')
            .action(async (roomid, userid, options) => {
                PrettyPrint.withSpinner(
                    async () => { return await this.removeUserFromRoom(roomid, userid); },
                    (json) => { PrettyPrint.display(json); }
                );
            });


    }

    async myUserId(options) {
        this.logger.debug('EndUser.myUserId');

        try {
            const me = await this.whoami();
            return me.data.id;
        }
        catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    async whoami() {
        this.logger.debug('EndUser.whoami');

        try {
            const json = await this.services['rest'].restCall('GET', '/api/rainbow/enduser/v1.0/users/me');
            return json;
        }
        catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    async myRoles() {
        this.logger.debug('EndUser.myRoles');

        try {
            const jsonResult = await this.whoami();
            return jsonResult.data.roles;
        }
        catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    async getMyNetwork(options) {
        this.logger.debug('EndUser.getMyNetwork');

        const query_parameters = [["format", options.format]];

        const json = await this.services["rest"].restCall('GET', '/api/rainbow/enduser/v1.0/users/networks', query_parameters);
        return json;
    }

    /*
    async getUserData(userid, options) {
        this.logger.debug('EndUser.getUserData');

        const query_parameters = [];

        await this.services["rest"].restCall('GET', `/api/rainbow/enduser/v1.0/users/${userid}`, query_parameters, {}, {},
            (json) => {
                if (!options.nooutput) console.log(json.data);
                return json;
            }
        );
    }
    */

    // Rooms management
    // ----------------

    async getRooms(options) {
        this.logger.debug('EndUser.getRooms');

        const limit = options.limit ? Math.min(Number(options.limit), 1000) : 25;
        const query_parameters = [
            ['limit', limit]
        ];

        if (options.page) {
            query_parameters.push(['offset', (options.page > 1 ? limit * (options.page - 1) : 0)]);
        }

        if (options.name) {
            query_parameters.push(['name', options.name]);
        }

        let endpointSuffix = '';
        if (options.member) {
            endpointSuffix = '/jids';
        }

        const json = await this.services["rest"].restCall('GET', `/api/rainbow/enduser/v1.0/rooms${endpointSuffix}`, query_parameters);
        return json;
    }

    async getRoom(id, options) {
        this.logger.debug('EndUser.getRoom');

        const query_parameters = [
            ["format", options.format]
        ];

        let endpointSuffix = '';
        if (options.byjid) {
            endpointSuffix = '/jids';
        }

        const json = await this.services["rest"].restCall('GET', `/api/rainbow/enduser/v1.0/rooms${endpointSuffix}/${id}`, query_parameters);
        return json;
    }
    
    async createRoom(name, options) {
        this.logger.debug('EndUser.createRoom');

        const body = {
            name: name,
            visibility: options.visibility,
            topic: options.topic,
            history: options.history,
            number: parseInt(options.number),
            disableNotifications: options.disableNotifications ? true : false,
            autoRegister: options.autoRegister,
            autoAcceptInvitation: options.autoAcceptInvitation ? true : false,
            muteUponEntry: options.muteUponEntry ? true : false,
            playEntryTone: options.playEntryTone ? true : false,
            contentToAutoClear: {
                im: options.autoClearIMs,
                poll: options.autoClearPolls,
                document: options.autoClearDocuments,
                member: options.autoClearMembers
            }
        };

        const json = await this.services["rest"].restCall('POST', '/api/rainbow/enduser/v1.0/rooms', [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }

    async deleteRoom(id) {
        this.logger.debug('EndUser.deleteRoom');

        const json = await this.services["rest"].restCall('DELETE', `/api/rainbow/enduser/v1.0/rooms/${id}`);
        return json;
    }


    async addUserToRoom(roomid, userid, options) {
        this.logger.debug('EndUser.addUserToRoom');

        const body = {
            userId: userid,
            reason: options.reason
        }

        const json = await this.services["rest"].restCall('POST', `/api/rainbow/enduser/v1.0/rooms/${roomid}/users`, [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }

    async removeUserFromRoom(roomid, userid) {
        this.logger.debug('EndUser.removeUserFromRoom');

        const json = await this.services["rest"].restCall('DELETE', `/api/rainbow/enduser/v1.0/rooms/${roomid}/users/${userid}`);
        return json;
    }
}