const transport = require('../../transport/ble-core.js')
const {
  alignBootloaderBuffer,
  getBootloaderExpectedLength,
  parseBootloaderResponse
} = require('../../protocols/bootloader/index.js')

const RESPONSE_TIMEOUT_MS = 3000

let activeResponseWaiter = null

function abortActiveResponseWaiter(message) {
  if (!activeResponseWaiter) return false

  const waiter = activeResponseWaiter
  activeResponseWaiter = null
  waiter.abort(new Error(message || '蓝牙已断开'))
  return true
}

function waitForResponse(kind, timeout, options = {}) {
  const expectedLength = getBootloaderExpectedLength(kind)
  const buffer = []

  return new Promise((resolve, reject) => {
    let settled = false
    let timer = null
    let unsubscribe = () => {}
    const waiter = {
      abort: (error) => {
        cleanup()
        reject(error)
      }
    }

    abortActiveResponseWaiter('新的 BootLoader 响应等待已开始')
    activeResponseWaiter = waiter

    unsubscribe = transport.subscribeRawResponse((bytes) => {
      buffer.push.apply(buffer, bytes)
      alignBootloaderBuffer(buffer)
      if (buffer.length < expectedLength) return

      const frame = buffer.slice(0, expectedLength)

      try {
        const response = parseBootloaderResponse(frame, kind)
        cleanup()
        resolve(response)
      } catch (error) {
        if (options.ignoreInvalid) {
          buffer.shift()
          return
        }

        cleanup()
        reject(error)
      }
    })
    timer = setTimeout(() => {
      cleanup()
      reject(new Error(`${kind} 响应超时`))
    }, timeout || RESPONSE_TIMEOUT_MS)

    function cleanup() {
      if (settled) return
      settled = true
      clearTimeout(timer)
      if (activeResponseWaiter === waiter) {
        activeResponseWaiter = null
      }
      unsubscribe()
    }
  })
}

async function sendRawFrame(frame, label) {
  return transport.sendRawFrameExact(frame, label)
}

async function sendFrame(frame, label, kind, timeout) {
  const responsePromise = kind ? waitForResponse(kind, timeout) : null
  const sent = await sendRawFrame(frame, label)

  if (!sent) {
    if (responsePromise) {
      responsePromise.catch(() => {})
      abortActiveResponseWaiter(`${label}发送失败`)
    }
    throw new Error(`${label}发送失败`)
  }

  return responsePromise ? responsePromise : true
}

module.exports = {
  RESPONSE_TIMEOUT_MS,
  abortActiveResponseWaiter,
  sendFrame,
  sendRawFrame,
  waitForResponse
}
