import { Platform } from 'react-native'
import { BleManager, type Characteristic, type Device, type ScanOptions } from 'react-native-ble-plx'

import { BLE_UUIDS } from '../protocol/Constants'
import { type BleDevice, type BleError, ConnectionState, type ScanResult } from './types'

export class BleClient {
  private static instance: BleClient
  private manager: BleManager | null = null
  private connectedDevice: Device | null = null

  // Simple event system
  private listeners: Map<string, Set<Function>> = new Map()

  private constructor() {
    if (Platform.OS !== 'web') {
      this.manager = new BleManager()
    }
  }

  public static getInstance(): BleClient {
    if (!BleClient.instance) {
      BleClient.instance = new BleClient()
    }
    return BleClient.instance
  }

  public addListener(event: string, callback: Function) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set())
    }
    this.listeners.get(event)!.add(callback)
  }

  public removeListener(event: string, callback: Function) {
    if (this.listeners.has(event)) {
      this.listeners.get(event)!.delete(callback)
    }
  }

  public removeAllListeners() {
    this.listeners.clear()
    console.debug('All BLE event listeners cleared')
  }

  private emit(event: string, ...args: any[]) {
    if (this.listeners.has(event)) {
      this.listeners.get(event)!.forEach((cb) => cb(...args))
    }
  }

  public async startScan(
    serviceUUIDs: string[] | null = null,
    options: ScanOptions = {},
    onDeviceFound: (result: ScanResult) => void,
  ): Promise<void> {
    if (!this.manager) {
      console.warn('BLE not supported on web')
      return
    }
    const state = ((await this.manager.state()) || '').toString()
    if (!state.toLowerCase().includes('poweredon')) {
      throw new Error(`Bluetooth is not powered on. State: ${state}`)
    }

    this.manager.startDeviceScan(serviceUUIDs, options, (error, device) => {
      if (error) {
        this.emit('scanError', error)
        return
      }
      if (device) {
        onDeviceFound({
          device,
          rssi: device.rssi,
          localName: device.name,
        })
      }
    })
  }

  public async getConnectedDevices(serviceUUIDs: string[]): Promise<BleDevice[]> {
    if (!this.manager) return []
    try {
      const devices = await this.manager.connectedDevices(serviceUUIDs)
      return devices as BleDevice[]
    } catch (e) {
      throw this.normalizeError(e)
    }
  }

  public stopScan() {
    if (!this.manager) return
    this.manager.stopDeviceScan()
  }

  public async connect(deviceId: string, timeout: number = 30000): Promise<BleDevice> {
    if (!this.manager) throw new Error('BLE not supported on web')

    // 创建超时 Promise
    const timeoutPromise = new Promise<never>((_, reject) => {
      setTimeout(() => {
        reject(new Error(`Connection timeout after ${timeout / 1000}s`))
      }, timeout)
    })

    try {
      this.emit('connectionStateChange', { deviceId, state: ConnectionState.CONNECTING })

      // 使用 Promise.race 实现超时控制
      const connectPromise = this.manager.connectToDevice(deviceId, { autoConnect: false })
      let device = await Promise.race([connectPromise, timeoutPromise])

      if (!device) {
        throw new Error('Failed to connect: device is null')
      }

      // 连上后再单独请求 MTU（容错处理）

      if (Platform.OS === 'android') {
        try {
          if (typeof (device as any).requestMTU === 'function') {
            const newDevice = await (device as any).requestMTU?.(BLE_UUIDS.REQUEST_MTU)
            if (newDevice && typeof newDevice.mtu === 'number') {
              device = newDevice
            } else {
              console.warn('requestMTU did not return a device with MTU info, but continuing...')
            }
            await new Promise((resolve) => setTimeout(resolve, 500))
          } else {
            console.warn('requestMTU not supported on this platform/device, but continuing...')
          }
        } catch (mtuErr) {
          console.warn('requestMTU failed, continuing without MTU change', mtuErr)
        }
      }

      await device.discoverAllServicesAndCharacteristics()

      console.log('Connected to device with MTU = ', device.mtu)
      this.connectedDevice = await device.discoverAllServicesAndCharacteristics()
      this.emit('connectionStateChange', { deviceId, state: ConnectionState.CONNECTED })

      // Handle disconnection monitoring
      device.onDisconnected((error: any, disconnectedDevice?: Device) => {
        this.connectedDevice = null
        const id = disconnectedDevice?.id || device.id || deviceId
        this.emit('connectionStateChange', {
          deviceId: id,
          state: ConnectionState.DISCONNECTED,
        })
        this.emit('disconnected', disconnectedDevice || { id })
      })

      return this.connectedDevice
    } catch (error: any) {
      this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTED })
      throw this.normalizeError(error)
    }
  }

  async disconnect(): Promise<void> {
    try {
      if (this.connectedDevice) {
        console.log(`Disconnecting from ${this.connectedDevice.id}`)

        const deviceId = this.connectedDevice.id

        // ✅ 修复：先触发断开状态，然后清理
        this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTING })

        try {
          // ✅ 修复：安全断开，设置较短超时
          await Promise.race([
            this.connectedDevice.cancelConnection(),
            new Promise((_, reject) => setTimeout(() => reject(new Error('Disconnect timeout')), 5000)),
          ])
        } catch (disconnectError: any) {
          // 忽略断开连接过程中的预期错误
          const errorMsg = disconnectError?.message || String(disconnectError)
          console.debug('Disconnect operation completed with:', errorMsg)
        }

        // ✅ 修复：无论断开是否成功，都清理状态
        this.connectedDevice = null
        this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTED })

        console.log('Device disconnected successfully')
      }
    } catch (error: any) {
      const errorMsg = error?.message || String(error)
      console.error('Disconnect error:', errorMsg)

      // 即使出现错误，也要清理状态
      if (this.connectedDevice) {
        const deviceId = this.connectedDevice.id
        this.connectedDevice = null
        this.emit('connectionStateChange', { deviceId, state: ConnectionState.DISCONNECTED })
      }

      // 只在严重错误时抛出异常
      if (!this.isDisconnectionError(error)) {
        throw this.normalizeError(error)
      }
    }
  }

  public async read(deviceId: string, serviceUUID: string, characteristicUUID: string): Promise<string> {
    if (!this.manager) throw new Error('BLE not supported on web')
    try {
      const char = await this.manager.readCharacteristicForDevice(deviceId, serviceUUID, characteristicUUID)
      return char.value || '' // Base64 string
    } catch (e) {
      throw this.normalizeError(e)
    }
  }

  public async write(
    deviceId: string,
    serviceUUID: string,
    characteristicUUID: string,
    dataBase64: string,
    response: boolean = true,
  ): Promise<Characteristic> {
    if (!this.manager) throw new Error('BLE not supported on web')
    let result: Characteristic | null
    try {
      if (response) {
        result = await this.manager.writeCharacteristicWithResponseForDevice(
          deviceId,
          serviceUUID,
          characteristicUUID,
          dataBase64,
        )
      } else {
        result = await this.manager.writeCharacteristicWithoutResponseForDevice(
          deviceId,
          serviceUUID,
          characteristicUUID,
          dataBase64,
        )
      }
      return result
    } catch (e) {
      throw this.normalizeError(e)
    }
  }

  public async monitor(
    deviceId: string,
    serviceUUID: string,
    characteristicUUID: string,
    listener: (error: BleError | null, value: string | null) => void,
  ) {
    if (!this.manager) {
      listener({ message: 'BLE not supported on web', errorCode: -1, reason: 'platform_not_supported' }, null)
      return {
        remove: () => {},
      }
    }

    try {
      return this.manager.monitorCharacteristicForDevice(deviceId, serviceUUID, characteristicUUID, (error, char) => {
        if (error) {
          // ✅ 修复：安全处理错误，确保不传递 null/undefined 值
          const normalizedError = this.normalizeError(error)

          // 特别处理断开连接错误，避免崩溃
          if (this.isDisconnectionError(error)) {
            console.debug('Device disconnected during monitoring:', normalizedError.message)
          }

          listener(normalizedError, null)
        } else {
          listener(null, char?.value || null)
        }
      })
    } catch (error: any) {
      // ✅ 修复：捕获监听器设置时的异常
      const normalizedError = this.normalizeError(error)
      listener(normalizedError, null)
      return {
        remove: () => {},
      }
    }
  }

  public async requestMtu(deviceId: string, mtu: number): Promise<number> {
    if (!this.manager) return 23
    try {
      const device = await this.manager.requestMTUForDevice(deviceId, mtu)
      return device.mtu
    } catch (e) {
      console.warn('MTU negotiation failed', e)
      // iOS doesn't allow explicit MTU request usually, so we might ignore or return default
      return 23
    }
  }

  public getConnectedDevice(): Device | null {
    return this.connectedDevice
  }

  private normalizeError(e: any): BleError {
    console.log('Normalizing BLE error:', e)

    // ✅ 修复：安全处理错误对象，确保所有字段都有有效值
    const errorCode = typeof e?.errorCode === 'number' ? e.errorCode : -1
    const message =
      typeof e?.message === 'string' && e.message.trim()
        ? e.message
        : typeof e === 'string' && e.trim()
          ? e
          : 'Unknown BLE error'
    const reason = typeof e?.reason === 'string' && e.reason.trim() ? e.reason : 'unknown'

    return {
      errorCode,
      message,
      reason,
    }
  }

  private isDisconnectionError(error: any): boolean {
    const message = error?.message || ''
    const reason = error?.reason || ''

    return (
      message.toLowerCase().includes('disconnect') ||
      message.toLowerCase().includes('gatt_conn_terminate') ||
      reason.toLowerCase().includes('disconnect') ||
      error?.errorCode === 19
    ) // GATT_CONN_TERMINATE_PEER_USER
  }
}
