import Storage from './storage';
import {requestConfigs} from '../config';

/**
 * @description
 * Queue contains discovered handles for which bios have not yet
 * been requested.
 *
 * @name PendingQueue
 */
class PendingQueue {

    /**
     * Get the pending queue
     * @returns {string[]}
     */
    static get queue() {
        return this._pendingQueue || [];
    }

    /**
     * Determine if queue is empty
     * @returns {boolean}
     */
    static get isEmpty() {
        return PendingQueue.queue.length === 0;
    }

    /**
     * Add a handle to pending queue
     * @param {string} value
     */
    static add(value) {
        this._pendingQueue = (PendingQueue.queue).concat([value]);
    }

    /**
     * Batch insert into queue
     * @param {string[]} values
     */
    static addAll(values) {
        this._pendingQueue = (PendingQueue.queue).concat(values);
    }

    /**
     * Whether given handle is currently in queue
     * @param {string} handle
     * @returns {boolean} - true for "yes, it is in queue"
     */
    static inQueue(handle) {
        return PendingQueue.queue.indexOf(handle) >= 0;
    }

    /**
     * Take allowed max number of names from the queue
     * @returns {string[]}
     */
    static takeNext() {
        const N = Math.min(PendingQueue.queue.length,
            requestConfigs.maxLookupCount);
        return PendingQueue.queue.splice(0, N);
    }

    /* @private */
    static clear() {
        this._pendingQueue = [];
    }
}

/**
 * @description
 * List of handles that have been checked during ongoing session
 * and will not be rechecked.
 *
 * @name HandledList
 */
class HandledList {

    /**
     * Get the handles checklist
     * @returns {string[]}
     */
    static get list() {
        return this._handleCheckList || [];
    }

    /**
     * Add (multiple) handles to list of "already
     * processed". Once here that handle will not
     * be checked again.
     * @param values - array of handles
     */
    static add(values) {
        this._handleCheckList = (HandledList.list).concat(values);
    }

    /**
     * Remove (multiple) handles from list of "already
     * processed".
     * @param {string[]} values - array of handles
     */
    static remove(values) {
        this._handleCheckList = HandledList.list
            .filter(value => values.indexOf(value) < 0);
    }

    /**
     * Determine if handle has already been checked
     * for "compliance"
     * @param {string} handle
     * @returns {boolean} - true if already checked
     */
    static isChecked(handle) {
        return HandledList.list.indexOf(handle) >= 0;
    }

    /* @private */
    static clear() {
        this._handleCheckList = [];
    }
}

/**
 * @description
 * Whitelisted handles contain blocked words but user has manually
 * selected to not block them.
 *
 * @name WhiteList
 */
class WhiteList {

    /**
     * Get the list of whitelisted handles
     * @returns {Object<string,string>}
     */
    static get whiteList() {
        return this._whiteList || {};
    }

    static set whiteList(value) {
        this._whiteList = value;
    }

    /**
     * Update client whitelist
     * @param {string} id
     * @param {string} handle
     */
    static add(id, handle) {
        Storage.addWhiteList({[id]: handle}, newList => {
            WhiteList.whiteList = newList;
        });
    }

    /**
     * Check if user id is whitelisted
     * @param {string} id
     * @returns {boolean}
     */
    static contains(id) {
        return !!WhiteList.whiteList[id];
    }
}

/**
 * @description
 * Manage blocker state
 *
 * @module
 * @name BlockerState
 */
export default class BlockerState {

    /**
     * @description
     * Get list of handles that have already been checked during current
     * session, meaning bio was requested and checked for blocking.
     *
     * @returns {HandledList}
     */
    static get handledList() {
        return HandledList;
    }

    /**
     * Get the queue that holds discovered handles waiting to be processed.
     * When handle is in queue, the handle has been found, but bio has not
     * been checked yet.
     *
     * @returns {PendingQueue}
     */
    static get pendingQueue() {
        return PendingQueue;
    }

    /**
     * Get the list of whitelisted handles. Whitelisted handles may contain
     * block words, but user has explicitly bypassed the block on this user.
     *
     * @returns {WhiteList}
     */
    static get whiteList() {
        return WhiteList;
    }

    /**
     * Get/set the timestamp of last API bio request.
     *
     * @returns {number|undefined} - if undefined,
     * request had never been made
     */
    static get lastBioTimestamp() {
        return this._bioRequest;
    }

    static set lastBioTimestamp(value) {
        this._bioRequest = value;
    }

    /**
     * Whether it has been "long enough" since last bio request.
     *
     * @readonly
     * @returns {boolean} - true when it has been long enough.
     */
    static get lastBioExpired() {
        const diff = Date.now() - BlockerState.lastBioTimestamp;
        return Math.abs(diff) > requestConfigs.maxInterval;
    }

    /**
     * Get/set API tokens
     * @returns {Object}
     */
    static get tokens() {
        return this._tokens;
    }

    static set tokens(value) {
        this._tokens = value;
    }

    /**
     * @description
     * Check if module is ready to make API calls (true/false).
     *
     * It needs API tokens from background first and getting them is an
     * async process. Once the API tokens have been gathered, this method
     * will return true, and subsequent API calls should succeed.
     *
     * @readonly
     * @returns {boolean} - true when ready to call API
     */
    static get ready() {
        return !!(BlockerState.tokens &&
            BlockerState.tokens.csrfToken &&
            BlockerState.tokens.bearerToken);
    }

    /**
     * Get/set the list of flagged words that lead to blocking
     * @returns {string[]}
     */
    static get keyList() {
        return this._keyList;
    }

    static set keyList(value) {
        this._keyList = value;
    }

    /**
     * Get/set confirmation setting
     * @returns {Boolean}
     */
    static get confirmBlocks() {
        return this._confirm;
    }

    static set confirmBlocks(value) {
        this._confirm = value;
    }
}