/**
 * 실시간 업데이트 통합 테스트
 */
import { renderHook, act, waitFor } from '@testing-library/react';
import { useRealtimeCandles } from '../hooks/useRealtimeCandles';
import { marketSocket } from '../services/marketSocket';

// Mock marketSocket
jest.mock('../services/marketSocket', () => ({
  marketSocket: {
    connect: jest.fn().mockResolvedValue(undefined),
    subscribe: jest.fn().mockReturnValue(() => {}),
    unsubscribe: jest.fn(),
    onConnectionStatusChange: jest.fn().mockReturnValue(() => {}),
    onError: jest.fn().mockReturnValue(() => {}),
  },
}));

describe('useRealtimeCandles Hook', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should connect and subscribe when enabled', async () => {
    const { result } = renderHook(() =>
      useRealtimeCandles({
        market: 'KRW-BTC',
        interval: '1m',
        enabled: true,
      })
    );

    await waitFor(() => {
      expect(marketSocket.connect).toHaveBeenCalled();
      expect(marketSocket.subscribe).toHaveBeenCalledWith(
        'KRW-BTC',
        '1m',
        expect.any(Function)
      );
    });
  });

  it('should not connect when disabled', () => {
    renderHook(() =>
      useRealtimeCandles({
        market: 'KRW-BTC',
        interval: '1m',
        enabled: false,
      })
    );

    expect(marketSocket.connect).not.toHaveBeenCalled();
    expect(marketSocket.subscribe).not.toHaveBeenCalled();
  });

  it('should update candles on snapshot', async () => {
    const onUpdate = jest.fn();
    const { result } = renderHook(() =>
      useRealtimeCandles({
        market: 'KRW-BTC',
        interval: '1m',
        enabled: true,
        onUpdate,
      })
    );

    // Simulate snapshot update
    const mockUpdate = {
      market: 'KRW-BTC',
      interval: '1m',
      candles: [
        { time: '2026-03-07T01:00', open: 100, high: 110, low: 90, close: 105, volume: 1000 },
        { time: '2026-03-07T01:01', open: 105, high: 115, low: 95, close: 110, volume: 1200 },
      ],
      timestamp: Date.now(),
      type: 'snapshot' as const,
    };

    // Get the callback that was passed to subscribe
    const subscribeCall = (marketSocket.subscribe as jest.Mock).mock.calls[0];
    const callback = subscribeCall[2];

    act(() => {
      callback(mockUpdate);
    });

    await waitFor(() => {
      expect(result.current.candles).toHaveLength(2);
      expect(result.current.updateCount).toBe(1);
      expect(onUpdate).toHaveBeenCalledWith(mockUpdate.candles);
    });
  });

  it('should update existing candle on incremental update', async () => {
    const onUpdate = jest.fn();
    const { result } = renderHook(() =>
      useRealtimeCandles({
        market: 'KRW-BTC',
        interval: '1m',
        enabled: true,
        onUpdate,
      })
    );

    // Initial snapshot
    const snapshotUpdate = {
      market: 'KRW-BTC',
      interval: '1m',
      candles: [
        { time: '2026-03-07T01:00', open: 100, high: 110, low: 90, close: 105, volume: 1000 },
      ],
      timestamp: Date.now(),
      type: 'snapshot' as const,
    };

    const subscribeCall = (marketSocket.subscribe as jest.Mock).mock.calls[0];
    const callback = subscribeCall[2];

    act(() => {
      callback(snapshotUpdate);
    });

    // Incremental update (same time, different values)
    const incrementalUpdate = {
      market: 'KRW-BTC',
      interval: '1m',
      candles: [
        { time: '2026-03-07T01:00', open: 100, high: 115, low: 85, close: 108, volume: 1500 },
      ],
      timestamp: Date.now(),
      type: 'update' as const,
    };

    act(() => {
      callback(incrementalUpdate);
    });

    await waitFor(() => {
      expect(result.current.candles).toHaveLength(1);
      expect(result.current.candles[0].close).toBe(108);
      expect(result.current.candles[0].volume).toBe(1500);
      expect(result.current.updateCount).toBe(2);
    });
  });

  it('should add new candle on incremental update', async () => {
    const onUpdate = jest.fn();
    const { result } = renderHook(() =>
      useRealtimeCandles({
        market: 'KRW-BTC',
        interval: '1m',
        enabled: true,
        onUpdate,
      })
    );

    // Initial snapshot
    const snapshotUpdate = {
      market: 'KRW-BTC',
      interval: '1m',
      candles: [
        { time: '2026-03-07T01:00', open: 100, high: 110, low: 90, close: 105, volume: 1000 },
      ],
      timestamp: Date.now(),
      type: 'snapshot' as const,
    };

    const subscribeCall = (marketSocket.subscribe as jest.Mock).mock.calls[0];
    const callback = subscribeCall[2];

    act(() => {
      callback(snapshotUpdate);
    });

    // New candle
    const newCandleUpdate = {
      market: 'KRW-BTC',
      interval: '1m',
      candles: [
        { time: '2026-03-07T01:01', open: 105, high: 115, low: 95, close: 110, volume: 1200 },
      ],
      timestamp: Date.now(),
      type: 'update' as const,
    };

    act(() => {
      callback(newCandleUpdate);
    });

    await waitFor(() => {
      expect(result.current.candles).toHaveLength(2);
      expect(result.current.candles[1].time).toBe('2026-03-07T01:01');
      expect(result.current.updateCount).toBe(2);
    });
  });

  it('should cleanup on unmount', () => {
    const { unmount } = renderHook(() =>
      useRealtimeCandles({
        market: 'KRW-BTC',
        interval: '1m',
        enabled: true,
      })
    );

    const unsubscribeFn = jest.fn();
    (marketSocket.subscribe as jest.Mock).mockReturnValue(unsubscribeFn);

    unmount();

    // Note: In actual implementation, unsubscribe is called in cleanup
    // This test verifies the hook structure
  });
});

describe('Real-time Chart Integration', () => {
  it('should handle timeframe switching', async () => {
    const { result, rerender } = renderHook(
      ({ market, interval }) =>
        useRealtimeCandles({
          market,
          interval,
          enabled: true,
        }),
      {
        initialProps: { market: 'KRW-BTC', interval: '1m' },
      }
    );

    // Initial subscription
    expect(marketSocket.subscribe).toHaveBeenCalledWith(
      'KRW-BTC',
      '1m',
      expect.any(Function)
    );

    // Change interval
    rerender({ market: 'KRW-BTC', interval: '5m' });

    await waitFor(() => {
      // Should resubscribe with new interval
      expect(marketSocket.subscribe).toHaveBeenCalledWith(
        'KRW-BTC',
        '5m',
        expect.any(Function)
      );
    });
  });

  it('should handle market switching', async () => {
    const { result, rerender } = renderHook(
      ({ market, interval }) =>
        useRealtimeCandles({
          market,
          interval,
          enabled: true,
        }),
      {
        initialProps: { market: 'KRW-BTC', interval: '1m' },
      }
    );

    // Change market
    rerender({ market: 'KRW-ETH', interval: '1m' });

    await waitFor(() => {
      // Should resubscribe with new market
      expect(marketSocket.subscribe).toHaveBeenCalledWith(
        'KRW-ETH',
        '1m',
        expect.any(Function)
      );
    });
  });
});

describe('Indicator Recalculation', () => {
  it('should trigger indicator recalculation on candle update', async () => {
    // This test would require mocking the full CandlestickTestPage component
    // For now, we verify the hook behavior
    const onUpdate = jest.fn();
    
    const { result } = renderHook(() =>
      useRealtimeCandles({
        market: 'KRW-BTC',
        interval: '1m',
        enabled: true,
        onUpdate,
      })
    );

    const mockUpdate = {
      market: 'KRW-BTC',
      interval: '1m',
      candles: [
        { time: '2026-03-07T01:00', open: 100, high: 110, low: 90, close: 105, volume: 1000 },
      ],
      timestamp: Date.now(),
      type: 'update' as const,
    };

    const subscribeCall = (marketSocket.subscribe as jest.Mock).mock.calls[0];
    const callback = subscribeCall[2];

    act(() => {
      callback(mockUpdate);
    });

    await waitFor(() => {
      expect(onUpdate).toHaveBeenCalled();
      expect(onUpdate).toHaveBeenCalledWith(
        expect.arrayContaining([
          expect.objectContaining({
            time: '2026-03-07T01:00',
            close: 105,
          }),
        ])
      );
    });
  });
});
