import { NativeEventEmitter, NativeModules } from 'react-native';
import SdkBle, {
  type BleDevice,
  type BleService,
  type BleCharacteristic,
  type BleScanOptions,
  type BleWriteOptions,
  type BleConnectionState,
} from './NativeSdkBle';

// Event types
export type BleEvent =
  | 'scanResult'
  | 'connected'
  | 'disconnected'
  | 'servicesDiscovered'
  | 'characteristicChanged'
  | 'error'
  | 'bluetoothStateChanged';

export interface BleScanResult {
  device: BleDevice;
}

export interface BleConnectedEvent {
  deviceId: string;
}

export interface BleDisconnectedEvent {
  deviceId: string;
  reason?: string;
}

export interface BleServicesDiscoveredEvent {
  deviceId: string;
  services: BleService[];
}

export interface BleCharacteristicChangedEvent {
  deviceId: string;
  serviceUuid: string;
  characteristicUuid: string;
  value: string;
}

export interface BleErrorEvent {
  code: string;
  message: string;
  deviceId?: string;
}

export interface BleBluetoothStateChangedEvent {
  state:
    | 'unknown'
    | 'resetting'
    | 'unsupported'
    | 'unauthorized'
    | 'poweredOff'
    | 'poweredOn';
}

export type BleEventData = {
  scanResult: BleScanResult;
  connected: BleConnectedEvent;
  disconnected: BleDisconnectedEvent;
  servicesDiscovered: BleServicesDiscoveredEvent;
  characteristicChanged: BleCharacteristicChangedEvent;
  error: BleErrorEvent;
  bluetoothStateChanged: BleBluetoothStateChangedEvent;
};

class BleManager {
  private eventEmitter: NativeEventEmitter;
  private listeners: Map<string, Set<Function>> = new Map();

  constructor() {
    this.eventEmitter = new NativeEventEmitter(NativeModules.SdkBle);
  }

  // Event handling
  on<T extends BleEvent>(
    event: T,
    listener: (data: BleEventData[T]) => void
  ): void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }

    const listeners = this.listeners.get(event)!;
    listeners.add(listener);

    // Create a wrapper function to handle the native event data
    const wrappedListener = (data: any) => {
      listener(data as BleEventData[T]);
    };

    this.eventEmitter.addListener(event, wrappedListener);
  }

  off<T extends BleEvent>(
    event: T,
    listener?: (data: BleEventData[T]) => void
  ): void {
    const listeners = this.listeners.get(event);
    if (!listeners) return;

    if (listener) {
      listeners.delete(listener);
      // For simplicity, remove all listeners and re-add the remaining ones
      this.eventEmitter.removeAllListeners(event);

      // Re-add remaining listeners
      for (const remainingListener of listeners) {
        const wrappedListener = (data: any) => {
          (remainingListener as any)(data);
        };
        this.eventEmitter.addListener(event, wrappedListener);
      }
    } else {
      // Remove all listeners for this event
      listeners.clear();
      this.eventEmitter.removeAllListeners(event);
    }

    // Clean up empty sets
    if (listeners.size === 0) {
      this.listeners.delete(event);
    }
  }

  // Scanning methods
  async startScan(options?: BleScanOptions): Promise<void> {
    return SdkBle.startScan(options);
  }

  async stopScan(): Promise<void> {
    return SdkBle.stopScan();
  }

  // Connection methods
  async connect(deviceId: string): Promise<void> {
    return SdkBle.connect(deviceId);
  }

  async disconnect(deviceId: string): Promise<void> {
    return SdkBle.disconnect(deviceId);
  }

  async isConnected(deviceId: string): Promise<boolean> {
    return SdkBle.isConnected(deviceId);
  }

  async getConnectedDevices(): Promise<BleDevice[]> {
    return SdkBle.getConnectedDevices();
  }

  // Service discovery
  async discoverServices(deviceId: string): Promise<BleService[]> {
    return SdkBle.discoverServices(deviceId);
  }

  // Characteristic operations
  async readCharacteristic(
    deviceId: string,
    serviceUuid: string,
    characteristicUuid: string
  ): Promise<string> {
    return SdkBle.readCharacteristic(deviceId, serviceUuid, characteristicUuid);
  }

  async writeCharacteristic(
    deviceId: string,
    serviceUuid: string,
    characteristicUuid: string,
    data: string,
    options?: BleWriteOptions
  ): Promise<void> {
    return SdkBle.writeCharacteristic(
      deviceId,
      serviceUuid,
      characteristicUuid,
      data,
      options
    );
  }

  async setNotify(
    deviceId: string,
    serviceUuid: string,
    characteristicUuid: string,
    enable: boolean
  ): Promise<void> {
    return SdkBle.setNotify(deviceId, serviceUuid, characteristicUuid, enable);
  }

  // Permissions and Bluetooth state
  async requestPermissions(): Promise<boolean> {
    return SdkBle.requestPermissions();
  }

  async isBluetoothEnabled(): Promise<boolean> {
    return SdkBle.isBluetoothEnabled();
  }

  async enableBluetooth(): Promise<void> {
    return SdkBle.enableBluetooth();
  }

  // Utility methods
  removeAllListeners(): void {
    for (const event of this.listeners.keys()) {
      this.eventEmitter.removeAllListeners(event);
    }
    this.listeners.clear();
  }

  // Infusion control methods
  async startInfusion(deviceId: string): Promise<void> {
    return SdkBle.startInfusion(deviceId);
  }

  async stopInfusion(deviceId: string): Promise<void> {
    return SdkBle.stopInfusion(deviceId);
  }

  async setInfusionLevel(deviceId: string, level: number): Promise<void> {
    return SdkBle.setInfusionLevel(deviceId, level);
  }

  // User role method
  async setUserRole(role: 'patient' | 'nurse' | 'engineer'): Promise<void> {
    return SdkBle.setUserRole(role);
  }
}

// Create singleton instance
const bleManager = new BleManager();

export default bleManager;

// Export types for external use
export type {
  BleDevice,
  BleService,
  BleCharacteristic,
  BleScanOptions,
  BleWriteOptions,
  BleConnectionState,
};
