// BLE connection: scan, connect, disconnect, reconnect logic

var protocol = require('./protocol')
var SERVICE = protocol.SERVICE
var CHAR = protocol.CHAR

// Shared state — accessed by commands.js via getters/setters
var _deviceId = null
var _connected = false
var _chars = {}
var _autoReconnect = true
var _reconnecting = false

// Event emitter — shared across modules
var _listeners = {}

function on(event, callback) {
  if (!_listeners[event]) _listeners[event] = []
  _listeners[event].push(callback)
}

function off(event, callback) {
  if (!_listeners[event]) return
  if (callback) {
    _listeners[event] = _listeners[event].filter(function (cb) { return cb !== callback })
  } else {
    _listeners[event] = []
  }
}

function emit(event, data) {
  if (!_listeners[event]) return
  _listeners[event].forEach(function (cb) {
    try { cb(data) } catch (e) { console.error('ble emit error:', e) }
  })
}

// --- state accessors (used by commands.js) ---

function getDeviceId() {
  return _deviceId
}

function isConnected() {
  return _connected && _deviceId !== null
}

function getChars() {
  return _chars
}

// --- notification handling ---

function handleNotification(frame) {
  var pendingAcks = require('./commands').getPendingAcks()
  switch (frame.type) {
    case protocol.NOTIFY.STATUS_REPORT:
      emit('status', protocol.parseStatusReport(frame.payload))
      break
    case protocol.NOTIFY.ACK:
      var ack = protocol.parseAck(frame.payload)
      emit('ack', ack)
      if (pendingAcks[ack.seq]) {
        if (ack.error_code === 0) {
          pendingAcks[ack.seq].resolve(ack)
        } else {
          pendingAcks[ack.seq].reject(ack)
        }
        delete pendingAcks[ack.seq]
      }
      break
    case protocol.NOTIFY.TREATMENT_COMPLETE:
      emit('treatment_complete', protocol.parseTreatmentComplete(frame.payload))
      break
    case protocol.NOTIFY.EXCEPTION:
      emit('exception', protocol.parseException(frame.payload))
      break
    case protocol.NOTIFY.BIND_SUCCESS:
      emit('bind_result', { success: frame.payload[0] === 0x00 })
      break
  }
}

function handleValueChange(res) {
  if (protocol.PROTOCOL_MODE === 'vendor_33') {
    var parsed = protocol.parseVendorStatus(res.value)
    console.log('[BLE] FFE4 收到', protocol.bufferToBytes(res.value).length + '字节', parsed.type, '| raw:', parsed.raw_hex)
    emit('vendor_raw', parsed)

    if (parsed.type === 'heartbeat') {
      emit('heartbeat', { value: parsed.heartbeat })
      return
    }

    if (parsed.type === 'adc') {
      var vbatMv = parsed.vbat
      var batteryPct = Math.min(100, Math.max(0, Math.round((vbatMv - 3000) / (4200 - 3000) * 100)))
      if (!parsed.checksum_ok) {
        console.warn('[BLE] ADC checksum mismatch, dropping')
        return
      }
      emit('adc', { pd: parsed.pd, vbat: vbatMv, battery: batteryPct, fitting: parsed.fitting, run_state: parsed.run_state })
      emit('battery', { battery: batteryPct, vbat: vbatMv })
      if (parsed.fitting !== -1) {
        emit('fitting', { fitting: parsed.fitting })
      }
      if (parsed.run_state !== -1) {
        emit('run_state', { run_state: parsed.run_state })
      }
      return
    }

    if (parsed.type === 'params' && parsed.ios) {
      var anyActive = false
      for (var i = 0; i < parsed.ios.length; i++) {
        if (parsed.ios[i].red || parsed.ios[i].infrared || parsed.ios[i].uv || parsed.ios[i].warm_yellow) {
          anyActive = true
          break
        }
      }
      emit('status', {
        mode_state: anyActive ? protocol.MODE_STATE.ACTIVE : protocol.MODE_STATE.IDLE,
        region_mask: protocol.REGION.FULL_FACE,
        wavelength: 0,
        brightness: 0,
        remaining_ms: parsed.hold_time > 0 ? parsed.hold_time * 1000 : 0,
        error_code: 0,
        battery: 0,
        temperature: 0,
        bind_status: 1,
        subscription: 1
      })
    }
    return
  }
  var frame = protocol.parseFrame(res.value)
  if (frame) {
    handleNotification(frame)
  }
}

// --- service/characteristic discovery ---

function discoverChars(deviceId, serviceId, group) {
  return new Promise(function (resolve) {
    wx.getBLEDeviceCharacteristics({
      deviceId: deviceId,
      serviceId: serviceId,
      success: function (res) {
        var chars = res.characteristics || []
        for (var i = 0; i < chars.length; i++) {
          var c = chars[i]
          var uuid = c.uuid.toUpperCase()
          var props = c.properties || {}
          if (uuid.indexOf(CHAR.DEVICE_INFO) !== -1) _chars.deviceInfo = { uuid: c.uuid, serviceId: serviceId }
          if (uuid.indexOf(CHAR.COMMAND) !== -1 && (props.write || props.writeNoResponse)) {
            _chars.command = { uuid: c.uuid, serviceId: serviceId }
          }
          if (uuid.indexOf(CHAR.STATUS) !== -1 && props.notify) {
            _chars.status = { uuid: c.uuid, serviceId: serviceId }
          }
          if (uuid.indexOf(CHAR.BOND_INFO) !== -1) _chars.bondInfo = { uuid: c.uuid, serviceId: serviceId }
          if (uuid.indexOf(CHAR.OTA_CONTROL) !== -1) _chars.otaControl = { uuid: c.uuid, serviceId: serviceId }
          if (uuid.indexOf(CHAR.OTA_DATA) !== -1) _chars.otaData = { uuid: c.uuid, serviceId: serviceId }
          if (uuid.indexOf(CHAR.OTA_STATUS) !== -1) _chars.otaStatus = { uuid: c.uuid, serviceId: serviceId }
        }
        resolve()
      },
      fail: function () { resolve() }
    })
  })
}

function subscribeToNotifications(deviceId, serviceId) {
  return new Promise(function (resolve) {
    if (!_chars.status) { resolve(); return }

    wx.notifyBLECharacteristicValueChange({
      deviceId: deviceId,
      serviceId: _chars.status.serviceId,
      characteristicId: _chars.status.uuid,
      state: true,
      success: function () {
        if (wx.offBLECharacteristicValueChange) {
          wx.offBLECharacteristicValueChange(handleValueChange)
        }
        wx.onBLECharacteristicValueChange(handleValueChange)
        resolve()
      },
      fail: function () { resolve() }
    })
  })
}

function requestMtu(deviceId) {
  return new Promise(function (resolve) {
    if (!wx.setBLEMTU) {
      resolve()
      return
    }
    wx.setBLEMTU({
      deviceId: deviceId,
      mtu: 64,
      complete: function () { resolve() }
    })
  })
}

function discoverServices(deviceId, callbacks) {
  wx.getBLEDeviceServices({
    deviceId: deviceId,
    success: function (res) {
      var services = res.services
      console.log('[BLE] 发现服务数量:', services.length)
      var serviceMap = {}
      for (var i = 0; i < services.length; i++) {
        var uuid = services[i].uuid.toUpperCase()
        console.log('[BLE] 服务:', uuid)
        if (uuid.indexOf(SERVICE.DEVICE_INFO) !== -1) {
          serviceMap.deviceInfo = services[i].uuid
        } else if (uuid.indexOf(SERVICE.DATA_COMM) !== -1) {
          serviceMap.dataComm = services[i].uuid
        } else if (uuid.indexOf(SERVICE.OTA) !== -1) {
          serviceMap.ota = services[i].uuid
        }
      }
      console.log('[BLE] 匹配服务:', JSON.stringify(serviceMap))

      var tasks = []
      if (serviceMap.deviceInfo) {
        tasks.push(discoverChars(deviceId, serviceMap.deviceInfo, 'deviceInfo'))
      }
      if (serviceMap.dataComm) {
        tasks.push(discoverChars(deviceId, serviceMap.dataComm, 'dataComm'))
      }

      Promise.all(tasks).then(function () {
        subscribeToNotifications(deviceId, serviceMap.dataComm).then(function () {
          if (callbacks.onConnected) callbacks.onConnected({ deviceId: deviceId })
        })
      })
    },
    fail: function () {
      if (callbacks.onError) callbacks.onError({ msg: '服务发现失败' })
    }
  })
}

// --- scan & connect ---

function startScan(callbacks) {
  var targetName = callbacks.targetDeviceId ? ('JW_' + callbacks.targetDeviceId).toUpperCase() : ''
  wx.openBluetoothAdapter({
    success: function () {
      wx.startBluetoothDevicesDiscovery({
        allowDuplicatesKey: false,
        success: function () {
          wx.offBluetoothDeviceFound()
          wx.onBluetoothDeviceFound(function (res) {
            var devices = res.devices || []
            for (var i = 0; i < devices.length; i++) {
              var d = devices[i]
              var name = (d.name || '').toUpperCase()
              var localName = (d.localName || '').toUpperCase()
              if (!name && !localName) continue
              console.log('[BLE] 发现设备:', d.name, '| localName:', d.localName, '| deviceId:', d.deviceId)
              var matched = false
              if (targetName) {
                matched = name.indexOf(targetName) !== -1 || localName.indexOf(targetName) !== -1
              } else {
                matched = localName.indexOf('JW_') !== -1 ||
                    name.indexOf('JW_') !== -1 ||
                    name.indexOf('HOX') !== -1 || localName.indexOf('HOX') !== -1 ||
                    name.indexOf('LIGHTMASK') !== -1 || localName.indexOf('LIGHTMASK') !== -1 ||
                    name.indexOf('SIMPLE PERIPHERAL') !== -1 || localName.indexOf('SIMPLE PERIPHERAL') !== -1
              }
              console.log('[BLE] 匹配结果:', matched, targetName ? '(精确:' + targetName + ')' : '(模糊)')
              if (matched) {
                wx.stopBluetoothDevicesDiscovery({})
                if (callbacks.onFound) callbacks.onFound(d)
                connect(d.deviceId, callbacks)
                return
              }
            }
          })
        },
        fail: function () {
          if (callbacks.onError) callbacks.onError({ msg: '扫描失败' })
        }
      })
    },
    fail: function () {
      if (callbacks.onError) callbacks.onError({ msg: '请开启蓝牙' })
    }
  })
}

function stopScan() {
  wx.stopBluetoothDevicesDiscovery({})
  wx.offBluetoothDeviceFound()
}

function connect(deviceId, callbacks) {
  console.log('[BLE] 开始连接:', deviceId)
  _deviceId = deviceId
  _chars = {}

  wx.createBLEConnection({
    deviceId: deviceId,
    timeout: 10000,
    success: function () {
      console.log('[BLE] 连接成功:', deviceId)
      _connected = true
      requestMtu(deviceId).then(function () {
        discoverServices(deviceId, callbacks)
      })
    },
    fail: function (err) {
      console.log('[BLE] 连接失败:', deviceId, err)
      _connected = false
      if (callbacks.onError) callbacks.onError({ msg: '连接失败' })
    }
  })
}

function disconnect() {
  _autoReconnect = false
  _reconnecting = false
  if (_deviceId) {
    wx.closeBLEConnection({ deviceId: _deviceId })
    _deviceId = null
  }
  _connected = false
  _chars = {}
  var commands = require('./commands')
  commands.clearPendingAcks()
  // Only emit disconnected event; do not clear _listeners so that
  // subscribers (other modules) retain their registrations across reconnects.
  emit('disconnect_cleanup', null)
  wx.closeBluetoothAdapter({})
}

// --- reconnect ---

function attemptReconnect(deviceId, retriesLeft) {
  wx.createBLEConnection({
    deviceId: deviceId,
    timeout: 10000,
    success: function () {
      _connected = true
      _chars = {}
      requestMtu(deviceId).then(function () {
        wx.getBLEDeviceServices({
          deviceId: deviceId,
          success: function (res) {
            var services = res.services
            var serviceMap = {}
            for (var i = 0; i < services.length; i++) {
              var uuid = services[i].uuid.toUpperCase()
              if (uuid.indexOf(SERVICE.DEVICE_INFO) !== -1) {
                serviceMap.deviceInfo = services[i].uuid
              } else if (uuid.indexOf(SERVICE.DATA_COMM) !== -1) {
                serviceMap.dataComm = services[i].uuid
              }
            }

            var tasks = []
            if (serviceMap.deviceInfo) {
              tasks.push(discoverChars(deviceId, serviceMap.deviceInfo, 'deviceInfo'))
            }
            if (serviceMap.dataComm) {
              tasks.push(discoverChars(deviceId, serviceMap.dataComm, 'dataComm'))
            }

            Promise.all(tasks).then(function () {
              var serviceId = serviceMap.dataComm || null
              return subscribeToNotifications(deviceId, serviceId)
            }).then(function () {
              _reconnecting = false
              emit('reconnected', { deviceId: deviceId })
            })
          },
          fail: function () {
            if (retriesLeft > 1) {
              setTimeout(function () {
                attemptReconnect(deviceId, retriesLeft - 1)
              }, 2000)
            } else {
              _reconnecting = false
              emit('reconnect_failed', { deviceId: deviceId })
            }
          }
        })
      })
    },
    fail: function () {
      if (retriesLeft > 1) {
        setTimeout(function () {
          attemptReconnect(deviceId, retriesLeft - 1)
        }, 2000)
      } else {
        _reconnecting = false
        emit('reconnect_failed', { deviceId: deviceId })
      }
    }
  })
}

function setAutoReconnect(enabled) {
  _autoReconnect = enabled
}

// --- BLE connection state change listener ---

wx.onBLEConnectionStateChange(function (res) {
  if (!res.connected) {
    _connected = false
    emit('disconnected', { deviceId: res.deviceId })
    if (_autoReconnect && !_reconnecting && _deviceId) {
      _reconnecting = true
      setTimeout(function () {
        attemptReconnect(_deviceId, 3)
      }, 2000)
    }
  }
})

module.exports = {
  on: on,
  off: off,
  emit: emit,

  getDeviceId: getDeviceId,
  isConnected: isConnected,
  getChars: getChars,

  startScan: startScan,
  stopScan: stopScan,
  connect: connect,
  disconnect: disconnect,
  attemptReconnect: attemptReconnect,
  setAutoReconnect: setAutoReconnect
}
