import { createModeRequestCoordinator } from "./mode-request-coordinator";

const createDeferred = () => {
    let resolve;
    const promise = new Promise((resolvePromise) => {
        resolve = resolvePromise;
    });

    return { promise, resolve };
};

describe("createModeRequestCoordinator", () => {
    it("runs an all-mode request after an in-flight default request", async () => {
        const coordinator = createModeRequestCoordinator("default");
        const defaultRequest = createDeferred();
        const startAllRequest = jest.fn(() => Promise.resolve("all"));

        const defaultResult = coordinator.run(
            "default",
            () => defaultRequest.promise,
        );
        const allResult = coordinator.run("all", startAllRequest);

        expect(coordinator.getSelectedMode()).toBe("all");
        expect(startAllRequest).not.toHaveBeenCalled();

        defaultRequest.resolve("default");

        await expect(defaultResult).resolves.toBe("default");
        await expect(allResult).resolves.toBe("all");
        expect(startAllRequest).toHaveBeenCalledTimes(1);
    });

    it("coalesces an automatic refresh into an in-flight all-mode request", async () => {
        const coordinator = createModeRequestCoordinator("default");
        const allRequest = createDeferred();
        const startAllRequest = jest.fn(() => allRequest.promise);
        const startPollRequest = jest.fn(() => Promise.resolve("poll"));

        const loadAllResult = coordinator.run("all", startAllRequest);
        const pollResult = coordinator.run(
            coordinator.getSelectedMode(),
            startPollRequest,
        );

        expect(coordinator.getSelectedMode()).toBe("all");
        expect(startPollRequest).not.toHaveBeenCalled();

        allRequest.resolve("all");

        await expect(loadAllResult).resolves.toBe("all");
        await expect(pollResult).resolves.toBe("all");
        expect(startAllRequest).toHaveBeenCalledTimes(1);
        expect(startPollRequest).not.toHaveBeenCalled();
    });

    it("refetches the selected mode after an in-flight request when freshness is required", async () => {
        const coordinator = createModeRequestCoordinator("all");
        const currentRequest = createDeferred();
        const startFreshRequest = jest.fn(() => Promise.resolve("fresh"));

        const currentResult = coordinator.run(
            "all",
            () => currentRequest.promise,
        );
        const freshResult = coordinator.run(
            coordinator.getSelectedMode(),
            startFreshRequest,
            { ensureFresh: true },
        );

        expect(startFreshRequest).not.toHaveBeenCalled();
        currentRequest.resolve("current");

        await expect(currentResult).resolves.toBe("current");
        await expect(freshResult).resolves.toBe("fresh");
        expect(startFreshRequest).toHaveBeenCalledTimes(1);
    });

    it("does not let an earlier default waiter replace a later all-mode selection", async () => {
        const coordinator = createModeRequestCoordinator("default");
        const initialRequest = createDeferred();
        const refreshedDefaultRequest = createDeferred();
        const startRefreshedDefault = jest.fn(
            () => refreshedDefaultRequest.promise,
        );
        const startAllRequest = jest.fn(() => Promise.resolve("all"));

        const initialResult = coordinator.run(
            "default",
            () => initialRequest.promise,
        );
        const defaultRefreshResult = coordinator.run(
            "default",
            startRefreshedDefault,
            { ensureFresh: true },
        );
        const allResult = coordinator.run("all", startAllRequest);

        initialRequest.resolve("initial");
        await expect(initialResult).resolves.toBe("initial");

        expect(startRefreshedDefault).toHaveBeenCalledTimes(1);
        expect(startAllRequest).not.toHaveBeenCalled();

        refreshedDefaultRequest.resolve("refreshed-default");

        await expect(defaultRefreshResult).resolves.toBe("refreshed-default");
        await expect(allResult).resolves.toBe("all");
        expect(coordinator.getSelectedMode()).toBe("all");
        expect(startAllRequest).toHaveBeenCalledTimes(1);
    });
});
