const {
  normalizeTextValue
} = require('../../utils/base-utils.js')
const {
  trimTrailingNullBytes
} = require('../../utils/binary-utils.js')
const {
  MAX_TEXT_BYTE_LENGTH
} = require('./constants.js')
const {
  getDataType
} = require('./value-types.js')

function encodeAsciiBytes(text, byteLimit = 32) {
  const bytes = []
  const stringValue = normalizeTextValue(text)

  for (let index = 0; index < stringValue.length; index += 1) {
    const code = stringValue.charCodeAt(index)
    if (code > 0x7F) {
      throw new Error('ASCII 文本只能包含 0x00 - 0x7F 字符')
    }
    bytes.push(code)
    if (bytes.length > byteLimit) break
  }

  if (bytes.length > byteLimit) {
    throw new Error(`长文本最长 ${byteLimit} 字节`)
  }

  return bytes
}

function encodeUtf8Bytes(text, byteLimit = 32) {
  const bytes = []
  const encoded = encodeURIComponent(normalizeTextValue(text))

  for (let index = 0; index < encoded.length; index += 1) {
    const char = encoded[index]
    if (char === '%') {
      const byte = parseInt(encoded.slice(index + 1, index + 3), 16)
      if (!Number.isFinite(byte)) break
      bytes.push(byte & 0xFF)
      index += 2
    } else {
      bytes.push(char.charCodeAt(0) & 0xFF)
    }
    if (bytes.length > byteLimit) break
  }

  if (bytes.length > byteLimit) {
    throw new Error(`长文本最长 ${byteLimit} 字节`)
  }

  return bytes
}

function decodeAsciiBytes(bytes = []) {
  return String.fromCharCode.apply(null, trimTrailingNullBytes(bytes).map((byte) => byte & 0xFF))
}

function decodeUtf8Bytes(bytes = []) {
  const trimmed = trimTrailingNullBytes(bytes)
  if (!trimmed.length) return ''

  let encoded = ''

  trimmed.forEach((byte) => {
    encoded += `%${(byte & 0xFF).toString(16).padStart(2, '0').toUpperCase()}`
  })

  try {
    return decodeURIComponent(encoded)
  } catch (error) {
    return decodeAsciiBytes(trimmed)
  }
}

function encodeTextBytes(text, dataType, byteLimit = MAX_TEXT_BYTE_LENGTH) {
  const normalizedType = getDataType(dataType).key

  if (normalizedType === 'ascii') return encodeAsciiBytes(text, byteLimit)
  return encodeUtf8Bytes(text, byteLimit)
}

function decodeTextBytes(bytes, dataType) {
  const normalizedType = getDataType(dataType).key

  return normalizedType === 'ascii'
    ? decodeAsciiBytes(bytes)
    : decodeUtf8Bytes(bytes)
}

module.exports = {
  decodeAsciiBytes,
  decodeTextBytes,
  decodeUtf8Bytes,
  encodeAsciiBytes,
  encodeTextBytes,
  encodeUtf8Bytes
}
