// @vitest-environment jsdom
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { act, renderHook, waitFor } from '@testing-library/react';
import { useWebSocket } from './useWebSocket';

const { getSessionMock, ioMock } = vi.hoisted(() => ({
    getSessionMock: vi.fn(),
    ioMock: vi.fn()
}));

vi.mock('../lib/supabaseClient', () => ({
    supabase: {
        auth: {
            getSession: getSessionMock
        }
    }
}));

vi.mock('socket.io-client', () => ({
    io: ioMock
}));

describe('useWebSocket DOM/event behavior', () => {
    const handlers: Record<string, (...args: any[]) => void> = {};
    let socketStub: any;

    beforeEach(() => {
        Object.keys(handlers).forEach((key) => delete handlers[key]);
        getSessionMock.mockReset();
        ioMock.mockReset();

        socketStub = {
            on: vi.fn((event: string, callback: (...args: any[]) => void) => {
                handlers[event] = callback;
                return socketStub;
            }),
            close: vi.fn()
        };
        ioMock.mockReturnValue(socketStub);
    });

    it('skips socket connection when there is no auth session token', async () => {
        getSessionMock.mockResolvedValue({ data: { session: null } });

        const { result } = renderHook(() => useWebSocket('http://localhost:5000'));

        await waitFor(() => {
            expect(getSessionMock).toHaveBeenCalledTimes(1);
        });

        expect(ioMock).not.toHaveBeenCalled();
        expect(result.current.connected).toBe(false);
        expect(result.current.botState.settings.executionMode).toBe('Alerts');
    });

    it('connects with token and applies websocket event updates', async () => {
        getSessionMock.mockResolvedValue({ data: { session: { access_token: 'token-abc' } } });

        const { result, unmount } = renderHook(() => useWebSocket('http://localhost:5000'));

        await waitFor(() => {
            expect(ioMock).toHaveBeenCalledTimes(1);
        });

        const options = ioMock.mock.calls[0][1];
        expect(options.auth.token).toBe('token-abc');
        expect(options.transports).toEqual(['polling', 'websocket']);

        act(() => handlers.connect());
        expect(result.current.connected).toBe(true);

        act(() => handlers.state({
            symbols: {},
            alerts: [],
            positions: [],
            orders: [],
            history: [],
            settings: {
                executionMode: 'Paper',
                riskPerTrade: 0.02,
                totalCapital: 2000,
                maxOpenTrades: 2,
                isAlgoEnabled: true,
                enabledRules: ['TrendBiasRule']
            },
            uptime: 10
        }));
        expect(result.current.botState.settings.executionMode).toBe('Paper');
        expect(result.current.botState.settings.isAlgoEnabled).toBe(true);

        act(() => handlers.symbol_update({
            symbol: 'BTC/USD',
            data: {
                price: 70000,
                change24h: 1.2,
                changeToday: 0.4,
                session: 'NY',
                volatility: 'High',
                signal: 'BUY',
                priceHistory: [],
                rules: {},
                indicators: {}
            }
        }));
        expect(result.current.botState.symbols['BTC/USD'].price).toBe(70000);

        act(() => handlers.new_alert({
            timestamp: 1,
            type: 'info',
            symbol: 'BTC/USD',
            message: 'alert'
        }));
        expect(result.current.botState.alerts).toHaveLength(1);

        act(() => handlers.positions_update([{
            id: 'pos-1',
            symbol: 'BTC/USD',
            side: 'BUY',
            size: 1,
            entryPrice: 70000,
            currentPrice: 70100,
            stopLoss: 69000,
            takeProfit: 71000,
            unrealizedPnl: 100,
            unrealizedPnlPercent: 0.14,
            marketValue: 70100
        }]));
        expect(result.current.botState.positions).toHaveLength(1);

        act(() => handlers.orders_update([{
            id: 'ord-1',
            symbol: 'BTC/USD',
            type: 'market',
            side: 'buy',
            qty: 1,
            price: 70000,
            status: 'filled',
            timestamp: 2
        }]));
        expect(result.current.botState.orders).toHaveLength(1);

        act(() => handlers.history_update({
            symbol: 'BTC/USD',
            side: 'BUY',
            entryPrice: 70000,
            exitPrice: 71000,
            size: 1,
            pnl: 1000,
            pnlPercent: 1.42,
            reason: 'tp',
            timestamp: 3
        }));
        expect(result.current.botState.history).toHaveLength(1);

        act(() => handlers.settings_update({
            executionMode: 'Live',
            riskPerTrade: 0.01,
            totalCapital: 5000,
            maxOpenTrades: 5,
            isAlgoEnabled: false,
            enabledRules: []
        }));
        expect(result.current.botState.settings.executionMode).toBe('Live');

        act(() => handlers.disconnect());
        expect(result.current.connected).toBe(false);

        unmount();
        expect(socketStub.close).toHaveBeenCalled();
    });
});
