const TextEncoder = require('util').TextEncoder;
const crossFetch = jest.requireActual('cross-fetch');
const knownAssets = require('../fixtures/known-assets.js');

const Headers = crossFetch.Headers;
const successText = 'successful response';

/**
 * @typedef MockFetchResponse The Response-like object returned by mockFetch.
 * @property {boolean} ok True if the simulated request was successful, false otherwise.
 * @property {number} status The HTTP status code of the simulated request.
 * @property {() => Promise<string>} [text] A success string if the simulated request succeeded, undefined otherwise.
 * @property {() => Promise<Uint8Array>} [arrayBuffer] Same as `text`, but encoded with UTF-8 if present.
 */

/**
 * @typedef {RequestInit & {mockFetchTestData: MockFetchTestData}} MockFetchRequestInit
 */

/**
 * @typedef MockFetchTestData
 * @property {Headers} [headers] A Headers object initialized with the header info received by mockFetch.
 * @property {Number} [headersCount] The number of headers in the 'headers' property.
 */

/**
 * Mock the 'fetch' method from browsers.
 * @param {RequestInfo|URL} resource The (mock) resource to fetch, which will determine the response.
 * @param {MockFetchRequestInit} [options] Optional object containing custom settings for this request.
 * @returns {Promise<MockFetchResponse>} A promise for a Response-like object. Does not fully implement Response.
 */
const mockFetch = (resource, options) => {
    /** @type MockFetchResponse */
    const results = {
        ok: false,
        status: 0
    };
    if (options?.mockFetchTestData) {
        options.mockFetchTestData.headers = new Headers(options.headers);
        options.mockFetchTestData.headersCount = Array.from(options.mockFetchTestData.headers).length;
    }

    const assetInfo = knownAssets[resource];
    if (assetInfo) {
        results.ok = true;
        results.status = 200;
        results.arrayBuffer = () => Promise.resolve(assetInfo.content);
    } else {
        switch (resource) {
        case '200':
            results.ok = true;
            results.status = 200;
            results.text = () => Promise.resolve(successText);
            results.arrayBuffer = () => Promise.resolve(new TextEncoder().encode(successText));
            break;
        case '404':
            results.ok = false;
            results.status = 404;
            break;
        case '500':
            results.ok = false;
            results.status = 500;
            break;
        default:
            throw new Error(`mockFetch doesn't know how to download: ${resource}`);
        }
    }
    return Promise.resolve(results);
};

// Mimic the cross-fetch module, but replace its `fetch` with `mockFetch` and add a few extras
module.exports = {
    ...crossFetch, // Headers, Request, Response, etc.
    default: mockFetch,
    fetch: mockFetch,
    mockFetch,
    successText
};