import { Option } from 'commander';
import { PrettyPrint } from '../output/pretty_print.js';
import { createInterface } from 'node:readline/promises';

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

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

        // === USERS ===
        // users - Get all users
        // GET https://openrainbow.com/api/rainbow/admin/v1.0/users
        program
            .command('users')
            .description('Get users')
            .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('-t, --terminated', 'filter on terminated users'))
            .addOption(new Option('-i, --cid <id>', 'filter users on company id').conflicts('company'))
            .addOption(new Option('-c, --company <name>', 'filter on company name').conflicts('cid'))
            .addOption(new Option('-r, --role <role>', 'filter on role'))
            .addOption(new Option('-e, --email <loginemail>', 'filter on login email'))
            .addOption(new Option('-n, --name <name>', 'filter by user name'))
            .addOption(new Option('--properties <properties...>', 'filter by any supported properties: displayName:Doe isTerminated:true'))
            .addOption(new Option('--columns <columns...>', 'filter columns to be displayed'))
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (options) => {
                this.services['telemetry'].sendTelemetry('users', '', '');
                if (options.interactive) {
                    options.limit = (options.limit ? options.limit : 100);
                    const json = await this.getUsers(options);
                    this.services['interactive_display'].displayObjectNavigator(json, "users", options);
                }
                else {
                    const defaultColumns = ['displayName', 'loginEmail', 'companyName', 'accountType', 'roles', 'isActive', 'id'];
                    PrettyPrint.withSpinner(
                        async () => { return await this.getUsers(options) },
                        (json) => { PrettyPrint.display(json, (options.columns ? options.columns : defaultColumns), options.json); }
                    );
                }
            });

        // users - Get a user data
        // GET /api/rainbow/admin/v1.0/users/:userId
        const userCommand = program
            .command('user')
            .description('Manage users');

        userCommand
            .command('get')
            .argument('<id>', 'id of user to get')
            .description('Get user with id')
            .addOption(new Option('--format <format>', 'output format: small, medium or full (default: full)').default('full'))
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (id, options) => {
                this.services['telemetry'].sendTelemetry('user', 'get', '');
                if (options.interactive) {
                    const json = await this.getUser(id, options);
                    this.services['interactive_display'].displayObjectNavigator(json);
                }
                else {
                    PrettyPrint.withSpinner(
                        async () => { return await this.getUser(id, options) },
                        (json) => { PrettyPrint.display(json, undefined, options.json); }
                    );
                }
            });

        // users - create a user
        // 
        userCommand
            .command('create')
            .argument('<email>', 'login email of user to create')
            .argument('<password>', 'password of user to create')
            .argument('<firstname>', 'first name of user to create')
            .argument('<lastname>', 'last name of user to create')
            .description('Create a new user')
            .addOption(new Option('-c, --company <id>', 'create user in company id'))
            .addOption(new Option('-a, --admin', 'create user with company admin role').conflicts(['org', 'guest']))
            .addOption(new Option('-o, --org <orgid>', 'create user with org admin role for organisation orgid').conflicts(['admin', 'guest']))
            .addOption(new Option('-g, --guest', 'create a user with guest role').conflicts(['admin', 'org'])) // TODO REMOVE ???
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .addOption(new Option('--public', 'create a public user'))
            .action(async (email, password, firstname, lastname, options) => {
                this.services['telemetry'].sendTelemetry('user', 'create', '');
                PrettyPrint.withSpinner(
                    async () => { return await this.createUser(email, password, firstname, lastname, options) },
                    (json) => { PrettyPrint.display(json, (options.columns ? options.columns : undefined)) }
                );
            });

        // users -- delete a user
        userCommand
            .command('delete')
            .argument('<id>', 'id of user to delete')
            .description('Delete existing user')
            .addOption(new Option('--force', 'skip confirmation prompt'))
            .addOption(new Option('-p, --pending', 'allows to pending the user account for 10 days before terminate it or restore it'))
            .action(async (id, options) => {
                this.services['telemetry'].sendTelemetry('user', 'delete', '');
                if (!options.force) {
                    const rl = createInterface({
                        input: process.stdin,
                        output: process.stdout
                    });
                    const answer = await rl.question('Are you sure (Y/N) ?');
                    const answerlow = answer.toLowerCase();
                    if (answerlow !== 'y' && answerlow !== 'yes') {
                        PrettyPrint.error('Action cancelled');
                        rl.close();
                        return;
                    }
                    rl.close();
                }
                PrettyPrint.withSpinner(
                    async () => { return await this.deleteUser(id, options) },
                    (json) => { PrettyPrint.display(json) }
                );
            });

        // users - restore a user
        userCommand
            .command('restore')
            .argument('<id>', 'id of user to restore')
            .description('Restore a user whose account is in pending termination state')
            .action(async (id, options) => {
                this.services['telemetry'].sendTelemetry('user', 'restore', '');
                PrettyPrint.withSpinner(
                    async () => { return await this.restoreUser(id, options) },
                    (json) => { PrettyPrint.display(json) }
                );
            });

        // users -- update user
        userCommand
            .command('update')
            .argument('<id>', 'id of user to update')
            .description('Update existing user')
            .addOption(new Option('--properties <properties...>', 'specify properties to update: lastName:Doe title:Mr'))
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (id, options) => {
                this.services['telemetry'].sendTelemetry('user', 'update', '');
                const body = {};
                if (options.properties) {
                    options.properties.forEach(pair => {
                        const [key, value] = pair.split(':');
                        body[key] = value;
                    });
                }
                PrettyPrint.withSpinner(
                    async () => { return await this.services["rest"].restCall('PUT', `/api/rainbow/admin/v1.0/users/${id}`, [], body, { 'Accept': 'application/json', 'Content-Type': 'application/json' }) },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );  
            });

        // === COMPANIES ===
        // companies - get all companiesq
        program
            .command('companies')
            .description('Get companies')
            .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('-n, --name <name>', 'filter by company name'))
            .addOption(new Option('-o, --org <orgid>', 'filter by organisation'))
            .addOption(new Option('--properties <properties...>', 'filter by any supported properties: displayName:Doe isTerminated:true'))
            .addOption(new Option('--columns <columns...>', 'filter columns to be displayed'))
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (options) => {
                this.services['telemetry'].sendTelemetry('companies', '', '');
                if (options.interactive) {
                    options.limit = (options.limit ? options.limit : 100);
                    const json = await this.getCompanies(options);
                    this.services['interactive_display'].displayObjectNavigator(json, "companies", options);
                }
                else {
                    const defaultColumns = ['name', 'offerType', 'visibility', 'status', 'organizationId', 'id'];
                    PrettyPrint.withSpinner(
                        async () => { return await this.getCompanies(options) },
                        (json) => { PrettyPrint.display(json, (options.columns ? options.columns : defaultColumns), options.json); }
                    );
                }
            });

        // companies - get company
        const companyCommand = program
            .command('company')
            .description('Manage companies');

        companyCommand
            .command('get')
            .argument('<id>', 'id of company to get')
            .description('Get company with id')
            .addOption(new Option('--format <format>', 'output format: small, medium or full (default: full)').default('full'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (id, options) => {
                this.services['telemetry'].sendTelemetry('company', 'get', '');
                if (options.interactive) {
                    const json = await this.getCompany(id, options);
                    this.services['interactive_display'].displayObjectNavigator(json);
                }
                else {
                    PrettyPrint.withSpinner(
                        async () => { return await this.getCompany(id, options) },
                        (json) => { PrettyPrint.display(json, undefined, options.json); }
                    );
                }
            });

        // companies - create a company
        companyCommand
            .command('create')
            .argument('<name>', 'name of the company')
            .description('create a new company')
            .addOption(new Option('--properties <properties...>', 'specify creation properties'))
            .action(async (name, options) => {
                this.services['telemetry'].sendTelemetry('company', 'create', '');
                PrettyPrint.withSpinner(
                    async () => { return await this.createCompany(name, options); },
                    (json) => { PrettyPrint.display(json); }
                );
            });

        // companies - update a company
        companyCommand
            .command('update')
            .argument('<id>', 'id of the company to update')
            .description('Update an existing company')
            .addOption(new Option('--name <name>', 'company name'))
            .addOption(new Option('--description <text>', 'company description'))
            .addOption(new Option('--country <code>', 'country (ISO 3166-1 alpha3, e.g. FRA)'))
            .addOption(new Option('--city <city>', 'city'))
            .addOption(new Option('--street <street>', 'street address'))
            .addOption(new Option('--postal-code <code>', 'postal code'))
            .addOption(new Option('--state <state>', 'state (for USA or CAN)'))
            .addOption(new Option('--website <url>', 'company website'))
            .addOption(new Option('--visibility <visibility>', 'visibility: public, private, organisation, closed'))
            .addOption(new Option('--status <status>', 'status: active, hold, terminated'))
            .addOption(new Option('--admin-email <email>', 'company admin contact email'))
            .addOption(new Option('--support-email <email>', 'company support email'))
            .addOption(new Option('--size <size>', 'company size (e.g. 1-50, 51-200, ...)'))
            .addOption(new Option('--timezone <tz>', 'timezone name'))
            .addOption(new Option('--properties <properties...>', 'additional properties as key:value pairs'))
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (id, options) => {
                this.services['telemetry'].sendTelemetry('company', 'update', '');
                PrettyPrint.withSpinner(
                    async () => { return await this.updateCompany(id, options); },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });

        // companies - delete a company
        companyCommand
            .command('delete')
            .argument('<id>', 'id of the company to delete')
            .description('Delete a company')
            .addOption(new Option('--force', 'skip confirmation prompt'))
            .action(async (id, options) => {
                this.services['telemetry'].sendTelemetry('company', 'delete', '');
                if (!options.force) {
                    const rl = createInterface({ input: process.stdin, output: process.stdout });
                    const answer = await rl.question('Are you sure you want to delete this company? (Y/N) ');
                    rl.close();
                    if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
                        PrettyPrint.error('Action cancelled');
                        return;
                    }
                }
                PrettyPrint.withSpinner(
                    async () => { return await this.deleteCompany(id); },
                    (json) => { PrettyPrint.display(json); }
                );
            });

        // webhook configuration
        // Convention : [companyid] est optionnel partout.
        // Pour les commandes a 2 args (companyid + webhookid/subscriptionid) :
        //   - 2 args fournis → comportement classique
        //   - 1 arg fourni   → cet arg est le webhookid/subscriptionid, companyId = whoami

        const webhookCommand = companyCommand
            .command('webhook')
            .argument('[companyid]', 'id of the company (default: current user\'s company)')
            .argument('[webhookid]', 'id of the webhook')
            .description('display a webhook of a company')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (a, b, options) => {
                const companyId = b !== undefined ? a : await this._resolveCompanyId(null);
                const webhookId = b !== undefined ? b : a;
                if (!webhookId) { console.error('webhook id is required'); return; }
                PrettyPrint.withSpinner(
                    async () => { return await this.getCompanyWebhook(companyId, webhookId) },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });

        companyCommand
            .command('webhooks')
            .argument('[companyid]', 'id of the company (default: current user\'s company)')
            .description('display all webhooks of a company')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (companyid, options) => {
                const companyId = await this._resolveCompanyId(companyid);
                PrettyPrint.withSpinner(
                    async () => { return await this.getCompanyWebhooks(companyId) },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            })

        webhookCommand
            .command('create')
            .argument('[companyid]', 'id of the company (default: current user\'s company)')
            .argument('[url]', 'url of the web hook')
            .argument('[token]', 'used to validate the web hook')
            .description('create company webhook')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (a, b, c, options) => {
                let companyId, url, token;
                if (c !== undefined) {
                    // 3 args : companyid url token
                    companyId = await this._resolveCompanyId(a); url = b; token = c;
                } else if (b !== undefined) {
                    // 2 args : url token (companyId depuis whoami)
                    companyId = await this._resolveCompanyId(null); url = a; token = b;
                } else {
                    console.error('Usage: company webhook create [companyid] <url> <token>'); return;
                }
                PrettyPrint.withSpinner(
                    async () => { return await this.createCompanyWebhook(companyId, url, token); },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });

        webhookCommand
            .command('delete')
            .argument('[companyid]', 'id of the company (default: current user\'s company)')
            .argument('[webhookid]', 'id of the webhook')
            .description('delete a company webhook')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (a, b, options) => {
                const companyId = b !== undefined ? a : await this._resolveCompanyId(null);
                const webhookId = b !== undefined ? b : a;
                if (!webhookId) { console.error('webhook id is required'); return; }
                PrettyPrint.withSpinner(
                    async () => { return await this.deleteCompanyWebhook(companyId, webhookId); },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });

        webhookCommand
            .command('update')
            .argument('[companyid]', 'id of the company (default: current user\'s company)')
            .argument('[webhookid]', 'id of the webhook to be updated')
            .description('update company webhook')
            .addOption(new Option('--url <url>', 'url of the web hook'))
            .addOption(new Option('--token <token>', 'token of the web hook'))
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (a, b, options) => {
                const companyId = b !== undefined ? a : await this._resolveCompanyId(null);
                const webhookId = b !== undefined ? b : a;
                if (!webhookId) { console.error('webhook id is required'); return; }
                PrettyPrint.withSpinner(
                    async () => { return await this.updateCompanyWebhook(companyId, webhookId, options); },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });

        // TODO : users web hook

        // web hook subscription
        const subscriptionCommand = webhookCommand
            .command('subscription')
            .argument('[companyid]', 'id of the company (default: current user\'s company)')
            .argument('[subscriptionid]', 'id of the webhook subscription')
            .description('display a webhook subscription of a company')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (a, b, options) => {
                const companyId = b !== undefined ? a : await this._resolveCompanyId(null);
                const subscriptionId = b !== undefined ? b : a;
                if (!subscriptionId) { console.error('subscription id is required'); return; }
                PrettyPrint.withSpinner(
                    async () => { return await this.getCompanyWebhookSubscription(companyId, subscriptionId) },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });

        webhookCommand
            .command('subscriptions')
            .argument('[companyid]', 'id of the company (default: current user\'s company)')
            .description('display all webhook subscriptions of a company')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (companyid, options) => {
                const companyId = await this._resolveCompanyId(companyid);
                PrettyPrint.withSpinner(
                    async () => { return await this.getCompanyWebhookSubscriptions(companyId) },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            })

        subscriptionCommand
            .command('create')
            .argument('[companyid]', 'id of the company (default: current user\'s company)')
            .argument('[webhookid]', 'id of the company webhook')
            .argument('[eventtype]', 'type of subscribed events (presence, device_state, calllog, authentication, security)')
            .argument('[domain]', 'domain of subscribed events (company)')
            .argument('[enabled]', 'is the subscription enabled')
            .description('create a company webhook subscription')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (a, b, c, d, e, options) => {
                let companyId, webhookId, eventType, domain, enabled;
                if (e !== undefined) {
                    // 5 args : companyid webhookid eventtype domain enabled
                    companyId = await this._resolveCompanyId(a); webhookId = b; eventType = c; domain = d; enabled = e;
                } else if (d !== undefined) {
                    // 4 args : webhookid eventtype domain enabled (companyId depuis whoami)
                    companyId = await this._resolveCompanyId(null); webhookId = a; eventType = b; domain = c; enabled = d;
                } else {
                    console.error('Usage: company webhook subscription create [companyid] <webhookid> <eventtype> <domain> <enabled>'); return;
                }
                PrettyPrint.withSpinner(
                    async () => { return await this.createCompanyWebhookSubscription(companyId, webhookId, eventType, domain, enabled); },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });

        subscriptionCommand
            .command('delete')
            .argument('[companyid]', 'id of the company (default: current user\'s company)')
            .argument('[subscriptionid]', 'id of the subscription')
            .description('delete a company webhook subscription')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .action(async (a, b, options) => {
                const companyId = b !== undefined ? a : await this._resolveCompanyId(null);
                const subscriptionId = b !== undefined ? b : a;
                if (!subscriptionId) { console.error('subscription id is required'); return; }
                PrettyPrint.withSpinner(
                    async () => { return await this.deleteCompanyWebhookSubscription(companyId, subscriptionId); },
                    (json) => { PrettyPrint.display(json, undefined, options.json); }
                );
            });


    }

    // -----------------------------------------------------------------------------------------------------------

    /**
     * Resoudre le companyId : utilise l'argument fourni ou celui de l'utilisateur connecte.
     */
    async _resolveCompanyId(companyIdArg) {
        this.logger.debug('Admin._resolveCompanyId');
        if (companyIdArg) return companyIdArg;
        const me = await this.services['enduser'].whoami();
        return me.data.companyId;
    }

    async getUsers(options) {
        this.logger.debug('Admin.getUsers');

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

        if (!options.format) {
            query_parameters.push(['format', 'full']);
        }

        if (options.properties) {
            options.properties.forEach(pair => {
                const [key, value] = pair.split(":");
                // TODO: check errors, format
                query_parameters.push([key, value]);
            });
        }

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

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

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

        if (options.role) {
            query_parameters.push(['roles', options.role])
        }

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

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

        if (options.terminated) {
            query_parameters.push(['isTerminated', 'true']);
        }

        // options.max

        this.logger.debug(query_parameters);

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

    async getUser(id, options) {
        this.logger.debug('Admin.getUser');

        const query_parameters = [];
        if (options.format) {
            query_parameters.push(['format', options.format]);
        }

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

    async createUser(email, password, firstname, lastname, options) {
        this.logger.debug('Admin.createUser');

        const query_parameters = [];
        const body = {
            'loginEmail': email,
            'password': password,
            'firstName': firstname,
            'lastName': lastname
        };

        const json = await this.services["rest"].restCall('POST', '/api/rainbow/admin/v1.0/users', query_parameters, body, {});
        return json;
    }

    async deleteUser(id, options) {
        this.logger.debug('Admin.deleteUser');

        const query_parameters = [];
        if (options.pending) {
            query_parameters.push(['pendingTerminated', true]);
        }

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

    async restoreUser(id, options) {
        this.logger.debug('Admin.restoreUser');

        const json = await this.services["rest"].restCall('PUT', `/api/rainbow/admin/v1.0/users/${id}/delete-cancelled`,
            [], {},
            { 'Accept': 'application/json' });
        return json;
    }

    async getCompanies(options) {
        this.logger.debug('Admin.getCompanies');

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

        if (!options.format) {
            query_parameters.push(['format', 'full']);
        }

        if (options.properties) {
            options.properties.forEach(pair => {
                const [key, value] = pair.split(":");
                // TODO: check errors, format
                query_parameters.push([key, value]);
            });
        }

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

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


        // options.max

        this.logger.debug(query_parameters);

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

    async getCompany(id, options) {
        this.logger.debug('Admin.getCompany');

        const query_parameters = [];
        if (options.format) {
            query_parameters.push(['format', options.format]);
        }

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

    async createCompany(name, options) {
        this.logger.debug('Admin.createCompany');

        const body = {
            'name': name
        };
        if (options.properties) {
            options.properties.forEach(pair => {
                const [key, value] = pair.split(':');
                body[key] = value;
            });
        }

        // WHEN COMPANY IS CREATED API RETURNS 201, NOT 200

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


    async updateCompany(id, options) {
        this.logger.debug('Admin.updateCompany');

        const body = {};
        if (options.name)          body['name']          = options.name;
        if (options.description)   body['description']   = options.description;
        if (options.country)       body['country']       = options.country;
        if (options.city)          body['city']          = options.city;
        if (options.street)        body['street']        = options.street;
        if (options.postalCode)    body['postalCode']    = options.postalCode;
        if (options.state)         body['state']         = options.state;
        if (options.website)       body['website']       = options.website;
        if (options.visibility)    body['visibility']    = options.visibility;
        if (options.status)        body['status']        = options.status;
        if (options.adminEmail)    body['adminEmail']    = options.adminEmail;
        if (options.supportEmail)  body['supportEmail']  = options.supportEmail;
        if (options.size)          body['size']          = options.size;
        if (options.timezone)      body['timezone']      = options.timezone;
        if (options.properties) {
            options.properties.forEach(pair => {
                const [key, value] = pair.split(':');
                body[key] = value;
            });
        }

        const json = await this.services["rest"].restCall('PUT', `/api/rainbow/admin/v1.0/companies/${id}`, [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' });
        return json;
    }

    async deleteCompany(id) {
        this.logger.debug('Admin.deleteCompany');
        const json = await this.services["rest"].restCall('DELETE', `/api/rainbow/admin/v1.0/companies/${id}`);
        return json;
    }

    // webhooks
    async createCompanyWebhook(id, url, token) {
        this.logger.debug('Admin.createCompanyWebhook');

        const body = {
            'url': url,
            'token': token,
            'enabled': true // TODO : make it configurable
        };

        const json = await this.services["rest"].restCall('POST', `/api/rainbow/admin/v1.0/companies/${id}/event-providers/webhooks`,
            [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }

    async updateCompanyWebhook(companyId, webhookId, options) {
        this.logger.debug('Admin.updateCompanyWebhook');

        const body = {};
        if (options.url) {
            body['url'] = options.url;
        }
        if (options.token) {
            body['token'] = options.token;
        }

        const json = await this.services["rest"].restCall('PUT', `/api/rainbow/admin/v1.0/companies/${companyId}/event-providers/webhooks/${webhookId}`,
            [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }

    async getCompanyWebhook(companyId, webhookId) {
        this.logger.debug('Admin.getCompanyWebhook');

        const body = {};

        const json = await this.services["rest"].restCall('GET', `/api/rainbow/admin/v1.0/companies/${companyId}/event-providers/webhooks/${webhookId}`,
            [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }

        );
        return json;
    }

    async getCompanyWebhooks(companyId) {
        this.logger.debug('Admin.getCompanyWebhooks');

        const json = await this.services["rest"].restCall('GET', `/api/rainbow/admin/v1.0/companies/${companyId}/event-providers/webhooks`,
            [], {}, 
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }

    async deleteCompanyWebhook(companyId, webhookId) {
        this.logger.debug('Admin.deleteCompanyWebhook');

        const body = {};

        const json = await this.services["rest"].restCall('DELETE', `/api/rainbow/admin/v1.0/companies/${companyId}/event-providers/webhooks/${webhookId}`,
            [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }

    async getCompanyWebhookSubscription(companyId, subscriptionId) {
        this.logger.debug('Admin.getCompanyWebhookSubscription');

        const body = {};

        const json = await this.services["rest"].restCall('GET', `/api/rainbow/admin/v1.0/companies/${companyId}/event-providers/subscriptions/${subscriptionId}`,
            [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }

    async getCompanyWebhookSubscriptions(companyId) {
        this.logger.debug('Admin.getCompanyWebhookSubscriptions');

        const json = await this.services["rest"].restCall('GET', `/api/rainbow/admin/v1.0/companies/${companyId}/event-providers/subscriptions`,
            [], {}, 
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }

    async createCompanyWebhookSubscription(companyId, webhookId, eventType, domain, enabled) {
        this.logger.debug('Admin.createCompanyWebhookSubscription');

        const body = {
            'webHookId': webhookId,
            'eventType': eventType,
            'domain': domain,
            'enabled': enabled
        };

        const json = await this.services["rest"].restCall('POST', `/api/rainbow/admin/v1.0/companies/${companyId}/event-providers/subscriptions`,
            [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }


    async deleteCompanyWebhookSubscription(companyId, subscriptionId) {
        this.logger.debug('Admin.deleteCompanyWebhookSubscription');

        const body = {};

        const json = await this.services["rest"].restCall('DELETE', `/api/rainbow/admin/v1.0/companies/${companyId}/event-providers/subscriptions/${subscriptionId}`,
            [], body,
            { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        );
        return json;
    }

    async getSubscriptionSnapshot(subscriptionId) {
        this.logger.debug('Admin.getSubscriptionSnapshot');
        const json = await this.services["rest"].restCall(
            'GET', `/api/rainbow/event-dispatcher/v1.0/subscription/${subscriptionId}/snapshot`
        );
        return json;
    }

    async getSubscriptionStatus(subscriptionId) {
        this.logger.debug('Admin.getSubscriptionStatus');
        const json = await this.services["rest"].restCall(
            'GET', `/api/rainbow/event-dispatcher/v1.0/subscription/${subscriptionId}/status`
        );
        return json;
    }

}
