import { renderHook, waitFor } from '@testing-library/react';
import { useSensorData } from '../useSensorData';
import { getLatestData } from '../../services/api';

// Mock the API service
jest.mock('../../services/api');
const mockGetLatestData = getLatestData as jest.MockedFunction<typeof getLatestData>;

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

  it('should initialize with empty data and loading state', () => {
    const { result } = renderHook(() => useSensorData(['device1']));
    
    expect(result.current.latestData).toEqual([]);
    expect(result.current.loading).toBe(true);
    expect(result.current.error).toBeNull();
  });

  it('should fetch data successfully', async () => {
    const mockData = [
      {
        device_id: 'device1',
        temperature: 25.5,
        humidity: 60.0,
        pm10: 15.2,
        pm25: 8.1,
        pressure: 1013.25,
        illumination: 500,
        tvoc: 120,
        co2: 450,
        o2: 20.9,
        co: 0.5,
        recorded_time: '2025-08-01T10:00:00Z'
      }
    ];

    mockGetLatestData.mockResolvedValue(mockData[0]);

    const { result } = renderHook(() => useSensorData(['device1']));

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(result.current.latestData).toEqual(mockData);
    expect(result.current.error).toBeNull();
    expect(mockGetLatestData).toHaveBeenCalledWith('device1');
  });

  it('should handle API errors', async () => {
    const errorMessage = 'Failed to fetch data';
    mockGetLatestData.mockRejectedValue(new Error(errorMessage));

    const { result } = renderHook(() => useSensorData(['device1']));

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(result.current.error).toBe('센서 데이터를 불러오는 중 오류가 발생했습니다.');
    expect(result.current.latestData).toEqual([]);
  });

  it('should cache data and avoid duplicate requests', async () => {
    const mockData = {
      device_id: 'device1',
      temperature: 25.5,
      humidity: 60.0,
      pm10: 15.2,
      pm25: 8.1,
      pressure: 1013.25,
      illumination: 500,
      tvoc: 120,
      co2: 450,
      o2: 20.9,
      co: 0.5,
      recorded_time: '2025-08-01T10:00:00Z'
    };

    mockGetLatestData.mockResolvedValue(mockData);

    const { result, rerender } = renderHook(() => useSensorData(['device1']));

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    // Rerender with same device IDs
    rerender();

    // Should not make another API call due to caching
    expect(mockGetLatestData).toHaveBeenCalledTimes(1);
  });

  it('should handle multiple devices', async () => {
    const mockData1 = {
      device_id: 'device1',
      temperature: 25.5,
      humidity: 60.0,
      pm10: 15.2,
      pm25: 8.1,
      pressure: 1013.25,
      illumination: 500,
      tvoc: 120,
      co2: 450,
      o2: 20.9,
      co: 0.5,
      recorded_time: '2025-08-01T10:00:00Z'
    };

    const mockData2 = {
      device_id: 'device2',
      temperature: 26.0,
      humidity: 55.0,
      pm10: 12.0,
      pm25: 6.5,
      pressure: 1012.0,
      illumination: 600,
      tvoc: 100,
      co2: 420,
      o2: 21.0,
      co: 0.3,
      recorded_time: '2025-08-01T10:00:00Z'
    };

    mockGetLatestData
      .mockResolvedValueOnce(mockData1)
      .mockResolvedValueOnce(mockData2);

    const { result } = renderHook(() => useSensorData(['device1', 'device2']));

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(result.current.latestData).toHaveLength(2);
    expect(mockGetLatestData).toHaveBeenCalledTimes(2);
    expect(mockGetLatestData).toHaveBeenCalledWith('device1');
    expect(mockGetLatestData).toHaveBeenCalledWith('device2');
  });

  it('should handle empty device IDs array', () => {
    const { result } = renderHook(() => useSensorData([]));
    
    expect(result.current.latestData).toEqual([]);
    expect(result.current.loading).toBe(true);
    expect(mockGetLatestData).not.toHaveBeenCalled();
  });

  it('should provide refetch function', async () => {
    const mockData = {
      device_id: 'device1',
      temperature: 25.5,
      humidity: 60.0,
      pm10: 15.2,
      pm25: 8.1,
      pressure: 1013.25,
      illumination: 500,
      tvoc: 120,
      co2: 450,
      o2: 20.9,
      co: 0.5,
      recorded_time: '2025-08-01T10:00:00Z'
    };

    mockGetLatestData.mockResolvedValue(mockData);

    const { result } = renderHook(() => useSensorData(['device1']));

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    // Clear cache and refetch
    await result.current.refetch();

    expect(mockGetLatestData).toHaveBeenCalledTimes(2);
  });

  it('should handle partial failures gracefully', async () => {
    const mockData = {
      device_id: 'device1',
      temperature: 25.5,
      humidity: 60.0,
      pm10: 15.2,
      pm25: 8.1,
      pressure: 1013.25,
      illumination: 500,
      tvoc: 120,
      co2: 450,
      o2: 20.9,
      co: 0.5,
      recorded_time: '2025-08-01T10:00:00Z'
    };

    mockGetLatestData
      .mockResolvedValueOnce(mockData)
      .mockRejectedValueOnce(new Error('Device 2 failed'));

    const { result } = renderHook(() => useSensorData(['device1', 'device2']));

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    // Should still have data from device1 even if device2 failed
    expect(result.current.latestData).toHaveLength(1);
    expect(result.current.latestData[0].device_id).toBe('device1');
  });
}); 