/**
 * Licensed Materials - Property of IBM
 * IBM Cognos Products: Glass
 * (C) Copyright IBM Corp. 2020, 2021
 * US Government Users Restricted Rights - Use, duplication or disclosure
 * restricted by GSA ADP Schedule Contract with IBM Corp.
 */

import secondaryLogin, {
    SecondaryLoginConstants,
    postSecondaryLogin
} from "cawebpack/services/SecondaryLoginService";
import BrowserUtils from "baglass/core-client/js/core-client/utils/BrowserUtils";

jest.mock("caglass/nls/StringResources", () => {
    return {
        get: jest.fn()
    };
});

jest.mock("baglass/core-client/js/core-client/utils/UrlUtils", () => {
    return {
        base64Encode: jest.fn(url => url)
    };
});

jest.mock("baglass/core-client/js/core-client/utils/BrowserUtils", () => {
    return {
        isIE: jest.fn().mockReturnValue(false)
    };
});

jest.mock("baglass/core-client/js/core-client/utils/Utils", () => {
    return {
        getCurrentWindow: jest.fn().mockReturnValue(window)
    };
});

let originalWindowOpen: any;

const mockGlassContext = {
    appController: {
        showMessage: jest.fn(),
        unlockGlass: jest.fn()
    }
};

beforeEach(() => {
    originalWindowOpen = window.open;
    mockGlassContext.appController.showMessage.mockReset();
});

afterEach(() => {
    window.open = originalWindowOpen;
});

describe("SecondaryLoginService", () => {
    it("opens a new window with the secondaryLogin query string", () => {
        const mockOpen = jest.fn();
        window.open = mockOpen;
        secondaryLogin(mockGlassContext);
        const windowURL = new URL(mockOpen.mock.calls[0][0]);
        expect(windowURL.pathname).toEqual(
            `/bi${SecondaryLoginConstants.path}`
        );
        expect(windowURL.search).toEqual(
            `?${SecondaryLoginConstants.parameters}`
        );
    });

    it("closes the secondary window once the login promise resolves", async () => {
        const mockWindowClose = jest.fn();
        const mockSecondaryLoginWindow = {
            ...window,
            close: mockWindowClose
        };
        window.open = jest.fn().mockReturnValueOnce(mockSecondaryLoginWindow);
        const mockEvent: any = {
            data: {
                mockData: "mock"
            },
            source: mockSecondaryLoginWindow
        };
        const mockAddEventListener = jest.fn((type: string, callback: any) => {
            callback(mockEvent);
        });
        window.addEventListener = mockAddEventListener;
        mockSecondaryLoginWindow.addEventListener = mockAddEventListener;
        const eventData = await secondaryLogin(mockGlassContext);
        expect(mockAddEventListener.mock.calls[0][0]).toEqual("load");
        expect(mockAddEventListener.mock.calls[1][0]).toEqual("message");
        expect(eventData).toEqual(mockEvent.data);
        expect(mockWindowClose).toHaveBeenCalled();
        expect(mockGlassContext.appController.showMessage).toHaveBeenCalled();
    });

    it("adds a storage event listener if using IE", async () => {
        const mockWindowClose = jest.fn();
        const mockSecondaryLoginWindow = {
            ...window,
            close: mockWindowClose
        };
        window.open = jest.fn().mockReturnValueOnce(mockSecondaryLoginWindow);
        BrowserUtils.isIE.mockReturnValueOnce(true);

        const mockEvent: any = {
            key: "secondaryLoginResult",
            newValue: JSON.stringify({
                result: {}
            })
        };
        const mockAddEventListener = jest.fn((type: string, callback: any) => {
            callback(mockEvent);
        });
        window.addEventListener = mockAddEventListener;
        window.removeEventListener = jest.fn();
        await secondaryLogin(mockGlassContext);
        expect(mockAddEventListener.mock.calls[0][0]).toEqual("message");
        expect(mockAddEventListener.mock.calls[1][0]).toEqual("storage");
        expect(mockWindowClose).toHaveBeenCalled();
        expect(window.removeEventListener).toHaveBeenCalled();
    });

    describe("after succesfully logging in", () => {
        it("writes to localStorage if using IE", () => {
            BrowserUtils.isIE.mockReturnValueOnce(true);
            const mockResult = {
                result: "test"
            };
            postSecondaryLogin(window, mockResult);
            expect(window.localStorage.secondaryLoginResult).toEqual(
                JSON.stringify(mockResult)
            );
        });

        it("sends a message to the origin window if its defined and not using IE", () => {
            const mockWindow = {
                postMessage: jest.fn()
            };
            window.opener = mockWindow;
            const mockResult = {
                result: "test"
            };
            postSecondaryLogin(window, mockResult);
            expect(mockWindow.postMessage).toHaveBeenCalledWith(mockResult);
        });

        it("closes the window if the origin window no longer exists", () => {
            window.close = jest.fn();
            window.opener = undefined;
            postSecondaryLogin(window, {});
            expect(window.close).toHaveBeenCalled();
        });
    });

    it("adds the CAM error to the Url", () => {
        const mockError: any = {
            type: "bus:promptInfo",
            captions: ["Please type your credentials for authentication."],
            displayObjects: [
                {
                    type: "hidden",
                    name: "CAMNamespace",
                    promptOptions: [],
                    value: "LDAP"
                },
                {
                    type: "display",
                    caption: "Namespace:",
                    name: "CAMNamespaceDisplayName",
                    promptOptions: [],
                    value: "LDAP"
                },
                {
                    type: "text",
                    caption: "User ID:",
                    name: "CAMUsername",
                    promptOptions: []
                },
                {
                    type: "textnoecho",
                    caption: "Password:",
                    name: "CAMPassword",
                    promptOptions: []
                },
                {
                    type: "hidden",
                    name: "h_CAM_action",
                    promptOptions: [],
                    value: "logonAs"
                }
            ]
        };

        const mockOpen = jest.fn();
        window.open = mockOpen;
        secondaryLogin(mockGlassContext, mockError);
        const windowURL = mockOpen.mock.calls[0][0];
        const stateParam = windowURL.split("&")[1];
        expect(stateParam).toEqual(`state=${JSON.stringify(mockError)}`);
    });

    it("redirects to correct url with gateway configured", () => {
        const mockOpen = jest.fn();
        window.open = mockOpen;
        const mockContextWithGateway: any = { ...mockGlassContext };
        mockContextWithGateway.gateway = "/gateway/bi";
        secondaryLogin(mockContextWithGateway);
        const windowURL = new URL(mockOpen.mock.calls[0][0]);
        expect(windowURL.pathname).toEqual("/gateway/bi/login");
        expect(windowURL.search).toEqual(
            `?${SecondaryLoginConstants.parameters}`
        );
    });
});
