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

export class Subscription {
    constructor(program, logger, services) {
        this.logger = logger;
        this.services = services;
        services['subscription'] = this;

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

        const subscriptionCommand = program
            .command('subscription')
            .description('Manage subscriptions and billing');

        subscriptionCommand
            .command('payment-methods')
            .argument('[userid]', 'developer user id (default: current user)')
            .description('List payment methods configured for a developer')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (userid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'payment-methods', '');
                try {
                    const resolvedId = userid || await this.services['enduser'].myUserId({});
                    const result = await this.getPaymentMethods(resolvedId);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result);
                    } else {
                        PrettyPrint.display(result, undefined, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        const offerCommand = subscriptionCommand
            .command('offer')
            .description('Manage a specific offer');

        offerCommand
            .command('get')
            .argument('<query>', 'offer id (24-char hex) or name')
            .option('-c, --company <id>', 'company id (default: current user\'s company)')
            .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'))
            .description('Display details of an offer by id or name')
            .action(async (query, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'offer-get', '');
                try {
                    const companyId = options.company || await this._currentCompanyId();
                    const offer = await this.resolveOffer(query, companyId, options.format);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(offer);
                    } else {
                        PrettyPrint.display(offer, undefined, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        program
            .command('subscriptions')
            .argument('[companyid]', 'company id (default: current user\'s company)')
            .description('List subscriptions of a company')
            .addOption(new Option('--format <format>', 'output format: small, medium or full (default: full)').default('full'))
            .addOption(new Option('--terminated', 'include terminated subscriptions'))
            .addOption(new Option('--guardian', 'include guardian employee active subscriptions'))
            .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 (companyid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'subscriptions', '');
                try {
                    const resolvedId = companyid || await this._currentCompanyId();
                    const result = await this.getSubscriptions(resolvedId, options);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result, 'subscriptions', options);
                    } else {
                        PrettyPrint.display(result, options.columns, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('catalogs')
            .description('List products from Zuora catalog')
            .addOption(new Option('--format <format>', 'output format: small, medium or full (default: full)').default('full'))
            .option('--service-type <type>', 'filter by service type (default: RB)')
            .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('subscription', 'catalogs', '');
                try {
                    const result = await this.getCatalogs(options);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result, 'catalogs', options);
                    } else {
                        PrettyPrint.display(result, options.columns, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('create')
            .argument('<offerid>', 'id of the offer to subscribe to')
            .option('-c, --company <id>', 'company id (default: current user\'s company)')
            .description('Subscribe a company to an offer')
            .option('--max-users <n>', 'number of user licences to buy')
            .option('--application-id <id>', 'CPaaS application to link to the subscription')
            .option('--promo-code <code>', 'promo code to apply')
            .option('--purchase-order <num>', 'purchase order number')
            .option('--expiration-date <date>', 'subscription expiration date (ISO 8601)')
            .option('--monthly-duration <n>', 'number of months for the monthly subscription')
            .option('--prepaid-duration <n>', 'prepaid duration')
            .option('--payment-period <period>', 'billing period for prepaid subscription')
            .option('--renewal-term <n>', 'renewal term duration')
            .addOption(new Option('--auto-renew', 'enable auto-renewal (prepaid offers)'))
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (offerid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'create', '');
                try {
                    const companyId = options.company || await this._currentCompanyId();
                    const result = await this.createSubscription(companyId, offerid, options);
                    PrettyPrint.display(result, undefined, options.json);
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('update')
            .argument('<subscriptionid>', 'subscription id')
            .option('-c, --company <id>', 'company id (default: current user\'s company)')
            .description('Update a company subscription')
            .option('--max-users <n>', 'number of user licences')
            .option('--application-id <id>', 'CPaaS application to link to the subscription')
            .option('--promo-code <code>', 'promo code to apply')
            .option('--purchase-order <num>', 'purchase order number')
            .option('--expiration-date <date>', 'subscription expiration date (ISO 8601)')
            .option('--monthly-duration <n>', 'number of months for the monthly subscription')
            .option('--prepaid-duration <n>', 'prepaid duration')
            .option('--renewal-term <n>', 'renewal term duration')
            .option('--renewal-volume <n>', 'number of licences for renewal with volume option')
            .option('--prepaid-renewal-option <opt>', 'prepaid renewal option (billed or volume)')
            .addOption(new Option('--auto-renew', 'enable auto-renewal (prepaid offers)'))
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (subscriptionid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'update', '');
                try {
                    const companyId = options.company || await this._currentCompanyId();
                    const result = await this.updateSubscription(companyId, subscriptionid, options);
                    PrettyPrint.display(result, undefined, options.json);
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('delete')
            .argument('<subscriptionid>', 'subscription id')
            .option('-c, --company <id>', 'company id (default: current user\'s company)')
            .description('Delete a company subscription')
            .addOption(new Option('--force', 'skip confirmation prompt'))
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (subscriptionid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'delete', '');
                try {
                    const companyId = options.company || await this._currentCompanyId();
                    if (!options.force) {
                        const { default: confirm } = await import('@inquirer/confirm');
                        const ok = await confirm({ message: `Delete subscription ${subscriptionid}?`, default: false });
                        if (!ok) return;
                    }
                    const result = await this.deleteSubscription(companyId, subscriptionid);
                    PrettyPrint.display(result, undefined, options.json);
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('get')
            .argument('<subscriptionid>', 'subscription id')
            .option('-c, --company <id>', 'company id (default: current user\'s company)')
            .description('Display details of a company subscription')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (subscriptionid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'get', '');
                try {
                    const companyId = options.company || await this._currentCompanyId();
                    const result = await this.getSubscription(companyId, subscriptionid);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result);
                    } else {
                        PrettyPrint.display(result, undefined, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('users')
            .argument('<subscriptionid>', 'subscription id')
            .option('-c, --company <id>', 'company id (default: current user\'s company)')
            .description('List users assigned to a subscription')
            .addOption(new Option('--format <format>', 'output format: small, medium or full (default: full)').default('full'))
            .addOption(new Option('-l, --limit <n>', 'max number of results'))
            .addOption(new Option('-o, --offset <n>', 'position of first result (pagination)'))
            .addOption(new Option('--sort-field <field>', 'field to sort by'))
            .addOption(new Option('--sort-order <order>', 'sort order: 1 (asc) or -1 (desc)'))
            .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 (subscriptionid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'users', '');
                try {
                    const companyId = options.company || await this._currentCompanyId();
                    const result = await this.getSubscriptionUsers(companyId, subscriptionid, options);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result, 'users', options);
                    } else {
                        PrettyPrint.display(result, options.columns, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('features')
            .argument('[companyid]', 'company id (default: current user\'s company)')
            .description('Get subscription features of a company')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (companyid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'features', '');
                try {
                    const companyId = companyid || await this._currentCompanyId();
                    const result = await this.getSubscriptionFeatures(companyId);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result);
                    } else {
                        PrettyPrint.display(result, undefined, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('counters')
            .argument('[companyid]', 'company id (default: current user\'s company)')
            .description('Get subscription counters of a company')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (companyid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'counters', '');
                try {
                    const companyId = companyid || await this._currentCompanyId();
                    const result = await this.getSubscriptionCounters(companyId);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result);
                    } else {
                        PrettyPrint.display(result, undefined, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('default')
            .argument('[companyid]', 'company id (default: current user\'s company)')
            .description('Get default subscription of a company')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (companyid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'default', '');
                try {
                    const companyId = companyid || await this._currentCompanyId();
                    const result = await this.getDefaultSubscription(companyId);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result);
                    } else {
                        PrettyPrint.display(result, undefined, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('prepaid')
            .argument('[companyid]', 'company id (default: current user\'s company)')
            .description('List prepaid subscriptions expiring soon')
            .option('--days <n>', 'subscriptions expiring within the next n days')
            .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 (companyid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'prepaid', '');
                try {
                    const companyId = companyid || await this._currentCompanyId();
                    const result = await this.getPrepaidSubscriptions(companyId, options);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result, 'subscriptions', options);
                    } else {
                        PrettyPrint.display(result, options.columns, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('by-bs-id')
            .argument('<bssubscriptionid>', 'Zuora subscription id (bsSubscriptionId)')
            .description('Get a subscription by its Zuora bsSubscriptionId')
            .addOption(new Option('-j, --json', 'display raw JSON').conflicts('interactive'))
            .addOption(new Option('--interactive', 'show result in interactive display').conflicts('json'))
            .action(async (bssubscriptionid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'by-bs-id', '');
                try {
                    const result = await this.getSubscriptionByBsId(bssubscriptionid);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result);
                    } else {
                        PrettyPrint.display(result, undefined, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('expiring')
            .description('Get all expiring (and expired) subscriptions (admin)')
            .addOption(new Option('--format <format>', 'output format: small, medium or full (default: full)').default('full'))
            .option('-c, --company <id>', 'filter by company id')
            .option('--offer <id>', 'filter by offer id')
            .option('--days <n>', 'filter by number of days before expiry')
            .addOption(new Option('--demo', 'filter: only demo subscriptions'))
            .addOption(new Option('--terminated', 'filter: only terminated subscriptions'))
            .addOption(new Option('--prepaid', 'filter: only prepaid subscriptions'))
            .option('--company-type <type>', 'filter by company type')
            .option('--auto-renew <value>', 'filter by autoRenew value')
            .option('--region <region>', 'filter by business region')
            .option('--cluster <cluster>', 'filter by business cluster')
            .option('--area <area>', 'filter by business area')
            .option('--country <country>', 'filter by business country')
            .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('subscription', 'expiring', '');
                try {
                    const result = await this.getExpiringSubscriptions(options);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result, 'subscriptions', options);
                    } else {
                        PrettyPrint.display(result, options.columns, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('offers')
            .argument('[companyid]', 'company id (default: current user\'s company)')
            .description('List offers available to a company')
            .addOption(new Option('--format <format>', 'output format: small, medium or full (default: full)').default('full'))
            .addOption(new Option('-n, --name <name>', 'filter by offer name'))
            .addOption(new Option('--can-be-sold', 'filter: only offers that can be sold'))
            .addOption(new Option('--auto-subscribe', 'filter: only offers with auto-subscribe enabled'))
            .addOption(new Option('--exclusive', 'filter: only exclusive offers'))
            .addOption(new Option('--prepaid', 'filter: only prepaid offers'))
            .addOption(new Option('--profile-id <id>', 'filter by profile id'))
            .addOption(new Option('--offer-reference <ref>', 'filter by offer reference'))
            .addOption(new Option('--sap-reference <ref>', 'filter by SAP reference'))
            .addOption(new Option('-l, --limit <n>', 'max number of results'))
            .addOption(new Option('-o, --offset <n>', 'position of first result (pagination)'))
            .addOption(new Option('--sort-field <field>', 'field to sort by'))
            .addOption(new Option('--sort-order <order>', 'sort order: 1 (asc) or -1 (desc)'))
            .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 (companyid, options) => {
                this.services['telemetry'].sendTelemetry('subscription', 'offers', '');
                try {
                    const resolvedId = companyid || await this._currentCompanyId();
                    const result = await this.getOffers(resolvedId, options);
                    if (options.interactive) {
                        this.services['interactive_display'].displayObjectNavigator(result, 'offers', options);
                    } else {
                        PrettyPrint.display(result, options.columns, options.json);
                    }
                } catch (error) {
                    PrettyPrint.error(error.message || error);
                }
            });

        subscriptionCommand
            .command('about')
            .description('Get subscription portal information')
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (options) => {
                PrettyPrint.withSpinner(
                    async () => this.about(),
                    (json) => PrettyPrint.display(json, undefined, options.json)
                );
            });

        subscriptionCommand
            .command('ping')
            .description('Check subscription portal health')
            .addOption(new Option('-j, --json', 'display raw JSON'))
            .action(async (options) => {
                PrettyPrint.withSpinner(
                    async () => this.ping(),
                    (json) => PrettyPrint.display(json, undefined, options.json)
                );
            });
    }

    async about() {
        this.logger.debug('Subscription.about');
        return this.services['rest'].restCall('GET', '/api/rainbow/subscription/v1.0/about');
    }

    async ping() {
        this.logger.debug('Subscription.ping');
        return this.services['rest'].restCall('GET', '/api/rainbow/ping/business-store');
    }

    async getDeveloperSubscriptions(userId) {
        this.logger.debug('Subscription.getDeveloperSubscriptions');

        const json = await this.services['rest'].restCall(
            'GET',
            `/api/rainbow/subscription/v1.0/developers/${userId}/subscriptions`
        );
        return json;
    }

    async getPaymentMethods(userId) {
        this.logger.debug('Subscription.getPaymentMethods');

        const json = await this.services['rest'].restCall(
            'GET',
            `/api/rainbow/subscription/v1.0/developers/${userId}/payments/methods`
        );
        return json;
    }

    async getOffers(companyId, options) {
        this.logger.debug('Subscription.getOffers');

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

        if (options.name)           query_parameters.push(['name', options.name]);
        if (options.canBeSold)      query_parameters.push(['canBeSold', 'true']);
        if (options.autoSubscribe)  query_parameters.push(['autoSubscribe', 'true']);
        if (options.exclusive)      query_parameters.push(['isExclusive', 'true']);
        if (options.prepaid)        query_parameters.push(['isPrepaid', 'true']);
        if (options.profileId)      query_parameters.push(['profileId', options.profileId]);
        if (options.offerReference) query_parameters.push(['offerReference', options.offerReference]);
        if (options.sapReference)   query_parameters.push(['sapReference', options.sapReference]);
        if (options.limit)          query_parameters.push(['limit', options.limit]);
        if (options.offset)         query_parameters.push(['offset', options.offset]);
        if (options.sortField)      query_parameters.push(['sortField', options.sortField]);
        if (options.sortOrder)      query_parameters.push(['sortOrder', options.sortOrder]);

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

    async getSubscriptions(companyId, options) {
        this.logger.debug('Subscription.getSubscriptions');

        const query_parameters = [['format', options.format]];
        if (options.terminated) query_parameters.push(['showTerminated', 'true']);
        if (options.guardian)   query_parameters.push(['showActiveGuardian', 'true']);

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

    async getCatalogs(options) {
        this.logger.debug('Subscription.getCatalogs');

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

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

    async createSubscription(companyId, offerId, options) {
        this.logger.debug('Subscription.createSubscription');

        const body = { offerId };
        if (options.maxUsers)        body.maxNumberUsers = parseInt(options.maxUsers);
        if (options.applicationId)   body.applicationId = options.applicationId;
        if (options.promoCode)       body.promoCode = options.promoCode;
        if (options.purchaseOrder)   body.purchaseOrderNumber = options.purchaseOrder;
        if (options.expirationDate)  body.expirationDate = options.expirationDate;
        if (options.monthlyDuration) body.monthlyDuration = parseInt(options.monthlyDuration);
        if (options.prepaidDuration) body.prepaidDuration = parseInt(options.prepaidDuration);
        if (options.paymentPeriod)   body.paymentPeriod = options.paymentPeriod;
        if (options.renewalTerm)     body.renewalTerm = parseInt(options.renewalTerm);
        if (options.autoRenew)       body.autoRenew = true;

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

    async updateSubscription(companyId, subscriptionId, options) {
        this.logger.debug('Subscription.updateSubscription');

        const body = {};
        if (options.maxUsers)              body.maxNumberUsers = parseInt(options.maxUsers);
        if (options.applicationId)         body.applicationId = options.applicationId;
        if (options.promoCode)             body.promoCode = options.promoCode;
        if (options.purchaseOrder)         body.purchaseOrderNumber = options.purchaseOrder;
        if (options.expirationDate)        body.expirationDate = options.expirationDate;
        if (options.monthlyDuration)       body.monthlyDuration = parseInt(options.monthlyDuration);
        if (options.prepaidDuration)       body.prepaidDuration = parseInt(options.prepaidDuration);
        if (options.renewalTerm)           body.renewalTerm = parseInt(options.renewalTerm);
        if (options.renewalVolume)         body.renewalVolume = parseInt(options.renewalVolume);
        if (options.prepaidRenewalOption)  body.prepaidRenewalOption = options.prepaidRenewalOption;
        if (options.autoRenew)             body.autoRenew = true;

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

    async deleteSubscription(companyId, subscriptionId) {
        this.logger.debug('Subscription.deleteSubscription');

        const json = await this.services['rest'].restCall(
            'DELETE',
            `/api/rainbow/subscription/v1.0/companies/${companyId}/subscriptions/${subscriptionId}`
        );
        return json;
    }

    async getSubscription(companyId, subscriptionId) {
        this.logger.debug('Subscription.getSubscription');

        const json = await this.services['rest'].restCall(
            'GET',
            `/api/rainbow/subscription/v1.0/companies/${companyId}/subscriptions/${subscriptionId}`
        );
        return json;
    }

    async getSubscriptionUsers(companyId, subscriptionId, options) {
        this.logger.debug('Subscription.getSubscriptionUsers');

        const query_parameters = [['format', options.format]];
        if (options.limit)     query_parameters.push(['limit', options.limit]);
        if (options.offset)    query_parameters.push(['offset', options.offset]);
        if (options.sortField) query_parameters.push(['sortField', options.sortField]);
        if (options.sortOrder) query_parameters.push(['sortOrder', options.sortOrder]);

        const json = await this.services['rest'].restCall(
            'GET',
            `/api/rainbow/subscription/v1.0/companies/${companyId}/subscriptions/${subscriptionId}/users`,
            query_parameters
        );
        return json;
    }

    async getSubscriptionFeatures(companyId) {
        this.logger.debug('Subscription.getSubscriptionFeatures');

        const json = await this.services['rest'].restCall(
            'GET',
            `/api/rainbow/subscription/v1.0/companies/${companyId}/subscriptions/features`
        );
        return json;
    }

    async getSubscriptionCounters(companyId) {
        this.logger.debug('Subscription.getSubscriptionCounters');

        const json = await this.services['rest'].restCall(
            'GET',
            `/api/rainbow/subscription/v1.0/companies/${companyId}/subscriptions/counters`
        );
        return json;
    }

    async getDefaultSubscription(companyId) {
        this.logger.debug('Subscription.getDefaultSubscription');

        const json = await this.services['rest'].restCall(
            'GET',
            `/api/rainbow/subscription/v1.0/companies/${companyId}/subscriptions/default`
        );
        return json;
    }

    async getPrepaidSubscriptions(companyId, options) {
        this.logger.debug('Subscription.getPrepaidSubscriptions');

        const query_parameters = [];
        if (options.days) query_parameters.push(['days', options.days]);

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

    async getSubscriptionByBsId(bsSubscriptionId) {
        this.logger.debug('Subscription.getSubscriptionByBsId');

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

    async getExpiringSubscriptions(options) {
        this.logger.debug('Subscription.getExpiringSubscriptions');

        const query_parameters = [['format', options.format]];
        if (options.company)     query_parameters.push(['companyId', options.company]);
        if (options.offer)       query_parameters.push(['offerId', options.offer]);
        if (options.days)        query_parameters.push(['days', options.days]);
        if (options.demo)        query_parameters.push(['isDemo', 'true']);
        if (options.terminated)  query_parameters.push(['isTerminated', 'true']);
        if (options.prepaid)     query_parameters.push(['isPrepaid', 'true']);
        if (options.companyType) query_parameters.push(['companyType', options.companyType]);
        if (options.autoRenew)   query_parameters.push(['autoRenew', options.autoRenew]);
        if (options.region)      query_parameters.push(['businessRegion', options.region]);
        if (options.cluster)     query_parameters.push(['businessCluster', options.cluster]);
        if (options.area)        query_parameters.push(['businessArea', options.area]);
        if (options.country)     query_parameters.push(['businessCountry', options.country]);

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

    async getOffer(companyId, offerId) {
        this.logger.debug('Subscription.getOffer');

        const json = await this.services['rest'].restCall(
            'GET',
            `/api/rainbow/subscription/v1.0/companies/${companyId}/offers/${offerId}`
        );
        return json;
    }

    async resolveOffer(query, companyId, format = 'full') {
        this.logger.debug('Subscription.resolveOffer');

        if (/^[0-9a-f]{24}$/i.test(query)) {
            return await this.getOffer(companyId, query);
        }

        const result = await this.getOffers(companyId, { format, name: query });
        const offers = result?.data;

        if (!offers || offers.length === 0) {
            throw new Error(`No offer found matching "${query}"`);
        }

        if (offers.length === 1) {
            return await this.getOffer(companyId, offers[0].id);
        }

        const { default: select } = await import('@inquirer/select');
        const selectedId = await select({
            message: `Multiple offers found for "${query}". Select one:`,
            choices: offers.map(o => ({ name: o.name, value: o.id }))
        });
        return await this.getOffer(companyId, selectedId);
    }

    async _currentCompanyId() {
        const me = await this.services['enduser'].whoami();
        const companyId = me?.data?.companyId;
        if (!companyId) throw new Error('Unable to determine current user\'s company id');
        return companyId;
    }
}
