import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

// Mock dependencies first
vi.mock('./log.js', () => ({
    default: {
        info: vi.fn(),
        error: vi.fn(),
        warn: vi.fn()
    }
}));

vi.mock('./sync_mutex.js', () => ({
    default: {
        doExclusively: vi.fn().mockImplementation((fn) => fn())
    }
}));

vi.mock('./sql.js', () => ({
    getManyRows: vi.fn(),
    getValue: vi.fn(),
    getRow: vi.fn()
}));

vi.mock('../becca/becca.js', () => ({
    default: {
        getAttribute: vi.fn(),
        getBranch: vi.fn(),
        getNote: vi.fn(),
        getOption: vi.fn()
    }
}));

vi.mock('./protected_session.js', () => ({
    default: {
        decryptString: vi.fn((str) => str)
    }
}));

vi.mock('./cls.js', () => ({
    getAndClearEntityChangeIds: vi.fn().mockReturnValue([])
}));

// Mock the entire ws module instead of trying to set up the server
vi.mock('ws', () => ({
    Server: vi.fn(),
    WebSocket: {
        OPEN: 1,
        CLOSED: 3,
        CONNECTING: 0,
        CLOSING: 2
    }
}));

describe('WebSocket Service', () => {
    let wsService: any;
    let log: any;

    beforeEach(async () => {
        vi.clearAllMocks();
        
        // Get mocked log
        log = (await import('./log.js')).default;

        // Import service after mocks are set up
        wsService = (await import('./ws.js')).default;
    });

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

    describe('Message Broadcasting', () => {
        it('should handle sendMessageToAllClients method exists', () => {
            expect(wsService.sendMessageToAllClients).toBeDefined();
            expect(typeof wsService.sendMessageToAllClients).toBe('function');
        });

        it('should handle LLM stream messages', () => {
            const message = {
                type: 'llm-stream' as const,
                chatNoteId: 'test-chat-123',
                content: 'Hello world',
                done: false
            };

            // Since WebSocket server is not initialized in test environment,
            // this should not throw an error
            expect(() => {
                wsService.sendMessageToAllClients(message);
            }).not.toThrow();
        });

        it('should handle regular messages', () => {
            const message = {
                type: 'frontend-update' as const,
                data: { test: 'data' }
            };

            expect(() => {
                wsService.sendMessageToAllClients(message);
            }).not.toThrow();
        });

        it('should handle sync-failed messages', () => {
            const message = {
                type: 'sync-failed' as const,
                lastSyncedPush: 123
            };

            expect(() => {
                wsService.sendMessageToAllClients(message);
            }).not.toThrow();
        });

        it('should handle api-log-messages', () => {
            const message = {
                type: 'api-log-messages' as const,
                messages: ['test message']
            };

            expect(() => {
                wsService.sendMessageToAllClients(message);
            }).not.toThrow();
        });
    });

    describe('Service Methods', () => {
        it('should have all required methods', () => {
            expect(wsService.init).toBeDefined();
            expect(wsService.sendMessageToAllClients).toBeDefined();
            expect(wsService.syncPushInProgress).toBeDefined();
            expect(wsService.syncPullInProgress).toBeDefined();
            expect(wsService.syncFinished).toBeDefined();
            expect(wsService.syncFailed).toBeDefined();
            expect(wsService.sendTransactionEntityChangesToAllClients).toBeDefined();
            expect(wsService.setLastSyncedPush).toBeDefined();
            expect(wsService.reloadFrontend).toBeDefined();
        });

        it('should handle sync methods without errors', () => {
            expect(() => wsService.syncPushInProgress()).not.toThrow();
            expect(() => wsService.syncPullInProgress()).not.toThrow();
            expect(() => wsService.syncFinished()).not.toThrow();
            expect(() => wsService.syncFailed()).not.toThrow();
        });

        it('should handle reload frontend', () => {
            expect(() => wsService.reloadFrontend('test reason')).not.toThrow();
        });

        it('should handle transaction entity changes', () => {
            expect(() => wsService.sendTransactionEntityChangesToAllClients()).not.toThrow();
        });

        it('should handle setLastSyncedPush', () => {
            expect(() => wsService.setLastSyncedPush(123)).not.toThrow();
        });
    });

    describe('LLM Stream Message Handling', () => {
        it('should handle streaming with content', () => {
            const message = {
                type: 'llm-stream' as const,
                chatNoteId: 'chat-456',
                content: 'Streaming content here',
                done: false
            };

            expect(() => wsService.sendMessageToAllClients(message)).not.toThrow();
        });

        it('should handle streaming with thinking', () => {
            const message = {
                type: 'llm-stream' as const,
                chatNoteId: 'chat-789',
                thinking: 'AI is thinking...',
                done: false
            };

            expect(() => wsService.sendMessageToAllClients(message)).not.toThrow();
        });

        it('should handle streaming with tool execution', () => {
            const message = {
                type: 'llm-stream' as const,
                chatNoteId: 'chat-012',
                toolExecution: {
                    action: 'executing',
                    tool: 'test-tool',
                    toolCallId: 'tc-123'
                },
                done: false
            };

            expect(() => wsService.sendMessageToAllClients(message)).not.toThrow();
        });

        it('should handle streaming completion', () => {
            const message = {
                type: 'llm-stream' as const,
                chatNoteId: 'chat-345',
                done: true
            };

            expect(() => wsService.sendMessageToAllClients(message)).not.toThrow();
        });

        it('should handle streaming with error', () => {
            const message = {
                type: 'llm-stream' as const,
                chatNoteId: 'chat-678',
                error: 'Something went wrong',
                done: true
            };

            expect(() => wsService.sendMessageToAllClients(message)).not.toThrow();
        });
    });

    describe('Non-LLM Message Types', () => {
        it('should handle frontend-update messages', () => {
            const message = {
                type: 'frontend-update' as const,
                data: {
                    lastSyncedPush: 100,
                    entityChanges: []
                }
            };

            expect(() => wsService.sendMessageToAllClients(message)).not.toThrow();
        });

        it('should handle ping messages', () => {
            const message = {
                type: 'ping' as const
            };

            expect(() => wsService.sendMessageToAllClients(message)).not.toThrow();
        });

        it('should handle task progress messages', () => {
            const message = {
                type: 'task-progress' as const,
                taskId: 'task-123',
                progressCount: 50
            };

            expect(() => wsService.sendMessageToAllClients(message)).not.toThrow();
        });
    });
});