import FetchWorkerTool from './FetchWorkerTool';
import {FetchTool} from './FetchTool';

/**
 * @typedef {object} Request
 * @property {string} url
 * @property {*} body
 * @property {string} method
 * @property {boolean} withCredentials
 */

/**
 * Get and send assets with other tools in sequence.
 */
export default class ProxyTool {
    // TODO: Typing
    public tools: any[];

    /**
     * Constant values that filter the set of tools in a ProxyTool instance.
     * @enum {string}
     */
    public static TOOL_FILTER = {
        /**
         * Use all tools.
         */
        ALL: 'all',

        /**
         * Use tools that are ready right now.
         */
        READY: 'ready'
    };

    constructor (filter = ProxyTool.TOOL_FILTER.ALL) {
        let tools;
        if (filter === ProxyTool.TOOL_FILTER.READY) {
            tools = [new FetchTool()];
        } else {
            tools = [new FetchWorkerTool(), new FetchTool()];
        }

        /**
         * Sequence of tools to proxy.
         * @type {Array.<Tool>}
         */
        this.tools = tools;
    }

    /**
     * Is get supported? false if all proxied tool return false.
     * @returns {boolean} Is get supported?
     */
    get isGetSupported () {
        return this.tools.some(tool => tool.isGetSupported);
    }

    /**
     * Request data from with one of the proxied tools.
     * @param {Request} reqConfig - Request configuration for data to get.
     * @returns {Promise.<Buffer>} Resolve to Buffer of data from server.
     */
    get (reqConfig) {
        let toolIndex = 0;
        const nextTool = (err?) => {
            const tool = this.tools[toolIndex++];
            if (!tool) {
                throw err;
            }
            if (!tool.isGetSupported) {
                return nextTool(err);
            }
            return tool.get(reqConfig).catch(nextTool);
        };
        return nextTool();
    }

    /**
     * Is sending supported? false if all proxied tool return false.
     * @returns {boolean} Is sending supported?
     */
    get isSendSupported () {
        return this.tools.some(tool => tool.isSendSupported);
    }

    /**
     * Send data to a server with one of the proxied tools.
     * @param {Request} reqConfig - Request configuration for data to send.
     * @returns {Promise.<Buffer|string|object>} Server returned metadata.
     */
    send (reqConfig) {
        let toolIndex = 0;
        const nextTool = (err?) => {
            const tool = this.tools[toolIndex++];
            if (!tool) {
                throw err;
            }
            if (!tool.isSendSupported) {
                return nextTool(err);
            }
            return tool.send(reqConfig).catch(nextTool);
        };
        return nextTool();
    }
}