import React, { createContext, useContext, useState, useEffect } from 'react';
import type {
  BLEDevice,
  CharacteristicNotification,
  DeviceConnectionState,
  UserRole,
} from '../types';
import bleMiddleware from '../services/BLEMiddleware';

/**
 * BLE Context State
 */
interface BLEContextState {
  // Connection state
  bluetoothEnabled: boolean;
  isScanning: boolean;
  discoveredDevices: BLEDevice[];
  connectedDevices: Map<string, DeviceConnectionState>;

  // Legacy single device support (for backward compatibility)
  connectedDeviceId: string | null;
  isInfusionRunning: boolean;
  ff01Notification: CharacteristicNotification | null;
  ff21Notification: CharacteristicNotification | null;
  ff31Notification: CharacteristicNotification | null;
  ff41Notification: CharacteristicNotification | null;
  ff02Notification: CharacteristicNotification | null;

  // Actions
  startScan: () => Promise<void>;
  stopScan: () => Promise<void>;
  connectToDevice: (device: BLEDevice) => Promise<void>;
  disconnectDevice: (deviceId: string) => Promise<void>;
  requestPermissions: () => Promise<void>;

  // Multi-device queries
  getDeviceState: (deviceId: string) => DeviceConnectionState | undefined;
  isDeviceConnected: (deviceId: string) => boolean;
  getConnectedDeviceIds: () => string[];

  // Infusion actions
  startInfusion: (deviceId: string) => Promise<void>;
  stopInfusion: (deviceId: string) => Promise<void>;
  setInfusionLevel: (deviceId: string, level: number) => Promise<void>;

  // Characteristic operations
  readCharacteristic: (
    deviceId: string,
    serviceUuid: string,
    characteristicUuid: string
  ) => Promise<any>;
  writeCharacteristic: (
    deviceId: string,
    serviceUuid: string,
    characteristicUuid: string,
    data: string,
    options?: { withResponse?: boolean }
  ) => Promise<void>;

  // User role management
  setUserRole: (role: UserRole) => void;
  getUserRole: () => UserRole;
}

const BLEContext = createContext<BLEContextState | undefined>(undefined);

/**
 * BLE Provider Component
 * Manages all BLE state and operations
 */
export const BLEProvider: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  const [bluetoothEnabled, setBluetoothEnabled] = useState(false);
  const [isScanning, setIsScanning] = useState(false);
  const [discoveredDevices, setDiscoveredDevices] = useState<BLEDevice[]>([]);
  const [connectedDevices, setConnectedDevices] = useState<
    Map<string, DeviceConnectionState>
  >(new Map());

  // Legacy single device support (tracks the first/primary connected device)
  const [connectedDeviceId, setConnectedDeviceId] = useState<string | null>(
    null
  );

  // Legacy notification states (for the primary device)
  const [ff01Notification, setFF01Notification] =
    useState<CharacteristicNotification | null>(null);
  const [ff21Notification, setFF21Notification] =
    useState<CharacteristicNotification | null>(null);
  const [ff31Notification, setFF31Notification] =
    useState<CharacteristicNotification | null>(null);
  const [ff41Notification, setFF41Notification] =
    useState<CharacteristicNotification | null>(null);
  const [ff02Notification, setFF02Notification] =
    useState<CharacteristicNotification | null>(null);

  // Legacy infusion state (for the primary device)
  const [isInfusionRunning, setIsInfusionRunning] = useState(false);

  useEffect(() => {
    const initialize = async () => {
      // Wait for BLE middleware to initialize first
      await bleMiddleware.waitForInitialization();

      // Then setup BLE and event listeners
      await setupBLE();
      setupEventListeners();
    };

    initialize();

    return () => {
      bleMiddleware.removeAllListeners();
    };
  }, []);

  const setupBLE = async () => {
    try {
      await requestPermissions();
      const enabled = await bleMiddleware.isBluetoothEnabled();
      setBluetoothEnabled(enabled);
    } catch (error) {
      console.error('Failed to setup BLE:', error);
    }
  };

  const requestPermissions = async () => {
    try {
      await bleMiddleware.requestPermissions();
    } catch (error) {
      console.error('Failed to request permissions:', error);
    }
  };

  const setupEventListeners = () => {
    // Scan result event
    bleMiddleware.on('scanResult', ({ device }: any) => {
      setDiscoveredDevices((prev) => {
        const exists = prev.find((d) => d.id === device.id);
        if (exists) {
          return prev.map((d) => (d.id === device.id ? device : d));
        }
        return [...prev, device];
      });
    });

    // Connected event
    bleMiddleware.on('connected', ({ deviceId }: any) => {
      setConnectedDevices((prev) => {
        const newMap = new Map(prev);
        newMap.set(deviceId, {
          deviceId,
          isConnected: true,
          isInfusionRunning: false,
          infusionLevel: null,
          notifications: {
            ff01: null,
            ff21: null,
            ff31: null,
            ff41: null,
            ff02: null,
          },
        });
        return newMap;
      });

      // Legacy: Set first connected device as primary
      setConnectedDeviceId((prev) => prev || deviceId);
      console.log('✅ Device connected:', deviceId);
    });

    // Disconnected event
    bleMiddleware.on('disconnected', ({ deviceId }: any) => {
      setConnectedDevices((prev) => {
        const newMap = new Map(prev);
        newMap.delete(deviceId);
        return newMap;
      });

      // Legacy: Update primary device
      setConnectedDeviceId((prev) => {
        if (prev === deviceId) {
          // If primary device disconnected, set new primary or null
          const remaining = Array.from(connectedDevices.keys()).filter(
            (id) => id !== deviceId
          );
          const newPrimary = remaining[0] || null;
          // Update legacy infusion state
          if (!newPrimary) {
            setIsInfusionRunning(false);
          }
          return newPrimary;
        }
        return prev;
      });

      console.log('❌ Device disconnected:', deviceId);
    });

    // Scan failed event
    bleMiddleware.on('BleManagerScanFailed', ({ error }: any) => {
      console.error('BLE Scan Failed:', error);
      setIsScanning(false);
    });

    // Bluetooth state changed event
    bleMiddleware.on('bluetoothStateChanged', ({ state }: any) => {
      setBluetoothEnabled(state === 'poweredOn');
    });

    // Characteristic changed event
    bleMiddleware.on('characteristicChanged', (event: any) => {
      console.log('📡 Characteristic Changed Event:', event);
      const { characteristicUuid, deviceId } = event;
      const uuidUpper = characteristicUuid?.toUpperCase();

      // Update device-specific notifications
      setConnectedDevices((prev) => {
        const newMap = new Map(prev);
        const deviceState = newMap.get(deviceId);
        if (deviceState) {
          const updatedState = { ...deviceState };
          if (uuidUpper === '0000FF01-0000-1000-8000-00805F9B34FB') {
            updatedState.notifications.ff01 = event;
          } else if (uuidUpper === '0000FF21-0000-1000-8000-00805F9B34FB') {
            updatedState.notifications.ff21 = event;
            console.log('✅ FF21 notification stored in device state');
          } else if (uuidUpper === '0000FF31-0000-1000-8000-00805F9B34FB') {
            updatedState.notifications.ff31 = event;
          } else if (uuidUpper === '0000FF41-0000-1000-8000-00805F9B34FB') {
            updatedState.notifications.ff41 = event;
          } else if (uuidUpper === '0000FF02-0000-1000-8000-00805F9B34FB') {
            updatedState.notifications.ff02 = event;
          }
          newMap.set(deviceId, updatedState);
        }
        return newMap;
      });

      // Legacy: Update global notifications using functional update to avoid stale closure
      setConnectedDeviceId((currentPrimaryDeviceId) => {
        console.log(`🔍 Comparing deviceId: ${deviceId} with primary: ${currentPrimaryDeviceId}`);
        
        // Check if this event is for the primary device
        if (deviceId === currentPrimaryDeviceId) {
          console.log('✅ This is the primary device, updating global notifications');
          
          if (uuidUpper === '0000FF01-0000-1000-8000-00805F9B34FB') {
            setFF01Notification(event);
          } else if (uuidUpper === '0000FF21-0000-1000-8000-00805F9B34FB') {
            setFF21Notification(event);
            console.log('✅ FF21 global notification updated!');
          } else if (uuidUpper === '0000FF31-0000-1000-8000-00805F9B34FB') {
            setFF31Notification(event);
          } else if (uuidUpper === '0000FF41-0000-1000-8000-00805F9B34FB') {
            setFF41Notification(event);
          } else if (uuidUpper === '0000FF02-0000-1000-8000-00805F9B34FB') {
            setFF02Notification(event);
          } else {
            console.log('⚠️ Unhandled characteristic:', characteristicUuid);
          }
        } else {
          console.log(`⚠️ Not primary device - skipping global update`);
        }
        
        return currentPrimaryDeviceId; // Return unchanged
      });
    });
  };

  const startScan = async () => {
    console.log('Starting device scan...');
    try {
      setDiscoveredDevices([]);
      setIsScanning(true);
      await bleMiddleware.startScan({ timeout: 10000, allowDuplicates: false });
    } catch (error) {
      console.error('Failed to start scan:', error);
      setIsScanning(false);
    }
  };

  const stopScan = async () => {
    try {
      await bleMiddleware.stopScan();
      setIsScanning(false);
    } catch (error) {
      console.error('Failed to stop scan:', error);
    }
  };

  const connectToDevice = async (device: BLEDevice) => {
    console.log('Connecting to device:', device);
    try {
      await bleMiddleware.connect(device.id);
      console.log('Connected successfully');
    } catch (error) {
      console.error('Failed to connect:', error);
    }
  };

  const disconnectDevice = async (deviceId: string) => {
    try {
      await bleMiddleware.disconnect(deviceId);
      // State will be updated in the 'disconnected' event handler
      console.log('Disconnected from device:', deviceId);
    } catch (error) {
      console.error('Failed to disconnect:', error);
    }
  };

  // Multi-device query helpers
  const getDeviceState = (
    deviceId: string
  ): DeviceConnectionState | undefined => {
    return connectedDevices.get(deviceId);
  };

  const isDeviceConnected = (deviceId: string): boolean => {
    return connectedDevices.has(deviceId);
  };

  const getConnectedDeviceIds = (): string[] => {
    return Array.from(connectedDevices.keys());
  };

  const startInfusion = async (deviceId: string) => {
    try {
      await bleMiddleware.startInfusion(deviceId);

      // Update device-specific state
      setConnectedDevices((prev) => {
        const newMap = new Map(prev);
        const deviceState = newMap.get(deviceId);
        if (deviceState) {
          newMap.set(deviceId, {
            ...deviceState,
            isInfusionRunning: true,
          });
        }
        return newMap;
      });

      // Legacy: Update global state if this is the primary device
      if (deviceId === connectedDeviceId) {
        setIsInfusionRunning(true);
      }

      console.log('✅ Infusion started successfully for device:', deviceId);
    } catch (error) {
      console.error('Failed to start infusion:', error);
      throw error;
    }
  };

  const stopInfusion = async (deviceId: string) => {
    try {
      await bleMiddleware.stopInfusion(deviceId);

      // Update device-specific state
      setConnectedDevices((prev) => {
        const newMap = new Map(prev);
        const deviceState = newMap.get(deviceId);
        if (deviceState) {
          newMap.set(deviceId, {
            ...deviceState,
            isInfusionRunning: false,
          });
        }
        return newMap;
      });

      // Legacy: Update global state if this is the primary device
      if (deviceId === connectedDeviceId) {
        setIsInfusionRunning(false);
      }

      console.log('✅ Infusion stopped successfully for device:', deviceId);
    } catch (error) {
      console.error('Failed to stop infusion:', error);
      throw error;
    }
  };

  const setInfusionLevel = async (deviceId: string, level: number) => {
    try {
      await bleMiddleware.setInfusionLevel(deviceId, level);

      // Update device-specific state
      setConnectedDevices((prev) => {
        const newMap = new Map(prev);
        const deviceState = newMap.get(deviceId);
        if (deviceState) {
          newMap.set(deviceId, {
            ...deviceState,
            infusionLevel: level,
          });
        }
        return newMap;
      });

      console.log(
        `✅ Infusion level set to ${level} successfully for device:`,
        deviceId
      );
    } catch (error) {
      console.error('Failed to set infusion level:', error);
      throw error;
    }
  };

  const readCharacteristic = async (
    deviceId: string,
    serviceUuid: string,
    characteristicUuid: string
  ) => {
    try {
      return await bleMiddleware.readCharacteristic(
        deviceId,
        serviceUuid,
        characteristicUuid
      );
    } catch (error) {
      console.error('Failed to read characteristic:', error);
      throw error;
    }
  };

  const writeCharacteristic = async (
    deviceId: string,
    serviceUuid: string,
    characteristicUuid: string,
    data: string,
    options?: { withResponse?: boolean }
  ) => {
    try {
      await bleMiddleware.writeCharacteristic(
        deviceId,
        serviceUuid,
        characteristicUuid,
        data,
        options
      );
    } catch (error) {
      console.error('Failed to write characteristic:', error);
      throw error;
    }
  };

  const setUserRole = (role: UserRole) => {
    bleMiddleware.setUserRole(role);
  };

  const getUserRole = (): UserRole => {
    return bleMiddleware.getUserRole();
  };

  const value: BLEContextState = {
    bluetoothEnabled,
    isScanning,
    discoveredDevices,
    connectedDevices,
    connectedDeviceId,
    isInfusionRunning,
    ff01Notification,
    ff21Notification,
    ff31Notification,
    ff41Notification,
    ff02Notification,
    startScan,
    stopScan,
    connectToDevice,
    disconnectDevice,
    requestPermissions,
    getDeviceState,
    isDeviceConnected,
    getConnectedDeviceIds,
    startInfusion,
    stopInfusion,
    setInfusionLevel,
    readCharacteristic,
    writeCharacteristic,
    setUserRole,
    getUserRole,
  };

  return <BLEContext.Provider value={value}>{children}</BLEContext.Provider>;
};

/**
 * Hook to access BLE context
 */
export const useBLEContext = (): BLEContextState => {
  const context = useContext(BLEContext);
  if (!context) {
    throw new Error('useBLEContext must be used within a BLEProvider');
  }
  return context;
};
