import { SdkStates } from "@boldpenguin/sdk-types";
import { newSpecPage } from "@stencil/core/testing";
import { BpSdkSetupContainer } from "./bp-sdk-setup-container";
const fakeStore = {
    config: {
        env: 'testEnv',
        uri: 'https://test.local',
    },
    auth: {
        token: 'testToken',
        expiresAt: 1234,
    },
    status: {
        local: {
            user: SdkStates.Configured,
        },
    },
};
const setupPage = async () => {
    return await newSpecPage({
        html: '<bp-sdk-setup-container></bp-sdk-setup-container>',
        components: [BpSdkSetupContainer],
    });
};
const setupInstance = (page) => {
    const instance = page.rootInstance;
    return instance;
};
describe('bp-sdk-setup-container', () => {
    it('renders', async () => {
        const page = await setupPage();
        const instance = setupInstance(page);
        instance.getFeatures = jest.fn(() => { });
        await page.waitForChanges();
        expect(page.root.querySelector('.bp-sdk-setup-container__container')).toBeTruthy();
        expect(page.root.querySelector('bp-sdk-external-setup-container')).toBeFalsy();
    });
    it('renders external component if defined', async () => {
        const whenDefinedMock = jest.spyOn(customElements, 'whenDefined').mockResolvedValueOnce({});
        const page = await setupPage();
        const instance = setupInstance(page);
        instance.getFeatures = jest.fn(() => { });
        await page.waitForChanges();
        expect(page.root.querySelector('.bp-sdk-setup-container__container')).toBeFalsy();
        expect(page.root.querySelector('bp-sdk-external-setup-container')).toBeTruthy();
        whenDefinedMock.mockRestore();
    });
    describe('disconnectedCallback', () => {
        it('makes expected calls', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            instance.getFeatures = jest.fn(() => { });
            instance.storeUnsubscribe = jest.fn();
            await page.waitForChanges();
            instance.disconnectedCallback();
            expect(instance.storeUnsubscribe).toBeCalledTimes(1);
        });
        it('handles null unsubscribe', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            instance.getFeatures = jest.fn(() => { });
            instance.storeUnsubscribe = null;
            await page.waitForChanges();
            expect(instance.disconnectedCallback).not.toThrow();
        });
    });
    describe('onExpiresAtChange', () => {
        let page, instance;
        beforeEach(async () => {
            // todo: revisit with jest modern timers
            jest.useFakeTimers({ legacyFakeTimers: true });
            jest.spyOn(Date, 'now').mockReturnValue(0);
            page = await setupPage();
            instance = setupInstance(page);
            jest.spyOn(instance, 'checkIfReady');
            global.setTimeout.mockReset();
            instance.expiresAt = Date.now() + 100;
            await page.waitForChanges();
        });
        afterAll(() => {
            Date.now.mockRestore();
            jest.useRealTimers();
        });
        it('sets timeout to check auth', async () => {
            expect(global.setTimeout).toHaveBeenCalledTimes(1);
            expect(global.setTimeout).toHaveBeenCalledWith(expect.any(Function), 100);
        });
        it('will check if authenticated on timeout', async () => {
            instance.checkIfReady.mockReset();
            global.setTimeout.mock.calls[0][0]();
            expect(instance.checkIfReady).toBeCalledTimes(1);
        });
    });
    describe('onCheckAuthenticationChange', () => {
        let page, instance;
        beforeEach(async () => {
            page = await setupPage();
            instance = setupInstance(page);
            await page.waitForChanges();
        });
        describe('when authenticated', () => {
            beforeEach(() => {
                instance.authenticated = true;
            });
            it('sets isReady=true when user also has permission', async () => {
                instance.hasPermission = true;
                instance.isSdkLoaded = true;
                await page.waitForChanges();
                expect(instance.isReady).toBeTruthy();
            });
            it('sets isReady=false when user does not have permission', async () => {
                instance.hasPermission = false;
                instance.isSdkLoaded = true;
                await page.waitForChanges();
                expect(instance.isReady).toBeFalsy();
            });
        });
        describe('when not authenticated', () => {
            beforeEach(() => {
                instance.authenticated = false;
                instance.isSdkLoaded = true;
            });
            it('sets isReady=false even if user has permission', async () => {
                instance.hasPermission = true;
                await page.waitForChanges();
                expect(instance.isReady).toBeFalsy();
            });
            it('sets isReady=false when user does not have permission', async () => {
                instance.hasPermission = false;
                await page.waitForChanges();
                expect(instance.isAuthenticated).toBeFalsy();
            });
        });
        describe('when sdk not loaded', () => {
            beforeEach(() => {
                instance.isSdkLoaded = false;
            });
            it('sets isReady=false even user has permission and authenticated', async () => {
                instance.hasPermission = true;
                instance.authenticated = true;
                await page.waitForChanges();
                expect(instance.isReady).toBeFalsy();
            });
        });
    });
    describe('onIsReadyChange', () => {
        it('makes expected calls', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            instance.getFeatures = jest.fn(() => Promise.resolve());
            await page.waitForChanges();
            instance.onIsReadyChange(true);
            expect(instance.getFeatures).toBeCalledTimes(1);
        });
        it('does nothing if not ready', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            instance.getFeatures = jest.fn(() => Promise.resolve());
            instance.isReady = false;
            await page.waitForChanges();
            instance.onIsReadyChange(false);
            expect(instance.getFeatures).toBeCalledTimes(0);
        });
    });
    describe('mapStateToProps', () => {
        it('returns an object with the expected keys', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            const result = instance.mapStateToProps(fakeStore);
            expect(result).toHaveProperty('config');
            expect(result).toHaveProperty('expiresAt');
            expect(result).toHaveProperty('token');
            expect(result).toHaveProperty('authenticated');
            expect(result).toHaveProperty('hasPermission');
        });
    });
    describe('handleSubscriptions', () => {
        it('calls unsubscribe if subscribed', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            const unsubscribe = jest.fn();
            instance.storeUnsubscribe = unsubscribe;
            instance.handleSubscriptions();
            expect(unsubscribe).toHaveBeenCalled();
        });
        it('overwrites an existing subscribe function', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            const unsubscribe = jest.fn();
            instance.storeUnsubscribe = unsubscribe;
            instance.handleSubscriptions();
            expect(instance.storeUnsubscribe).not.toEqual(unsubscribe);
            expect(instance.storeUnsubscribe).toBeDefined();
        });
        it('subscribes if not subscribed', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            instance.storeUnsubscribe = null;
            instance.handleSubscriptions();
            expect(instance.storeUnsubscribe).not.toBeNull();
        });
        it('subscribes updates mapStateToProps if already has storeUnsubscribe', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            const unsubscribe = jest.fn();
            instance.storeUnsubscribe = unsubscribe;
            instance.handleSubscriptions();
            expect(instance.storeUnsubscribe).not.toEqual(unsubscribe);
        });
    });
    describe('connectedCallback', () => {
        it('makes expected call if root available', async () => {
            const page = await setupPage();
            const instance = setupInstance(page);
            instance.handleSubscriptions = jest.fn();
            jest.spyOn(instance.el, 'querySelector').mockImplementationOnce(() => ({
                componentOnReady: () => Promise.resolve(true),
            }));
            instance.connectedCallback();
            await page.waitForChanges();
            expect(instance.handleSubscriptions).toBeCalledTimes(1);
        });
    });
});
