import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it, vi } from 'vitest';
import {
    DEFAULT_BOT_STATE,
    appendAlertUpdate,
    appendHistoryUpdate,
    applySymbolUpdate,
    replaceOrdersUpdate,
    replacePositionsUpdate,
    replaceSettingsUpdate,
    useWebSocket
} from './useWebSocket';

vi.mock('../lib/supabaseClient', () => ({
    supabase: {
        auth: {
            getSession: vi.fn(async () => ({ data: { session: null } }))
        }
    }
}));

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

describe('useWebSocket', () => {
    it('provides default bot state before any socket updates', () => {
        const Probe = () => {
            const { botState, connected } = useWebSocket('http://localhost:5000');
            return React.createElement(
                'div',
                null,
                `${botState.settings.executionMode}|${botState.settings.maxOpenTrades}|${connected ? '1' : '0'}|${Object.keys(botState.symbols).length}`
            );
        };

        const html = renderToStaticMarkup(React.createElement(Probe));
        expect(html).toContain('Alerts|3|0|0');
    });

    it('applies symbol updates without mutating previous symbols', () => {
        const prev = {
            ...DEFAULT_BOT_STATE,
            symbols: {
                'BTC/USD': {
                    price: 100,
                    change24h: 1,
                    changeToday: 0.5,
                    session: 'NY',
                    volatility: 'High',
                    signal: 'BUY',
                    priceHistory: [],
                    rules: {},
                    indicators: {}
                }
            }
        } as any;

        const next = applySymbolUpdate(prev, 'ETH/USD', {
            price: 200,
            change24h: -0.5,
            changeToday: 0.1,
            session: 'LDN',
            volatility: 'Med',
            signal: 'NONE',
            priceHistory: [],
            rules: {},
            indicators: {}
        } as any);

        expect(Object.keys(prev.symbols)).toEqual(['BTC/USD']);
        expect(Object.keys(next.symbols)).toEqual(['BTC/USD', 'ETH/USD']);
        expect(next.symbols['ETH/USD'].price).toBe(200);
    });

    it('appends alert and history updates in order', () => {
        const withAlert = appendAlertUpdate(DEFAULT_BOT_STATE, {
            timestamp: 1,
            type: 'info',
            symbol: 'BTC/USD',
            message: 'alert-1'
        });
        const withHistory = appendHistoryUpdate(withAlert, {
            symbol: 'BTC/USD',
            side: 'BUY',
            entryPrice: 100,
            exitPrice: 110,
            size: 1,
            pnl: 10,
            pnlPercent: 10,
            reason: 'tp',
            timestamp: 2
        });

        expect(withAlert.alerts).toHaveLength(1);
        expect(withAlert.alerts[0].message).toBe('alert-1');
        expect(withHistory.history).toHaveLength(1);
        expect(withHistory.history[0].pnl).toBe(10);
    });

    it('replaces positions, orders and settings atomically', () => {
        const withPositions = replacePositionsUpdate(DEFAULT_BOT_STATE, [{
            id: 'p1',
            symbol: 'BTC/USD',
            side: 'BUY',
            size: 1,
            entryPrice: 100,
            currentPrice: 101,
            stopLoss: 95,
            takeProfit: 110,
            unrealizedPnl: 1,
            unrealizedPnlPercent: 1,
            marketValue: 101
        }]);

        const withOrders = replaceOrdersUpdate(withPositions, [{
            id: 'o1',
            symbol: 'BTC/USD',
            type: 'market',
            side: 'buy',
            qty: 1,
            price: 100,
            status: 'filled',
            timestamp: 1
        }]);

        const withSettings = replaceSettingsUpdate(withOrders, {
            executionMode: 'Paper',
            riskPerTrade: 0.02,
            totalCapital: 5000,
            maxOpenTrades: 5,
            isAlgoEnabled: true,
            enabledRules: ['TrendBiasRule']
        });

        expect(withSettings.positions).toHaveLength(1);
        expect(withSettings.orders).toHaveLength(1);
        expect(withSettings.settings.executionMode).toBe('Paper');
        expect(withSettings.settings.isAlgoEnabled).toBe(true);
    });
});
