import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import * as suiteAuth from '@theorchard/suite-auth';
import { SessionExpirationModal } from '../sessionExpirationModal';
import type { Props } from '../sessionExpirationModal';

vi.mock('@theorchard/suite-auth');

describe('<SessionExpirationModal', () => {
    const authClient = { logout: vi.fn(), retrySession: vi.fn() } as any;
    const defaultProps = {
        children: <div>Main Content</div>,
    };
    const renderModal = (props?: Props) =>
        render(<SessionExpirationModal {...defaultProps} {...props} />);

    beforeEach(() => {
        vi.clearAllMocks();
        vi.spyOn(suiteAuth, 'useAuthClient').mockReturnValue(authClient);
        vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(false);
    });

    afterEach(() => vi.clearAllMocks());

    describe('child content', () => {
        test('is rendered when session is not expired', () => {
            renderModal();
            expect(screen.getByText('Main Content')).toBeVisible();
        });

        test('is rendered when session is expired', () => {
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(true);
            renderModal();
            expect(screen.getByText('Main Content')).toBeVisible();
        });

        test('is not rendered if custom backdrop is defined', () => {
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(true);
            renderModal({
                ...defaultProps,
                config: {
                    modal: () => true,
                    SessionExpiredBackdrop: () => <div>BACKDROP_OVERRIDE</div>,
                },
            });
            expect(screen.queryByText('Main Content')).toBeNull();
            expect(screen.getByText('BACKDROP_OVERRIDE')).toBeVisible();
        });
    });

    describe('modal', () => {
        test('is not rendered by default', () => {
            renderModal();
            expect(screen.queryByTestId('Modal')).toBeNull();
        });

        test('is rendered when session is expired', () => {
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(true);
            renderModal();
            expect(screen.getByTestId('Modal')).toBeVisible();
            expect(screen.getByText("You've Been Logged Out")).toBeVisible();
        });

        test('does not render a close button in the header', () => {
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(true);
            renderModal();
            expect(screen.queryByTestId('CloseGlyphIcon')).toBeNull();
        });

        test('does not render a logout button', () => {
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(true);
            renderModal();
            expect(screen.queryByText('Log Out')).toBeNull();
        });

        test('clicking log back in calls page reload', () => {
            vi.spyOn(window.location, 'reload');
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(true);
            renderModal();
            const btn = screen.getByText('Log Back In');
            fireEvent.click(btn);
            expect(window.location.reload).toHaveBeenCalled();
        });

        test('retry button calls retrySession', async () => {
            authClient.retrySession.mockResolvedValue(true);
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(true);
            renderModal();
            const btn = screen.getByText('Retry');
            fireEvent.click(btn);
            await waitFor(() => expect(authClient.retrySession).toHaveBeenCalled());
        });

        test('modal stays open when retrySession fails', async () => {
            authClient.retrySession.mockResolvedValue(false);
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(true);
            renderModal();
            const btn = screen.getByText('Retry');
            fireEvent.click(btn);
            await waitFor(() => expect(authClient.retrySession).toHaveBeenCalled());
            expect(screen.getByTestId('Modal')).toBeVisible();
        });
    });

    describe('auto-retry on storage event', () => {
        const auth0TokenKey =
            '@@auth0spajs@@::clientId::https://api.example.com::openid profile email';

        test('calls retrySession when auth0 writes an access token to localStorage', async () => {
            authClient.retrySession.mockResolvedValue(true);
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(true);
            renderModal();

            window.dispatchEvent(
                new StorageEvent('storage', {
                    key: auth0TokenKey,
                    newValue: JSON.stringify({ access_token: 'tok_abc123' }),
                })
            );

            await waitFor(() => expect(authClient.retrySession).toHaveBeenCalled());
        });

        test('does not call retrySession on storage events when session is not expired', async () => {
            authClient.retrySession.mockResolvedValue(true);
            vi.spyOn(suiteAuth, 'useSessionExpired').mockReturnValue(false);
            renderModal();

            window.dispatchEvent(
                new StorageEvent('storage', {
                    key: auth0TokenKey,
                    newValue: JSON.stringify({ access_token: 'tok_abc123' }),
                })
            );

            await new Promise((resolve) => setTimeout(resolve, 0));
            expect(authClient.retrySession).not.toHaveBeenCalled();
        });
    });
});
