import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import Dashboard from '../pages/Dashboard';
import { 
  transformSensorDataToChartData, 
  transformSensorDataToCardData,
  validateChartData,
  createDefaultChartData,
  createDefaultCardData
} from '../utils/dataTransformers';
import { useDevices } from '../hooks/useDevices';
import { useSensorData } from '../hooks/useSensorData';

// Mock the hooks
jest.mock('../hooks/useDevices');
jest.mock('../hooks/useSensorData');

const mockUseDevices = useDevices as jest.MockedFunction<typeof useDevices>;
const mockUseSensorData = useSensorData as jest.MockedFunction<typeof useSensorData>;

// Mock Recharts components
jest.mock('recharts', () => ({
  LineChart: ({ children, data }: any) => (
    <div data-testid="line-chart" data-chart-data={JSON.stringify(data)}>
      {children}
    </div>
  ),
  Line: ({ dataKey }: any) => (
    <div data-testid={`line-${dataKey}`}>Line: {dataKey}</div>
  ),
  XAxis: () => <div data-testid="x-axis">XAxis</div>,
  YAxis: () => <div data-testid="y-axis">YAxis</div>,
  CartesianGrid: () => <div data-testid="cartesian-grid">CartesianGrid</div>,
  Tooltip: () => <div data-testid="tooltip">Tooltip</div>,
  Legend: () => <div data-testid="legend">Legend</div>,
  ResponsiveContainer: ({ children }: any) => (
    <div data-testid="responsive-container">{children}</div>
  ),
}));

const renderWithRouter = (component: React.ReactElement) => {
  return render(
    <BrowserRouter>
      {component}
    </BrowserRouter>
  );
};

describe('Dashboard 데이터 변환 테스트', () => {
  const mockDevices = [
    {
      id: 1,
      device_id: 'sensor-001',
      name: '테스트 센서',
      status: 'active',
      last_seen: '2025-08-01T10:00:00Z'
    }
  ];

  beforeEach(() => {
    jest.clearAllMocks();
    
    // 기본 mock 설정
    mockUseDevices.mockReturnValue({
      devices: mockDevices,
      loading: false,
      error: null,
      refetch: jest.fn()
    });

    mockUseSensorData.mockReturnValue({
      latestData: [],
      loading: false,
      error: null,
      refetch: jest.fn()
    });
  });

  describe('데이터 변환 유틸리티 함수 테스트', () => {
    it('transformSensorDataToChartData가 빈 배열을 처리한다', () => {
      const result = transformSensorDataToChartData([]);
      expect(result).toEqual(createDefaultChartData());
    });

    it('transformSensorDataToChartData가 null 데이터를 처리한다', () => {
      const result = transformSensorDataToChartData(null as any);
      expect(result).toEqual(createDefaultChartData());
    });

    it('transformSensorDataToChartData가 유효한 데이터를 변환한다', () => {
      const validData = [
        {
          device_id: 'sensor-001',
          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 result = transformSensorDataToChartData(validData);
      
      expect(result).toHaveLength(1);
      expect(result[0].device_id).toBe('sensor-001');
      expect(result[0].temperature).toBe(25.5);
      expect(result[0].humidity).toBe(60.0);
    });

    it('transformSensorDataToChartData가 잘못된 데이터를 필터링한다', () => {
      const invalidData = [
        { invalid_key: 'test' }, // device_id가 없음
        { device_id: 'sensor-001', temperature: 'invalid' }, // 숫자가 아닌 값
        { device_id: 'sensor-002', temperature: 25.5, humidity: 60.0 } // 유효한 데이터
      ];

      const result = transformSensorDataToChartData(invalidData);
      
      // 유효한 데이터만 처리됨
      expect(result).toHaveLength(1);
      expect(result[0].device_id).toBe('sensor-002');
    });

    it('transformSensorDataToCardData가 null 데이터를 처리한다', () => {
      const result = transformSensorDataToCardData(null);
      expect(result).toEqual(createDefaultCardData());
    });

    it('transformSensorDataToCardData가 유효한 데이터를 변환한다', () => {
      const validData = {
        device_id: 'sensor-001',
        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 result = transformSensorDataToCardData(validData);
      
      expect(result).toHaveLength(10); // 모든 센서 타입
      expect(result[0].title).toBe('현재 온도');
      expect(result[0].value).toBe(25.5);
      expect(result[0].unit).toBe('°C');
    });

    it('validateChartData가 유효한 데이터를 검증한다', () => {
      const validData = createDefaultChartData();
      expect(validateChartData(validData)).toBe(true);
    });

    it('validateChartData가 잘못된 데이터를 감지한다', () => {
      const invalidData = [
        { device_id: 'test' } // 필수 필드 누락
      ] as any;
      
      expect(validateChartData(invalidData)).toBe(false);
    });
  });

  describe('Dashboard 컴포넌트 데이터 변환 테스트', () => {
    it('빈 데이터 시 기본 카드들이 렌더링된다', () => {
      mockUseSensorData.mockReturnValue({
        latestData: [],
        loading: false,
        error: null,
        refetch: jest.fn()
      });

      renderWithRouter(<Dashboard />);

      // 기본 센서 카드들이 렌더링되는지 확인
      expect(screen.getByText('현재 온도')).toBeInTheDocument();
      expect(screen.getByText('현재 습도')).toBeInTheDocument();
      expect(screen.getByText('PM10')).toBeInTheDocument();
      expect(screen.getByText('PM2.5')).toBeInTheDocument();
      expect(screen.getByText('기압')).toBeInTheDocument();
      expect(screen.getByText('조도')).toBeInTheDocument();
      expect(screen.getByText('TVOC')).toBeInTheDocument();
      expect(screen.getByText('CO2')).toBeInTheDocument();
      expect(screen.getByText('O2')).toBeInTheDocument();
      expect(screen.getByText('CO')).toBeInTheDocument();
    });

    it('유효한 센서 데이터가 올바르게 변환되어 표시된다', async () => {
      const validData = [
        {
          device_id: 'sensor-001',
          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'
        }
      ];

      mockUseSensorData.mockReturnValue({
        latestData: validData,
        loading: false,
        error: null,
        refetch: jest.fn()
      });

      renderWithRouter(<Dashboard />);

      // 변환된 값들이 올바르게 표시되는지 확인
      await waitFor(() => {
        expect(screen.getByText('25.5°C')).toBeInTheDocument();
        expect(screen.getByText('60.0%')).toBeInTheDocument();
        expect(screen.getByText('15.2μg/m³')).toBeInTheDocument();
        expect(screen.getByText('8.1μg/m³')).toBeInTheDocument();
        expect(screen.getByText('1013.3hPa')).toBeInTheDocument();
        expect(screen.getByText('500lux')).toBeInTheDocument();
        expect(screen.getByText('120ppb')).toBeInTheDocument();
        expect(screen.getByText('450ppm')).toBeInTheDocument();
        expect(screen.getByText('20.9%')).toBeInTheDocument();
        expect(screen.getByText('0.5ppm')).toBeInTheDocument();
      });
    });

    it('잘못된 데이터가 포함된 경우 유효한 데이터만 처리한다', async () => {
      const mixedData = [
        {
          device_id: 'sensor-001',
          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'
        },
        {
          device_id: 'sensor-002',
          temperature: null,
          humidity: undefined,
          pm10: NaN,
          pm25: Infinity,
          pressure: 'invalid',
          illumination: 500,
          tvoc: 120,
          co2: 450,
          o2: 20.9,
          co: 0.5,
          recorded_time: '2025-08-01T10:00:00Z'
        }
      ];

      mockUseSensorData.mockReturnValue({
        latestData: mixedData,
        loading: false,
        error: null,
        refetch: jest.fn()
      });

      renderWithRouter(<Dashboard />);

      // 첫 번째 센서의 유효한 데이터가 표시되는지 확인
      await waitFor(() => {
        expect(screen.getByText('25.5°C')).toBeInTheDocument();
        expect(screen.getByText('60.0%')).toBeInTheDocument();
        expect(screen.getByText('15.2μg/m³')).toBeInTheDocument();
        expect(screen.getByText('8.1μg/m³')).toBeInTheDocument();
      });
    });

    it('차트 데이터가 올바르게 변환되어 렌더링된다', async () => {
      const validData = [
        {
          device_id: 'sensor-001',
          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'
        }
      ];

      mockUseSensorData.mockReturnValue({
        latestData: validData,
        loading: false,
        error: null,
        refetch: jest.fn()
      });

      renderWithRouter(<Dashboard />);

      // 차트들이 렌더링되는지 확인 (여러 개의 차트가 있음)
      await waitFor(() => {
        const charts = screen.getAllByTestId('line-chart');
        expect(charts.length).toBeGreaterThan(0);
      });

      // 첫 번째 차트의 데이터가 올바르게 변환되었는지 확인
      const charts = screen.getAllByTestId('line-chart');
      const firstChartData = charts[0].getAttribute('data-chart-data');
      const parsedData = JSON.parse(firstChartData || '[]');
      
      expect(parsedData).toHaveLength(1);
      expect(parsedData[0].device_id).toBe('sensor-001');
      expect(parsedData[0].temperature).toBe(25.5);
      expect(parsedData[0].humidity).toBe(60.0);
    });
  });

  describe('데이터 변환 오류 처리 테스트', () => {
    it('데이터 변환 중 오류가 발생하면 기본 데이터를 사용한다', async () => {
      // 잘못된 데이터 구조로 오류 유발
      const invalidData = [
        {
          device_id: 'sensor-001',
          temperature: 'invalid_string', // 숫자가 아닌 값
          humidity: null,
          pm10: undefined,
          pm25: NaN,
          pressure: Infinity,
          illumination: -Infinity,
          tvoc: 'not_a_number',
          co2: {},
          o2: [],
          co: () => {},
          recorded_time: 123 // 문자열이어야 함
        }
      ];

      mockUseSensorData.mockReturnValue({
        latestData: invalidData,
        loading: false,
        error: null,
        refetch: jest.fn()
      });

      renderWithRouter(<Dashboard />);

      // 기본값들이 표시되는지 확인
      await waitFor(() => {
        expect(screen.getByText('0.0°C')).toBeInTheDocument();
        expect(screen.getByText('0.0%')).toBeInTheDocument();
        expect(screen.getByText('0.0μg/m³')).toBeInTheDocument();
      });
    });

    it('빈 배열이 전달되면 기본 차트 데이터가 사용된다', async () => {
      mockUseSensorData.mockReturnValue({
        latestData: [],
        loading: false,
        error: null,
        refetch: jest.fn()
      });

      renderWithRouter(<Dashboard />);

      await waitFor(() => {
        const charts = screen.getAllByTestId('line-chart');
        expect(charts.length).toBeGreaterThan(0);
      });

      const charts = screen.getAllByTestId('line-chart');
      const firstChartData = charts[0].getAttribute('data-chart-data');
      const parsedData = JSON.parse(firstChartData || '[]');
      
      expect(parsedData).toEqual(createDefaultChartData());
    });
  });
}); 