// BLE Context - Global state for Bluetooth management

import React, { createContext, useContext, useState, useCallback, useEffect, ReactNode } from 'react';
import {
  bleManager,
  WPDevice,
  WiFiNetwork,
  WiFiStatus,
  isBLEAvailable,
  checkBLEReadiness,
  BulkOperationResult,
  BulkWiFiResult,
  ReconnectConfig,
  ReconnectState,
  BLEConnectionState,
} from '@/services/ble';
import { setOnLogoutBLECleanupCallback } from '@/services/api';
import { BleManager } from 'react-native-ble-plx';

interface BLEContextType {
  // State
  foundDevices: WPDevice[];
  isScanning: boolean;
  connectedDevices: Set<string>;
  isBLEAvailable: boolean;
  error: string | null;
  permissionError: boolean; // true if error is related to permissions
  reconnectingDevices: Set<string>; // Devices currently attempting to reconnect

  // Actions
  scanDevices: () => Promise<void>;
  stopScan: () => void;
  connectDevice: (deviceId: string) => Promise<boolean>;
  disconnectDevice: (deviceId: string) => Promise<void>;
  getWiFiList: (deviceId: string) => Promise<WiFiNetwork[]>;
  setWiFi: (deviceId: string, ssid: string, password: string) => Promise<boolean>;
  getCurrentWiFi: (deviceId: string) => Promise<WiFiStatus | null>;
  rebootDevice: (deviceId: string) => Promise<void>;
  cleanupBLE: () => Promise<void>;
  clearError: () => void;
  checkPermissions: () => Promise<boolean>; // Manual permission check with UI prompts

  // Bulk operations
  bulkDisconnect: (deviceIds: string[]) => Promise<BulkOperationResult[]>;
  bulkReboot: (deviceIds: string[]) => Promise<BulkOperationResult[]>;
  bulkSetWiFi: (
    devices: { id: string; name: string }[],
    ssid: string,
    password: string,
    onProgress?: (deviceId: string, status: 'connecting' | 'configuring' | 'rebooting' | 'success' | 'error', error?: string) => void
  ) => Promise<BulkWiFiResult[]>;

  // Reconnect functionality
  enableAutoReconnect: (deviceId: string, deviceName?: string) => void;
  disableAutoReconnect: (deviceId: string) => void;
  manualReconnect: (deviceId: string) => Promise<boolean>;
  cancelReconnect: (deviceId: string) => void;
  getReconnectState: (deviceId: string) => ReconnectState | undefined;
  setReconnectConfig: (config: Partial<ReconnectConfig>) => void;
  getConnectionState: (deviceId: string) => BLEConnectionState;
}

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

export function BLEProvider({ children }: { children: ReactNode }) {
  const [foundDevices, setFoundDevices] = useState<WPDevice[]>([]);
  const [isScanning, setIsScanning] = useState(false);
  const [connectedDevices, setConnectedDevices] = useState<Set<string>>(new Set());
  const [reconnectingDevices, setReconnectingDevices] = useState<Set<string>>(new Set());
  const [error, setError] = useState<string | null>(null);
  const [permissionError, setPermissionError] = useState(false);

  // Lazy BleManager instance for permission checks
  const [bleManagerInstance] = useState(() => new BleManager());

  const isPermissionError = (errorMessage: string): boolean => {
    const permissionKeywords = [
      'permission',
      'unauthorized',
      'not granted',
      'denied',
      'bluetooth is disabled',
      'enable it in settings',
    ];
    const lowerMessage = errorMessage.toLowerCase();
    return permissionKeywords.some(keyword => lowerMessage.includes(keyword));
  };

  const checkPermissions = useCallback(async (): Promise<boolean> => {
    try {
      setError(null);
      setPermissionError(false);
      const ready = await checkBLEReadiness(bleManagerInstance);
      return ready;
    } catch {
      const errorMsg = 'Failed to check Bluetooth permissions';
      setError(errorMsg);
      setPermissionError(true);
      return false;
    }
  }, [bleManagerInstance]);

  const scanDevices = useCallback(async () => {
    try {
      setError(null);
      setPermissionError(false);
      setIsScanning(true);
      const devices = await bleManager.scanDevices();
      // Sort by RSSI (strongest first)
      const sorted = devices.sort((a, b) => b.rssi - a.rssi);
      setFoundDevices(sorted);
    } catch (err: any) {
      const errorMsg = err.message || 'Failed to scan for devices';
      setError(errorMsg);
      setPermissionError(isPermissionError(errorMsg));
      throw err;
    } finally {
      setIsScanning(false);
    }
  }, []);

  const stopScan = useCallback(() => {
    bleManager.stopScan();
    setIsScanning(false);
  }, []);

  const connectDevice = useCallback(async (deviceId: string): Promise<boolean> => {
    try {
      setError(null);
      setPermissionError(false);
      const success = await bleManager.connectDevice(deviceId);
      if (success) {
        setConnectedDevices(prev => new Set(prev).add(deviceId));
      } else {
        // Connection failed but no exception - set user-friendly error
        const errorMsg = 'Could not connect to sensor. Please move closer and try again.';
        setError(errorMsg);
        setPermissionError(false);
      }
      return success;
    } catch (err: any) {
      const errorMsg = err.message || 'Failed to connect to device';
      setError(errorMsg);
      setPermissionError(isPermissionError(errorMsg));
      return false;
    }
  }, []);

  const disconnectDevice = useCallback(async (deviceId: string): Promise<void> => {
    try {
      await bleManager.disconnectDevice(deviceId);
      setConnectedDevices(prev => {
        const next = new Set(prev);
        next.delete(deviceId);
        return next;
      });
    } catch (err: any) {
      setError(err.message || 'Failed to disconnect device');
    }
  }, []);

  const getWiFiList = useCallback(async (deviceId: string): Promise<WiFiNetwork[]> => {
    try {
      setError(null);
      const networks = await bleManager.getWiFiList(deviceId);
      return networks;
    } catch (err: any) {
      setError(err.message || 'Failed to get WiFi networks');
      throw err;
    }
  }, []);

  const setWiFi = useCallback(
    async (deviceId: string, ssid: string, password: string): Promise<boolean> => {
      try {
        setError(null);
        return await bleManager.setWiFi(deviceId, ssid, password);
      } catch (err: any) {
        setError(err.message || 'Failed to configure WiFi');
        throw err;
      }
    },
    []
  );

  const getCurrentWiFi = useCallback(
    async (deviceId: string): Promise<WiFiStatus | null> => {
      try {
        setError(null);
        return await bleManager.getCurrentWiFi(deviceId);
      } catch (err: any) {
        setError(err.message || 'Failed to get current WiFi');
        throw err;
      }
    },
    []
  );

  const rebootDevice = useCallback(async (deviceId: string): Promise<void> => {
    try {
      setError(null);
      await bleManager.rebootDevice(deviceId);
      // Remove from connected devices
      setConnectedDevices(prev => {
        const next = new Set(prev);
        next.delete(deviceId);
        return next;
      });
    } catch (err: any) {
      setError(err.message || 'Failed to reboot device');
      throw err;
    }
  }, []);

  const clearError = useCallback(() => {
    setError(null);
    setPermissionError(false);
  }, []);

  const cleanupBLE = useCallback(async () => {
    try {

      // Stop any ongoing scan
      if (isScanning) {
        stopScan();
      }

      // Cleanup via bleManager (disconnects all devices)
      await bleManager.cleanup();

      // Clear context state
      setFoundDevices([]);
      setConnectedDevices(new Set());
      setError(null);

    } catch {
      // Don't throw - we want to allow logout to proceed even if BLE cleanup fails
    }
  }, [isScanning, stopScan]);

  // Bulk operations
  const bulkDisconnect = useCallback(async (deviceIds: string[]): Promise<BulkOperationResult[]> => {
    try {
      setError(null);
      setPermissionError(false);
      const results = await bleManager.bulkDisconnect(deviceIds);
      // Update connected devices set
      const successfulDisconnects = results.filter(r => r.success).map(r => r.deviceId);
      if (successfulDisconnects.length > 0) {
        setConnectedDevices(prev => {
          const next = new Set(prev);
          successfulDisconnects.forEach(id => next.delete(id));
          return next;
        });
      }
      return results;
    } catch (err: any) {
      const errorMsg = err.message || 'Bulk disconnect failed';
      setError(errorMsg);
      setPermissionError(isPermissionError(errorMsg));
      throw err;
    }
  }, []);

  const bulkReboot = useCallback(async (deviceIds: string[]): Promise<BulkOperationResult[]> => {
    try {
      setError(null);
      setPermissionError(false);
      const results = await bleManager.bulkReboot(deviceIds);
      // Devices that were rebooted are no longer connected
      const successfulReboots = results.filter(r => r.success).map(r => r.deviceId);
      if (successfulReboots.length > 0) {
        setConnectedDevices(prev => {
          const next = new Set(prev);
          successfulReboots.forEach(id => next.delete(id));
          return next;
        });
      }
      return results;
    } catch (err: any) {
      const errorMsg = err.message || 'Bulk reboot failed';
      setError(errorMsg);
      setPermissionError(isPermissionError(errorMsg));
      throw err;
    }
  }, []);

  const bulkSetWiFi = useCallback(async (
    devices: { id: string; name: string }[],
    ssid: string,
    password: string,
    onProgress?: (deviceId: string, status: 'connecting' | 'configuring' | 'rebooting' | 'success' | 'error', error?: string) => void
  ): Promise<BulkWiFiResult[]> => {
    try {
      setError(null);
      setPermissionError(false);
      const results = await bleManager.bulkSetWiFi(devices, ssid, password, onProgress);
      // Update connected devices - successful setups result in reboots (disconnected)
      const successfulSetups = results.filter(r => r.success).map(r => r.deviceId);
      if (successfulSetups.length > 0) {
        setConnectedDevices(prev => {
          const next = new Set(prev);
          successfulSetups.forEach(id => next.delete(id));
          return next;
        });
      }
      return results;
    } catch (err: any) {
      const errorMsg = err.message || 'Bulk WiFi configuration failed';
      setError(errorMsg);
      setPermissionError(isPermissionError(errorMsg));
      throw err;
    }
  }, []);

  // Reconnect functionality
  const enableAutoReconnect = useCallback((deviceId: string, deviceName?: string) => {
    bleManager.enableAutoReconnect(deviceId, deviceName);
  }, []);

  const disableAutoReconnect = useCallback((deviceId: string) => {
    bleManager.disableAutoReconnect(deviceId);
    setReconnectingDevices(prev => {
      const next = new Set(prev);
      next.delete(deviceId);
      return next;
    });
  }, []);

  const manualReconnect = useCallback(async (deviceId: string): Promise<boolean> => {
    try {
      setError(null);
      setPermissionError(false);
      setReconnectingDevices(prev => new Set(prev).add(deviceId));

      const success = await bleManager.manualReconnect(deviceId);

      if (success) {
        setConnectedDevices(prev => new Set(prev).add(deviceId));
      }

      setReconnectingDevices(prev => {
        const next = new Set(prev);
        next.delete(deviceId);
        return next;
      });

      return success;
    } catch (err: any) {
      const errorMsg = err.message || 'Reconnection failed';
      setError(errorMsg);
      setPermissionError(isPermissionError(errorMsg));
      setReconnectingDevices(prev => {
        const next = new Set(prev);
        next.delete(deviceId);
        return next;
      });
      return false;
    }
  }, []);

  const cancelReconnect = useCallback((deviceId: string) => {
    bleManager.cancelReconnect(deviceId);
    setReconnectingDevices(prev => {
      const next = new Set(prev);
      next.delete(deviceId);
      return next;
    });
  }, []);

  const getReconnectState = useCallback((deviceId: string): ReconnectState | undefined => {
    return bleManager.getReconnectState(deviceId);
  }, []);

  const setReconnectConfig = useCallback((config: Partial<ReconnectConfig>) => {
    bleManager.setReconnectConfig(config);
  }, []);

  const getConnectionState = useCallback((deviceId: string): BLEConnectionState => {
    return bleManager.getConnectionState(deviceId);
  }, []);

  // Register BLE cleanup callback for logout
  useEffect(() => {
    setOnLogoutBLECleanupCallback(cleanupBLE);

    return () => {
      setOnLogoutBLECleanupCallback(null);
    };
  }, [cleanupBLE]);

  // Set up BLE event listeners for connection state changes
  useEffect(() => {
    const handleBLEEvent = (deviceId: string, event: string, data?: any) => {
      switch (event) {
        case 'disconnected':
          // Device unexpectedly disconnected
          setConnectedDevices(prev => {
            const next = new Set(prev);
            next.delete(deviceId);
            return next;
          });
          // Check if auto-reconnect is in progress
          if (data?.unexpected) {
            const reconnectState = bleManager.getReconnectState(deviceId);
            if (reconnectState?.isReconnecting) {
              setReconnectingDevices(prev => new Set(prev).add(deviceId));
            }
          }
          break;

        case 'ready':
          // Device connected or reconnected
          setConnectedDevices(prev => new Set(prev).add(deviceId));
          setReconnectingDevices(prev => {
            const next = new Set(prev);
            next.delete(deviceId);
            return next;
          });
          break;

        case 'connection_failed':
          // Check if max reconnect attempts reached
          if (data?.reconnectFailed) {
            setReconnectingDevices(prev => {
              const next = new Set(prev);
              next.delete(deviceId);
              return next;
            });
            setError(`Failed to reconnect to device after multiple attempts`);
          }
          break;

        case 'state_changed':
          if (data?.reconnecting) {
            setReconnectingDevices(prev => new Set(prev).add(deviceId));
          }
          break;
      }
    };

    bleManager.addEventListener(handleBLEEvent);

    return () => {
      bleManager.removeEventListener(handleBLEEvent);
    };
  }, []);

  const value: BLEContextType = {
    foundDevices,
    isScanning,
    connectedDevices,
    reconnectingDevices,
    isBLEAvailable,
    error,
    permissionError,
    scanDevices,
    stopScan,
    connectDevice,
    disconnectDevice,
    getWiFiList,
    setWiFi,
    getCurrentWiFi,
    rebootDevice,
    cleanupBLE,
    clearError,
    checkPermissions,
    bulkDisconnect,
    bulkReboot,
    bulkSetWiFi,
    // Reconnect functionality
    enableAutoReconnect,
    disableAutoReconnect,
    manualReconnect,
    cancelReconnect,
    getReconnectState,
    setReconnectConfig,
    getConnectionState,
  };

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

export function useBLE() {
  const context = useContext(BLEContext);
  if (context === undefined) {
    throw new Error('useBLE must be used within a BLEProvider');
  }
  return context;
}
