import { vi, describe, expect, it, beforeAll, beforeEach } from 'vitest';
import { axios } from 'app/test/vitest/helper';
import { useSession } from 'composables/useSession';
import { useState } from 'composables/useState';

const session = useSession();
const state = useState();

describe('session', () => {
    describe('getToken / setToken', () => {
        it('should return an empty string if no token is found in local or session storage', async () => {
            const expectedToken = '';

            const token = session.getToken();

            expect(token).toEqual(expectedToken);
        });

        it('should return the token stored in local or session storage', async () => {
            const expectedToken = 'myToken';
            const data = {
                token: expectedToken,
                keepLogin: false,
            };
            session.setToken(data);

            const token = session.getToken();

            expect(token).toEqual(expectedToken);
        });
    });

    describe('destroy', () => {
        it('should remove the token from the local storage and set a blank user', async () => {
            const previousUser = {
                id: 999,
                name: `T'Challa`,
                nickname: 'Black Panther',
                lang: 'en',
                darkMode: false,
            };
            const expectedUser = {
                id: 0,
                name: '',
                nickname: '',
                lang: '',
                darkMode: null,
            };
            let user = state.getUser();

            localStorage.setItem('token', 'tokenToBeGone');
            state.setUser(previousUser);

            expect(localStorage.getItem('token')).toEqual('tokenToBeGone');
            expect(user.value).toEqual(previousUser);

            vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
            vi.spyOn(axios, 'get').mockResolvedValue({ data: true });
            await session.destroy();

            user = state.getUser();
            expect(localStorage.getItem('token')).toBeNull();
            expect(user.value).toEqual(expectedUser);
        });
    });

    describe(
        'login',
        () => {
            const expectedUser = {
                id: 999,
                name: `T'Challa`,
                nickname: 'Black Panther',
                lang: 'en',
                userConfig: {
                    darkMode: false,
                },
            };
            const rolesData = [
                {
                    role: {
                        name: 'salesPerson',
                    },
                },
                {
                    role: {
                        name: 'admin',
                    },
                },
            ];
            beforeEach(() => {
                vi.spyOn(axios, 'get').mockImplementation((url) => {
                    if (url === 'VnUsers/acls') return Promise.resolve({ data: [] });
                    return Promise.resolve({
                        data: { roles: rolesData, user: expectedUser },
                    });
                });
            });

            it('should fetch the user roles and then set token in the sessionStorage', async () => {
                const expectedRoles = ['salesPerson', 'admin'];
                const expectedToken = 'mySessionToken';
                const expectedTokenMultimedia = 'mySessionTokenMultimedia';
                const keepLogin = false;

                await session.login({
                    token: expectedToken,
                    tokenMultimedia: expectedTokenMultimedia,
                    keepLogin,
                });

                const roles = state.getRoles();
                const localToken = localStorage.getItem('token');
                const sessionToken = sessionStorage.getItem('token');

                expect(roles.value).toEqual(expectedRoles);
                expect(localToken).toBeNull();
                expect(sessionToken).toEqual(expectedToken);

                await session.destroy(); // this clears token and user for any other test
            });

            it('should fetch the user roles and then set token in the localStorage', async () => {
                const expectedRoles = ['salesPerson', 'admin'];
                const expectedToken = 'myLocalToken';
                const expectedTokenMultimedia = 'myLocalTokenMultimedia';
                const keepLogin = true;

                await session.login({
                    token: expectedToken,
                    tokenMultimedia: expectedTokenMultimedia,
                    keepLogin,
                });

                const roles = state.getRoles();
                const localToken = localStorage.getItem('token');
                const sessionToken = sessionStorage.getItem('token');

                expect(roles.value).toEqual(expectedRoles);
                expect(localToken).toEqual(expectedToken);
                expect(sessionToken).toBeNull();

                await session.destroy(); // this clears token and user for any other test
            });
        },
        {}
    );

    describe('RenewToken', () => {
        const expectedToken = 'myToken';
        const expectedTokenMultimedia = 'myTokenMultimedia';
        const currentDate = new Date();
        beforeAll(() => {
            const tokenConfig = {
                id: 1,
                renewPeriod: 21600,
                courtesyTime: 60,
                renewInterval: 300,
            };
            state.setTokenConfig(tokenConfig);
            sessionStorage.setItem('renewPeriod', 1);
        });
        it('NOT Should renewToken', async () => {
            const data = {
                token: expectedToken,
                tokenMultimedia: expectedTokenMultimedia,
                keepLogin: false,
                ttl: 1,
                created: Date.now(),
            };
            session.setSession(data);
            expect(sessionStorage.getItem('keepLogin')).toBeFalsy();
            expect(sessionStorage.getItem('created')).toBeDefined();
            expect(sessionStorage.getItem('ttl')).toEqual(1);
            await session.checkValidity();
            expect(sessionStorage.getItem('token')).toEqual(expectedToken);
            expect(sessionStorage.getItem('tokenMultimedia')).toEqual(
                expectedTokenMultimedia
            );
        });
        it('Should renewToken', async () => {
            currentDate.setMinutes(currentDate.getMinutes() - 100);
            const data = {
                token: expectedToken,
                tokenMultimedia: expectedTokenMultimedia,
                keepLogin: false,
                ttl: 1,
                created: currentDate,
            };
            session.setSession(data);

            vi.spyOn(axios, 'post')
                .mockResolvedValueOnce({
                    data: { id: '' },
                })
                .mockResolvedValueOnce({
                    data: {
                        id: '',
                    },
                });
            expect(sessionStorage.getItem('keepLogin')).toBeFalsy();
            expect(sessionStorage.getItem('created')).toBeDefined();
            expect(sessionStorage.getItem('ttl')).toEqual(1);
            await session.checkValidity();
            expect(sessionStorage.getItem('token')).not.toEqual(expectedToken);
            expect(sessionStorage.getItem('tokenMultimedia')).not.toEqual(
                expectedTokenMultimedia
            );
        });
    });
});
