import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactNode } from 'react'
import { useRealtimeData } from '../useRealtimeData'

const mockSubscribe = vi.fn(() => vi.fn())
const mockShowInfo = vi.fn()
const mockShowWarning = vi.fn()

vi.mock('../../components/WebSocketProvider', () => ({
    useWebSocketContext: () => ({
        isConnected: true,
        lastMessage: null,
        subscribe: mockSubscribe,
    }),
}))

vi.mock('../../contexts/SnackbarContext', () => ({
    useSnackbar: () => ({
        showInfo: mockShowInfo,
        showWarning: mockShowWarning,
        showError: vi.fn(),
        showSuccess: vi.fn(),
    }),
}))

describe('useRealtimeData', () => {
    let queryClient: QueryClient

    beforeEach(() => {
        queryClient = new QueryClient({
            defaultOptions: {
                queries: { retry: false },
            },
        })
        vi.clearAllMocks()
    })

    const wrapper = ({ children }: { children: ReactNode }) => (
        <QueryClientProvider client= { queryClient } > { children } </QueryClientProvider>
  )

it('subscribes to all message types on mount', () => {
    renderHook(() => useRealtimeData(), { wrapper })

    expect(mockSubscribe).toHaveBeenCalledWith('order_update', expect.any(Function))
    expect(mockSubscribe).toHaveBeenCalledWith('position_update', expect.any(Function))
    expect(mockSubscribe).toHaveBeenCalledWith('price_update', expect.any(Function))
    expect(mockSubscribe).toHaveBeenCalledWith('alert_triggered', expect.any(Function))
    expect(mockSubscribe).toHaveBeenCalledWith('strategy_signal', expect.any(Function))
    expect(mockSubscribe).toHaveBeenCalledWith('system_event', expect.any(Function))
})

it('handles order update messages', async () => {
    const unsubscribeMock = vi.fn()
    let orderHandler: ((message: any) => void) | undefined

    mockSubscribe.mockImplementation((type: string, handler: any) => {
        if (type === 'order_update') {
            orderHandler = handler
        }
        return unsubscribeMock
    })

    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')

    renderHook(() => useRealtimeData(), { wrapper })

    // Simulate order filled message
    if (orderHandler) {
        orderHandler({ type: 'order_update', order_id: '123', status: 'filled' })
    }

    await waitFor(() => {
        expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['orders'] })
        expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['balance'] })
    })

    expect(mockShowInfo).toHaveBeenCalledWith('Order 123 filled')
})

it('handles alert triggered messages', async () => {
    let alertHandler: ((message: any) => void) | undefined

    mockSubscribe.mockImplementation((type: string, handler: any) => {
        if (type === 'alert_triggered') {
            alertHandler = handler
        }
        return vi.fn()
    })

    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')

    renderHook(() => useRealtimeData(), { wrapper })

    // Simulate alert triggered
    if (alertHandler) {
        alertHandler({ type: 'alert_triggered', alert_name: 'BTC Price Alert' })
    }

    await waitFor(() => {
        expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['alerts'] })
    })

    expect(mockShowWarning).toHaveBeenCalledWith('Alert triggered: BTC Price Alert')
})

it('unsubscribes from all message types on unmount', () => {
    const unsubscribeMocks = {
        order_update: vi.fn(),
        position_update: vi.fn(),
        price_update: vi.fn(),
        alert_triggered: vi.fn(),
        strategy_signal: vi.fn(),
        system_event: vi.fn(),
    }

    mockSubscribe.mockImplementation((type: string) => {
        return unsubscribeMocks[type as keyof typeof unsubscribeMocks] || vi.fn()
    })

    const { unmount } = renderHook(() => useRealtimeData(), { wrapper })

    unmount()

    expect(unsubscribeMocks.order_update).toHaveBeenCalled()
    expect(unsubscribeMocks.position_update).toHaveBeenCalled()
    expect(unsubscribeMocks.price_update).toHaveBeenCalled()
    expect(unsubscribeMocks.alert_triggered).toHaveBeenCalled()
    expect(unsubscribeMocks.strategy_signal).toHaveBeenCalled()
    expect(unsubscribeMocks.system_event).toHaveBeenCalled()
})
})
