import LimitService from '@tryghost/limit-service';
import RSVP from 'rsvp';
import Service, {inject as service} from '@ember/service';
import {bind} from '@ember/runloop';

class LimitError {
    constructor({errorType, errorDetails, message}) {
        this.errorType = errorType;
        this.errorDetails = errorDetails;
        this.message = message;
    }
}

class IncorrectUsageError extends LimitError {
    constructor(options) {
        super(Object.assign({errorType: 'IncorrectUsageError'}, options));
    }
}

class HostLimitError extends LimitError {
    constructor(options) {
        super(Object.assign({errorType: 'HostLimitError'}, options));
    }
}

export default class LimitsService extends Service {
    @service config;
    @service store;
    @service membersStats;

    constructor() {
        super(...arguments);

        let limits = this.config.get('hostSettings.limits');

        this.limiter = new LimitService();

        if (!limits) {
            return;
        }

        let helpLink;

        if (this.config.get('hostSettings.billing.enabled')
            && this.config.get('hostSettings.billing.enabled') === true
            && this.config.get('hostSettings.billing.url')) {
            helpLink = this.config.get('hostSettings.billing.url');
        } else {
            helpLink = 'https://ghost.org/help/';
        }

        return this.limiter.loadLimits({
            limits: this.decorateWithCountQueries(limits),
            helpLink,
            errors: {
                HostLimitError,
                IncorrectUsageError
            }
        });
    }

    async checkWouldGoOverLimit(limitName, metadata = {}) {
        return this.limiter.checkWouldGoOverLimit(limitName, metadata);
    }

    decorateWithCountQueries(limits) {
        if (limits.staff) {
            limits.staff.currentCountQuery = bind(this, this.getStaffUsersCount);
        }

        if (limits.members) {
            limits.members.currentCountQuery = bind(this, this.getMembersCount);
        }

        return limits;
    }

    async getStaffUsersCount() {
        return RSVP.hash({
            users: this.store.findAll('user', {reload: true}),
            invites: this.store.findAll('invite', {reload: true}),
            roles: this.store.findAll('role', {reload: true}) // NOTE: roles have to be fetched as they are not always loaded with invites
        }).then((data) => {
            const staffUsers = data.users.filter(u => u.get('status') !== 'inactive' && u.role.get('name') !== 'Contributor');
            const staffInvites = data.invites.filter(i => i.role.get('name') !== 'Contributor');

            return staffUsers.length + staffInvites.length;
        });
    }

    async getMembersCount() {
        const counts = await this.membersStats.fetchCounts();

        return counts.total;
    }
}
