const {ScratchStorage} = require('../../src/ScratchStorage');

/**
 * Simulate a storage helper, adding log messages when "load" is called rather than actually loading anything.
 */
class LoggingHelper {
    /**
     * Construct a LoggingHelper instance.
     * @param {Storage} storage - An instance of the storage module.
     * @param {string} label - A label for this instance.
     * @param {boolean} shouldSucceed - set to true to make `load` always succeed, or false to make `load` always fail.
     * @param {Array.<string>} logContainer - an array in which log messages will be stored.
     * @constructor
     */
    constructor (storage, label, shouldSucceed, logContainer) {
        this.storage = storage;
        this.label = label;
        this.shouldSucceed = shouldSucceed;
        this.logContainer = logContainer;
    }

    /**
     * Pretend to fetch an asset, but instead add a message to the log container.
     * @param {AssetType} assetType - The type of asset to fetch.
     * @param {string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
     * @param {DataFormat} dataFormat - The file format / file extension of the asset to fetch: PNG, JPG, etc.
     * @return {Promise.<Asset>} A promise for the contents of the asset.
     */
    load (assetType, assetId, dataFormat) {
        this.logContainer.push(this.label);
        return this.shouldSucceed ?
            Promise.resolve(new this.storage.Asset(assetType, assetId, dataFormat, Buffer.from(this.label))) :
            Promise.reject(new Error(`This is an expected failure from ${this.label}`));
    }
}

test('ScratchStorage constructor', () => {
    const storage = new ScratchStorage();
    expect(storage).toBeInstanceOf(ScratchStorage);
});

test('LoggingHelper constructor', () => {
    const storage = new ScratchStorage();
    const loggingHelper = new LoggingHelper(storage, 'constructor test', true, []);
    expect(loggingHelper).toBeInstanceOf(LoggingHelper);
});

test('addHelper', async () => {
    const logContainer = [];
    const storage = new ScratchStorage();

    const initialHelperCount = storage._helpers.length;

    // The first two helpers should fail (shouldSucceed=false) so that the storage module continues through the list.
    // The third helper should succeed (shouldSucceed=true) so that the overall load succeeds.
    const loggingHelpers = [
        new LoggingHelper(storage, 'first', false, logContainer),
        new LoggingHelper(storage, 'second', false, logContainer),
        new LoggingHelper(storage, 'third', true, logContainer)
    ];

    // Add out of order to check that the priority values are respected
    storage.addHelper(loggingHelpers[2], -50);
    storage.addHelper(loggingHelpers[0], 50);
    storage.addHelper(loggingHelpers[1], 0);

    // Did they all get added?
    expect(storage._helpers.length).toBe(initialHelperCount + loggingHelpers.length);

    // We shouldn't have any log entries yet
    expect(logContainer).toStrictEqual([]);

    await storage.load(storage.AssetType.Project, '0');

    // Verify that all helpers were consulted, and in the correct order
    expect(logContainer).toStrictEqual([
        'first',
        'second',
        'third'
    ]);
});