/**
 * Setup WiFi Screen Tests
 * Tests WiFi network selection, batch sensor setup, and error handling
 */

import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import { useLocalSearchParams, router } from 'expo-router';
import SetupWiFiScreen from '../[id]/setup-wifi';
import { useBLE } from '@/contexts/BLEContext';
import { api } from '@/services/api';
import * as wifiPasswordStore from '@/services/wifiPasswordStore';

// Mock Alert
const mockAlert = jest.fn();
jest.mock('react-native/Libraries/Alert/Alert', () => ({
  alert: mockAlert,
}));

// Mock dependencies
jest.mock('expo-router', () => ({
  useLocalSearchParams: jest.fn(),
  router: {
    push: jest.fn(),
    back: jest.fn(),
    replace: jest.fn(),
  },
}));

jest.mock('@/contexts/BLEContext', () => ({
  useBLE: jest.fn(),
}));

jest.mock('@/services/api', () => ({
  api: {
    attachDeviceToBeneficiary: jest.fn(),
  },
}));

jest.mock('@/services/wifiPasswordStore', () => ({
  getAllWiFiPasswords: jest.fn(),
  saveWiFiPassword: jest.fn(),
  migrateFromAsyncStorage: jest.fn(),
}));

jest.mock('@/services/analytics', () => ({
  analytics: {
    trackSensorSetupStep: jest.fn(),
    trackSensorSetupComplete: jest.fn(),
    trackSensorSetupRetry: jest.fn(),
    trackSensorSetupSkip: jest.fn(),
    trackSensorSetupCancelled: jest.fn(),
  },
}));

jest.mock('expo-device', () => ({
  isDevice: false, // Simulate running in simulator
}));

describe('SetupWiFiScreen', () => {
  const mockDevices = [
    { id: 'device-1', name: 'WP_497_81a14c', mac: '81A14C', wellId: 497 },
    { id: 'device-2', name: 'WP_498_82b25d', mac: '82B25D', wellId: 498 },
  ];

  const mockWiFiNetworks = [
    { ssid: 'HomeNetwork', rssi: -45 },
    { ssid: 'GuestNetwork', rssi: -65 },
    { ssid: 'WeakSignal', rssi: -80 },
  ];

  const mockConnectDevice = jest.fn();
  const mockDisconnectDevice = jest.fn();
  const mockGetWiFiList = jest.fn();
  const mockSetWiFi = jest.fn();
  const mockRebootDevice = jest.fn();

  beforeEach(() => {
    jest.clearAllMocks();

    // Mock route params with serialized devices
    (useLocalSearchParams as jest.Mock).mockReturnValue({
      id: '1',
      devices: JSON.stringify(mockDevices),
    });

    // Mock BLE context
    (useBLE as jest.Mock).mockReturnValue({
      connectDevice: mockConnectDevice,
      disconnectDevice: mockDisconnectDevice,
      getWiFiList: mockGetWiFiList,
      setWiFi: mockSetWiFi,
      rebootDevice: mockRebootDevice,
    });

    // Mock successful BLE operations
    mockConnectDevice.mockResolvedValue(true);
    mockGetWiFiList.mockResolvedValue(mockWiFiNetworks);
    mockSetWiFi.mockResolvedValue(true);
    mockRebootDevice.mockResolvedValue(true);

    // Mock API
    (api.attachDeviceToBeneficiary as jest.Mock).mockResolvedValue({ ok: true });

    // Mock WiFi password store
    (wifiPasswordStore.getAllWiFiPasswords as jest.Mock).mockResolvedValue({});
    (wifiPasswordStore.saveWiFiPassword as jest.Mock).mockResolvedValue(undefined);
    (wifiPasswordStore.migrateFromAsyncStorage as jest.Mock).mockResolvedValue(undefined);
  });

  describe('WiFi Network Loading', () => {
    it('should display loading state while scanning for networks', async () => {
      mockConnectDevice.mockImplementation(() => new Promise(resolve => setTimeout(() => resolve(true), 100)));
      mockGetWiFiList.mockImplementation(() => new Promise(resolve => setTimeout(() => resolve(mockWiFiNetworks), 100)));

      const { getByText } = render(<SetupWiFiScreen />);

      expect(getByText('Scanning for WiFi networks...')).toBeTruthy();
    });

    it('should display WiFi networks after loading', async () => {
      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      expect(getByText('HomeNetwork')).toBeTruthy();
      expect(getByText('GuestNetwork')).toBeTruthy();
      expect(getByText('WeakSignal')).toBeTruthy();
    });

    it('should display network count in header', async () => {
      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      expect(getByText(/Available Networks \(3\)/)).toBeTruthy();
    });

    it('should display signal strength for each network', async () => {
      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      // Check for signal labels
      expect(getByText(/Excellent/)).toBeTruthy(); // -45 dBm
      expect(getByText(/Good/)).toBeTruthy(); // -65 dBm
      expect(getByText(/Weak/)).toBeTruthy(); // -80 dBm
    });
  });

  describe('Network Selection', () => {
    it('should show password input when network is selected', async () => {
      const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));

      expect(getByText('WiFi Password')).toBeTruthy();
      expect(getByPlaceholderText('Enter password')).toBeTruthy();
    });

    it('should auto-fill saved password for known network', async () => {
      (wifiPasswordStore.getAllWiFiPasswords as jest.Mock).mockResolvedValue({
        'HomeNetwork': 'savedPassword123',
      });

      const { getByText, queryByText, getByDisplayValue } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));

      await waitFor(() => {
        expect(getByDisplayValue('savedPassword123')).toBeTruthy();
      });
    });

    it('should show saved password icon for networks with stored passwords', async () => {
      (wifiPasswordStore.getAllWiFiPasswords as jest.Mock).mockResolvedValue({
        'HomeNetwork': 'savedPassword123',
      });

      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      // The key icon should be visible for HomeNetwork
      // This would require checking for the Ionicons component
    });

    it('should toggle password visibility', async () => {
      const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));

      const passwordInput = getByPlaceholderText('Enter password');
      fireEvent.changeText(passwordInput, 'mypassword');

      // Password should be hidden by default (secureTextEntry)
      expect(passwordInput.props.secureTextEntry).toBe(true);

      // Find and press the eye icon button to toggle visibility
      // This would need the toggle button to have a testID
    });
  });

  describe('Device Info Display', () => {
    it('should display single device info when one sensor selected', async () => {
      (useLocalSearchParams as jest.Mock).mockReturnValue({
        id: '1',
        devices: JSON.stringify([mockDevices[0]]),
      });

      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      expect(getByText('WP_497_81a14c')).toBeTruthy();
      expect(getByText('Well ID: 497')).toBeTruthy();
    });

    it('should display multiple device summary when multiple sensors selected', async () => {
      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      expect(getByText('2 Sensors Selected')).toBeTruthy();
    });
  });

  describe('Validation', () => {
    it('should disable connect button when no password entered', async () => {
      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));

      const connectButton = getByText(/Connect/);
      expect(connectButton.parent?.props.disabled || connectButton.props.disabled).toBeTruthy();
    });

    it('should enable connect button when password is entered', async () => {
      const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));
      fireEvent.changeText(getByPlaceholderText('Enter password'), 'validpassword');

      // Connect button should now be enabled
    });

    it('should show validation error for short password', async () => {
      const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));
      fireEvent.changeText(getByPlaceholderText('Enter password'), 'short');

      // Try to connect with short password
      fireEvent.press(getByText(/Connect/));

      await waitFor(() => {
        expect(mockAlert).toHaveBeenCalledWith(
          'Invalid WiFi Credentials',
          expect.stringContaining('8-63 characters')
        );
      });
    });
  });

  describe('Batch Setup Process', () => {
    it('should show progress screen during batch setup', async () => {
      const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));
      fireEvent.changeText(getByPlaceholderText('Enter password'), 'validpassword123');
      fireEvent.press(getByText(/Connect All/));

      await waitFor(() => {
        expect(getByText('Setting Up Sensors')).toBeTruthy();
      });
    });

    it('should show step progress for each sensor', async () => {
      const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));
      fireEvent.changeText(getByPlaceholderText('Enter password'), 'validpassword123');
      fireEvent.press(getByText(/Connect All/));

      await waitFor(() => {
        // Should show connecting steps
        expect(queryByText(/Connected|Connecting/)).toBeTruthy();
      });
    });
  });

  describe('Error Handling', () => {
    it('should show error when connection fails', async () => {
      mockConnectDevice.mockRejectedValue(new Error('Connection failed'));

      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        // Should show error alert
        expect(mockAlert).toHaveBeenCalled();
      });
    });

    it('should show retry and skip options on sensor failure', async () => {
      mockConnectDevice.mockResolvedValueOnce(true);
      mockSetWiFi.mockRejectedValueOnce(new Error('WiFi configuration failed'));

      const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));
      fireEvent.changeText(getByPlaceholderText('Enter password'), 'validpassword123');
      fireEvent.press(getByText(/Connect All/));

      // Should show error with retry/skip options
      await waitFor(() => {
        expect(queryByText(/Retry|Skip/)).toBeTruthy();
      }, { timeout: 10000 });
    });

    it('should handle API attachment error', async () => {
      (api.attachDeviceToBeneficiary as jest.Mock).mockResolvedValue({
        ok: false,
        error: { message: 'Device already attached to another beneficiary' },
      });

      // Setup and trigger batch process
      // Verify error is shown
    });
  });

  describe('Results Screen', () => {
    it('should show results after all sensors processed', async () => {
      const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));
      fireEvent.changeText(getByPlaceholderText('Enter password'), 'validpassword123');
      fireEvent.press(getByText(/Connect All/));

      // Wait for setup to complete
      await waitFor(() => {
        expect(queryByText('Setup Complete') || queryByText('Done')).toBeTruthy();
      }, { timeout: 15000 });
    });

    it('should show success count on results screen', async () => {
      // After successful setup, should show how many succeeded
    });

    it('should navigate to equipment screen when Done is pressed', async () => {
      // Complete setup and press Done
      // Verify router.replace is called with equipment route
    });
  });

  describe('Navigation', () => {
    it('should go back and disconnect when back button pressed', async () => {
      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      // Press back button (arrow)
      // Verify disconnectDevice is called for each device
      // Verify router.back is called
    });

    it('should show cancel confirmation during active setup', async () => {
      const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      fireEvent.press(getByText('HomeNetwork'));
      fireEvent.changeText(getByPlaceholderText('Enter password'), 'validpassword123');
      fireEvent.press(getByText(/Connect All/));

      // During setup, trying to cancel should show confirmation
    });
  });

  describe('Empty Network List', () => {
    it('should show empty state when no networks found', async () => {
      mockGetWiFiList.mockResolvedValue([]);

      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(queryByText('Scanning for WiFi networks...')).toBeNull();
      });

      expect(getByText('No WiFi networks found')).toBeTruthy();
      expect(getByText('Try Again')).toBeTruthy();
    });

    it('should refresh networks when Try Again is pressed', async () => {
      mockGetWiFiList.mockResolvedValueOnce([]).mockResolvedValueOnce(mockWiFiNetworks);

      const { getByText, queryByText } = render(<SetupWiFiScreen />);

      await waitFor(() => {
        expect(getByText('No WiFi networks found')).toBeTruthy();
      });

      fireEvent.press(getByText('Try Again'));

      await waitFor(() => {
        expect(getByText('HomeNetwork')).toBeTruthy();
      });
    });
  });
});

describe('WiFi Password Store Integration', () => {
  beforeEach(() => {
    jest.clearAllMocks();

    (useLocalSearchParams as jest.Mock).mockReturnValue({
      id: '1',
      devices: JSON.stringify([{ id: 'device-1', name: 'WP_497', mac: '81A14C', wellId: 497 }]),
    });

    (useBLE as jest.Mock).mockReturnValue({
      connectDevice: jest.fn().mockResolvedValue(true),
      disconnectDevice: jest.fn(),
      getWiFiList: jest.fn().mockResolvedValue([{ ssid: 'TestNetwork', rssi: -50 }]),
      setWiFi: jest.fn().mockResolvedValue(true),
      rebootDevice: jest.fn().mockResolvedValue(true),
    });

    (api.attachDeviceToBeneficiary as jest.Mock).mockResolvedValue({ ok: true });
    (wifiPasswordStore.migrateFromAsyncStorage as jest.Mock).mockResolvedValue(undefined);
  });

  it('should save password after successful setup', async () => {
    (wifiPasswordStore.getAllWiFiPasswords as jest.Mock).mockResolvedValue({});

    const { getByText, queryByText, getByPlaceholderText } = render(<SetupWiFiScreen />);

    await waitFor(() => {
      expect(queryByText('Scanning for WiFi networks...')).toBeNull();
    });

    fireEvent.press(getByText('TestNetwork'));
    fireEvent.changeText(getByPlaceholderText('Enter password'), 'newpassword123');
    fireEvent.press(getByText(/Connect/));

    await waitFor(() => {
      expect(wifiPasswordStore.saveWiFiPassword).toHaveBeenCalledWith('TestNetwork', 'newpassword123');
    });
  });

  it('should migrate passwords from AsyncStorage on mount', async () => {
    render(<SetupWiFiScreen />);

    await waitFor(() => {
      expect(wifiPasswordStore.migrateFromAsyncStorage).toHaveBeenCalled();
    });
  });
});
