import { jsx as _jsx } from "react/jsx-runtime";
import React from 'react';
import { render, act, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import { PersSDKProvider, usePersSDK } from '../providers/PersSDKProvider';
// Store SDK instances for event emission
let mockSdkInstance = null;
// Mock the SDK core module BEFORE any imports use it
jest.mock('@explorins/pers-sdk/core', () => {
    const createMockEventEmitter = () => {
        const subscribers = [];
        return {
            subscribe: jest.fn((callback, options) => {
                subscribers.push({ callback, options });
                return () => {
                    const index = subscribers.findIndex(s => s.callback === callback);
                    if (index > -1)
                        subscribers.splice(index, 1);
                };
            }),
            emit: (event) => {
                subscribers.forEach(({ callback, options }) => {
                    if (!options?.domains || options.domains.includes(event.domain)) {
                        callback(event);
                    }
                });
            },
            _subscribers: subscribers
        };
    };
    const MockPersSDK = jest.fn().mockImplementation(() => {
        const events = createMockEventEmitter();
        mockSdkInstance = {
            events,
            auth: {
                ensureValidToken: jest.fn().mockResolvedValue(true)
            },
            users: {
                getCurrentUser: jest.fn().mockResolvedValue({ id: 'refreshed-user', email: 'refreshed@test.com' })
            },
            restoreSession: jest.fn().mockResolvedValue(null)
        };
        return mockSdkInstance;
    });
    return {
        PersSDK: MockPersSDK,
        DefaultAuthProvider: jest.fn().mockImplementation(() => ({
            getToken: jest.fn().mockResolvedValue('mock-token')
        }))
    };
});
// Mock platform-specific providers
jest.mock('../providers/react-native-http-client', () => ({
    ReactNativeHttpClient: jest.fn().mockImplementation(() => ({}))
}));
jest.mock('../providers/react-native-auth-provider', () => ({
    createReactNativeAuthProvider: jest.fn().mockReturnValue({
        getToken: jest.fn().mockResolvedValue('mock-token')
    })
}));
jest.mock('../providers/rn-dpop-provider', () => ({
    ReactNativeDPoPProvider: jest.fn().mockImplementation(() => ({}))
}));
// Helper component to access context
const TestConsumer = ({ onContext }) => {
    const context = usePersSDK();
    React.useEffect(() => {
        onContext(context);
    }, [context, onContext]);
    return _jsx("div", { "data-testid": "consumer", children: "Consumer" });
};
// Helper to emit events on the mock SDK
const emitAuthEvent = (type, details = {}) => {
    if (mockSdkInstance?.events) {
        mockSdkInstance.events.emit({
            type,
            domain: 'authentication',
            details
        });
    }
};
describe('PersSDKProvider', () => {
    beforeEach(() => {
        jest.clearAllMocks();
        mockSdkInstance = null;
    });
    describe('initialization', () => {
        it('should initialize with null SDK when no config provided', async () => {
            let capturedContext = null;
            render(_jsx(PersSDKProvider, { children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext).not.toBeNull();
            });
            expect(capturedContext.sdk).toBeNull();
            expect(capturedContext.isInitialized).toBe(false);
            expect(capturedContext.isAuthenticated).toBe(false);
            expect(capturedContext.user).toBeNull();
        });
        it('should auto-initialize when config is provided', async () => {
            let capturedContext = null;
            render(_jsx(PersSDKProvider, { config: { apiProjectKey: 'test-project' }, children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext?.isInitialized).toBe(true);
            });
            expect(capturedContext.sdk).not.toBeNull();
        });
        it('should prevent multiple initializations', async () => {
            const { PersSDK } = require('@explorins/pers-sdk/core');
            let capturedContext = null;
            render(_jsx(PersSDKProvider, { config: { apiProjectKey: 'test-project' }, children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext?.isInitialized).toBe(true);
            });
            // Try to initialize again - should be ignored
            await act(async () => {
                await capturedContext.initialize({ apiProjectKey: 'different-project' });
            });
            // Should only have been called once
            expect(PersSDK).toHaveBeenCalledTimes(1);
        });
    });
    describe('event handling', () => {
        it('should set user on session_restored event', async () => {
            let capturedContext = null;
            const mockUser = { id: 'user-123', email: 'test@example.com' };
            render(_jsx(PersSDKProvider, { config: { apiProjectKey: 'test-project' }, children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext?.sdk).not.toBeNull();
            });
            // Emit session_restored event
            await act(async () => {
                emitAuthEvent('session_restored', { user: mockUser });
            });
            await waitFor(() => {
                expect(capturedContext?.isAuthenticated).toBe(true);
                expect(capturedContext?.user).toEqual(mockUser);
            });
        });
        it('should set user on login_success event', async () => {
            let capturedContext = null;
            const mockUser = { id: 'user-456', email: 'login@example.com' };
            render(_jsx(PersSDKProvider, { config: { apiProjectKey: 'test-project' }, children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext?.sdk).not.toBeNull();
            });
            await act(async () => {
                emitAuthEvent('login_success', { user: mockUser });
            });
            await waitFor(() => {
                expect(capturedContext?.isAuthenticated).toBe(true);
                expect(capturedContext?.user).toEqual(mockUser);
            });
        });
        it('should clear user on logout_success event', async () => {
            let capturedContext = null;
            const mockUser = { id: 'user-789', email: 'logout@example.com' };
            render(_jsx(PersSDKProvider, { config: { apiProjectKey: 'test-project' }, children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext?.sdk).not.toBeNull();
            });
            // First, log in
            await act(async () => {
                emitAuthEvent('login_success', { user: mockUser });
            });
            await waitFor(() => {
                expect(capturedContext?.isAuthenticated).toBe(true);
            });
            // Then logout
            await act(async () => {
                emitAuthEvent('logout_success', {});
            });
            await waitFor(() => {
                expect(capturedContext?.isAuthenticated).toBe(false);
                expect(capturedContext?.user).toBeNull();
            });
        });
        it('should clear user on session_restoration_failed event', async () => {
            let capturedContext = null;
            render(_jsx(PersSDKProvider, { config: { apiProjectKey: 'test-project' }, children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext?.sdk).not.toBeNull();
            });
            // Set initial authenticated state
            await act(async () => {
                capturedContext.setAuthenticationState({ id: 'temp-user' }, true);
            });
            await waitFor(() => {
                expect(capturedContext?.isAuthenticated).toBe(true);
            });
            // Emit session_restoration_failed
            await act(async () => {
                emitAuthEvent('session_restoration_failed', { error: 'Token expired' });
            });
            await waitFor(() => {
                expect(capturedContext?.isAuthenticated).toBe(false);
                expect(capturedContext?.user).toBeNull();
            });
        });
        it('should NOT respond to AUTH_FAILED code (dead code was removed)', async () => {
            // This test verifies that we no longer check for event.code === 'AUTH_FAILED'
            // which was dead code that never triggered
            let capturedContext = null;
            const mockUser = { id: 'user-stay', email: 'stay@example.com' };
            render(_jsx(PersSDKProvider, { config: { apiProjectKey: 'test-project' }, children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext?.sdk).not.toBeNull();
            });
            // Log in first
            await act(async () => {
                emitAuthEvent('login_success', { user: mockUser });
            });
            await waitFor(() => {
                expect(capturedContext?.isAuthenticated).toBe(true);
            });
            // Emit an event with code: 'AUTH_FAILED' - this should NOT clear user
            // because we removed the dead code that checked for this
            await act(async () => {
                if (mockSdkInstance?.events) {
                    mockSdkInstance.events.emit({
                        type: 'some_other_event',
                        code: 'AUTH_FAILED', // This was the dead code path we removed
                        domain: 'authentication',
                        details: {}
                    });
                }
            });
            // User should STILL be authenticated - the code: 'AUTH_FAILED' check is gone
            expect(capturedContext.isAuthenticated).toBe(true);
            expect(capturedContext.user).toEqual(mockUser);
        });
    });
    describe('usePersSDK hook', () => {
        it('should throw error when used outside provider', () => {
            // Suppress the expected error
            const spy = jest.spyOn(console, 'error').mockImplementation(() => { });
            const TestComponent = () => {
                usePersSDK();
                return null;
            };
            expect(() => render(_jsx(TestComponent, {}))).toThrow('usePersSDK must be used within a PersSDKProvider');
            spy.mockRestore();
        });
    });
    describe('context methods', () => {
        it('should allow manual authentication state updates', async () => {
            let capturedContext = null;
            const mockUser = { id: 'manual-user', email: 'manual@example.com' };
            render(_jsx(PersSDKProvider, { config: { apiProjectKey: 'test-project' }, children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext?.isInitialized).toBe(true);
            });
            await act(async () => {
                capturedContext.setAuthenticationState(mockUser, true);
            });
            await waitFor(() => {
                expect(capturedContext?.isAuthenticated).toBe(true);
                expect(capturedContext?.user).toEqual(mockUser);
            });
        });
        it('should refresh user data from SDK', async () => {
            let capturedContext = null;
            render(_jsx(PersSDKProvider, { config: { apiProjectKey: 'test-project' }, children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext?.isInitialized).toBe(true);
            });
            await act(async () => {
                await capturedContext.refreshUserData();
            });
            await waitFor(() => {
                expect(capturedContext?.user).toEqual({ id: 'refreshed-user', email: 'refreshed@test.com' });
            });
        });
        it('should throw error when refreshUserData called before init', async () => {
            let capturedContext = null;
            render(_jsx(PersSDKProvider, { children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext).not.toBeNull();
            });
            await expect(capturedContext.refreshUserData()).rejects.toThrow('SDK not initialized');
        });
        it('should throw error when restoreSession called before init', async () => {
            let capturedContext = null;
            render(_jsx(PersSDKProvider, { children: _jsx(TestConsumer, { onContext: (ctx) => { capturedContext = ctx; } }) }));
            await waitFor(() => {
                expect(capturedContext).not.toBeNull();
            });
            await expect(capturedContext.restoreSession()).rejects.toThrow('SDK not initialized');
        });
    });
});
