const {
  DEFAULT_PACKET_SIZE,
  formatSignalText,
  hasTargetAdvertisedUuid,
  inferPacketSize,
  mergeAdvertisedServiceUUIDs,
  normalizeDevice
} = require('./ble-utils.js')

const DEFAULT_DEVICE_LIMIT = 30

function createBleDeviceRegistry(options = {}) {
  const deviceLimit = Math.max(1, Number(options.deviceLimit) || DEFAULT_DEVICE_LIMIT)
  let deviceMap = {}
  let deviceSequence = 0

  function getDevice(deviceId) {
    return deviceMap[deviceId] || null
  }

  function getSortedDeviceList() {
    return Object.keys(deviceMap)
      .map((deviceId) => deviceMap[deviceId])
      .sort((left, right) => {
        const leftIndex = Number(left.seenIndex) || 0
        const rightIndex = Number(right.seenIndex) || 0

        return leftIndex - rightIndex
      })
  }

  function getDeviceList() {
    return getSortedDeviceList().slice(0, deviceLimit)
  }

  function mergeDevices(devices = []) {
    if (!Array.isArray(devices) || !devices.length) return false

    let changed = false
    devices.forEach((device) => {
      if (!device || !device.deviceId) return
      const previousDevice = deviceMap[device.deviceId] || {}
      const nextDevice = normalizeDevice(device)
      const advertisServiceUUIDs = mergeAdvertisedServiceUUIDs(
        previousDevice.advertisServiceUUIDs,
        nextDevice.advertisServiceUUIDs
      )
      const isTargetAdvertised = !!previousDevice.isTargetAdvertised || hasTargetAdvertisedUuid({
        advertisServiceUUIDs
      })
      const isTargetDevice = !!previousDevice.isTargetDevice
      const seenIndex = previousDevice.seenIndex || (deviceSequence += 1)

      deviceMap[device.deviceId] = {
        ...previousDevice,
        ...nextDevice,
        advertisServiceUUIDs,
        displayName: nextDevice.displayName === '未命名设备' && previousDevice.displayName
          ? previousDevice.displayName
          : nextDevice.displayName,
        isTargetAdvertised,
        isTargetDevice,
        packetSize: nextDevice.packetSize || previousDevice.packetSize || DEFAULT_PACKET_SIZE,
        seenIndex,
        serviceText: advertisServiceUUIDs.length ? advertisServiceUUIDs.join(', ') : '未广播服务',
        targetText: isTargetDevice ? '已发现目标特征' : (isTargetAdvertised ? '广播含目标 UUID' : '')
      }
      changed = true
    })

    return changed
  }

  function markConnectedDevice(deviceId, discovery = {}) {
    const device = getDevice(deviceId)
    if (!device) return null

    const isTargetDevice = !!discovery.isTargetDevice
    const connectedDevice = {
      ...device,
      isTargetDevice,
      packetSize: device.packetSize || inferPacketSize(device),
      targetText: isTargetDevice ? '已发现目标特征' : device.targetText
    }
    deviceMap[deviceId] = connectedDevice

    return connectedDevice
  }

  function applyRssiUpdate(deviceId, rssi, connectedDevice = null) {
    if (typeof rssi !== 'number') return null
    if (connectedDevice && connectedDevice.deviceId !== deviceId) return null

    const signalText = formatSignalText(rssi)
    const lastSeenAt = Date.now()
    const updatedDevice = connectedDevice
      ? {
        ...connectedDevice,
        RSSI: rssi,
        lastSeenAt,
        signalText
      }
      : null

    deviceMap[deviceId] = {
      ...(deviceMap[deviceId] || {}),
      RSSI: rssi,
      lastSeenAt,
      signalText
    }

    return {
      deviceList: getDeviceList(),
      updatedDevice
    }
  }

  function clear() {
    deviceMap = {}
    deviceSequence = 0
  }

  return {
    applyRssiUpdate,
    clear,
    getDevice,
    getDeviceList,
    getSortedDeviceList,
    markConnectedDevice,
    mergeDevices
  }
}

module.exports = {
  DEFAULT_DEVICE_LIMIT,
  createBleDeviceRegistry
}
