const TYPE_ALIASES = {
  bit: 'uint8_t',
  bool: 'uint8_t',
  char: 'int8_t',
  double: 'float',
  float: 'float',
  int: 'int16_t',
  int8: 'int8_t',
  int8_t: 'int8_t',
  int16: 'int16_t',
  int16_t: 'int16_t',
  int32: 'int32_t',
  int32_t: 'int32_t',
  long: 'int32_t',
  short: 'int16_t',
  'signed char': 'int8_t',
  'signed int': 'int16_t',
  'signed long': 'int32_t',
  'signed short': 'int16_t',
  uint8: 'uint8_t',
  uint8_t: 'uint8_t',
  uint16: 'uint16_t',
  uint16_t: 'uint16_t',
  uint32: 'uint32_t',
  uint32_t: 'uint32_t',
  'unsigned char': 'uint8_t',
  'unsigned int': 'uint16_t',
  'unsigned long': 'uint32_t',
  'unsigned short': 'uint16_t'
}

const TYPE_QUALIFIERS = {
  _I: true,
  _IO: true,
  _O: true,
  const: true,
  extern: true,
  register: true,
  static: true,
  volatile: true
}

const STRUCT_PATTERNS = [
  /typedef\s+struct(?:\s+[A-Za-z_]\w*)?\s*\{([\s\S]*?)\}\s*([A-Za-z_]\w*)\s*;/g,
  /struct\s+([A-Za-z_]\w*)\s*\{([\s\S]*?)\}\s*;/g
]

function stripComments(source) {
  return String(source || '')
    .replace(/\/\*[\s\S]*?\*\//g, '')
    .replace(/\/\/.*$/gm, '')
}

function normalizeTypeText(typeText) {
  return String(typeText || '')
    .replace(/\*/g, ' ')
    .replace(/\s+/g, ' ')
    .trim()
}

function createAliasMap(source) {
  const aliases = {
    ...TYPE_ALIASES
  }
  const definePattern = /^\s*#\s*define\s+([A-Za-z_]\w*)\s+([A-Za-z_]\w*)\s*$/gm
  let defineMatch

  while ((defineMatch = definePattern.exec(source))) {
    const name = defineMatch[1]
    const value = defineMatch[2]
    if (aliases[value]) aliases[name] = aliases[value]
  }

  const typedefPattern = /typedef\s+(?!struct\b)([^;{}]+?)\s+([A-Za-z_]\w*)\s*;/g
  let typedefMatch

  while ((typedefMatch = typedefPattern.exec(source))) {
    const resolvedType = resolveType(typedefMatch[1], aliases)
    if (resolvedType) aliases[typedefMatch[2]] = resolvedType
  }

  return aliases
}

function resolveType(typeText, aliases) {
  const normalized = normalizeTypeText(typeText)
  if (!normalized) return ''

  const compact = normalized
    .split(/\s+/)
    .filter((token) => !TYPE_QUALIFIERS[token])
    .join(' ')
    .trim()

  if (!compact || /^struct\b/.test(compact) || /^enum\b/.test(compact) || compact.indexOf('*') >= 0) {
    return ''
  }

  if (aliases[compact]) return aliases[compact]

  const tokens = compact.split(/\s+/).filter(Boolean)
  for (const token of tokens) {
    if (aliases[token]) return aliases[token]
  }

  return ''
}

function findStruct(source) {
  for (const pattern of STRUCT_PATTERNS) {
    pattern.lastIndex = 0
    const match = pattern.exec(source)
    if (!match) continue

    if (pattern === STRUCT_PATTERNS[0]) {
      return {
        body: match[1],
        name: match[2]
      }
    }

    return {
      body: match[2],
      name: match[1]
    }
  }

  return null
}

function parseArrayDimensions(suffix) {
  const dimensions = []
  const pattern = /\[([^\]]*)\]/g
  let match

  while ((match = pattern.exec(suffix || ''))) {
    const text = String(match[1] || '').trim()
    const value = Number(text)
    if (!Number.isInteger(value) || value < 1) {
      throw new Error('数组长度需为正整数')
    }
    dimensions.push(value)
  }

  return dimensions
}

function splitDeclarations(body) {
  return String(body || '')
    .split(';')
    .map((item) => item.trim())
    .filter(Boolean)
}

function splitDeclarators(statement) {
  return String(statement || '')
    .split(',')
    .map((item) => item.trim())
    .filter(Boolean)
}

function parseFirstDeclarator(text) {
  const match = String(text || '').match(/^(.+?)\s+(\**\s*[A-Za-z_]\w*(?:\s*\[[^\]]*\])*(?:\s*:\s*\d+)?)$/)
  if (!match) return null

  return {
    declarator: match[2],
    typeText: match[1]
  }
}

function parseDeclarator(text) {
  const cleaned = String(text || '')
    .replace(/=.*/, '')
    .replace(/:\s*\d+\s*$/, '')
    .replace(/\*/g, '')
    .trim()
  const match = cleaned.match(/^([A-Za-z_]\w*)\s*((?:\[[^\]]*\])*)$/)
  if (!match) return null

  return {
    arrayDimensions: parseArrayDimensions(match[2]),
    name: match[1]
  }
}

function isAsciiArray(typeText, dataType, name, arrayLength) {
  if (!arrayLength || arrayLength < 2 || arrayLength > 32) return false

  const normalizedType = normalizeTypeText(typeText).toLowerCase()
  if (normalizedType === 'char' || normalizedType === 'signed char') return true

  return dataType === 'uint8_t' && /(^|_)(model|name|text|str|string|chip|version|ver|serial|sn)($|_)/i.test(name)
}

function createRegisterFromField(field, dataType, originalTypeText) {
  const arrayLength = field.arrayDimensions.reduce((total, value) => total * value, 1)
  const hasArray = field.arrayDimensions.length > 0

  if (hasArray && isAsciiArray(originalTypeText, dataType, field.name, arrayLength)) {
    return [{
      dataType: 'ascii',
      name: field.name,
      textByteLength: String(arrayLength)
    }]
  }

  if (!hasArray) {
    return [{
      dataType,
      name: field.name
    }]
  }

  const registers = []
  for (let index = 0; index < arrayLength; index += 1) {
    registers.push({
      dataType,
      name: `${field.name}[${index}]`
    })
  }

  return registers
}

function parseStructFields(body, aliases) {
  const registers = []
  const declarations = splitDeclarations(body)

  declarations.forEach((statement) => {
    if (!statement || statement.indexOf('(') >= 0) return

    const parts = splitDeclarators(statement)
    if (!parts.length) return

    const first = parseFirstDeclarator(parts[0])
    if (!first) return

    const dataType = resolveType(first.typeText, aliases)
    if (!dataType) return

    const declarators = [first.declarator].concat(parts.slice(1))
    declarators.forEach((declaratorText) => {
      const field = parseDeclarator(declaratorText)
      if (!field) return

      registers.push(...createRegisterFromField(field, dataType, first.typeText))
    })
  })

  return registers
}

function parseStructDefinition(sourceText) {
  const source = stripComments(sourceText)
  const aliases = createAliasMap(source)
  const structInfo = findStruct(source)
  if (!structInfo) {
    throw new Error('未找到结构体定义')
  }

  const registers = parseStructFields(structInfo.body, aliases)
  if (!registers.length) {
    throw new Error('结构体中没有可识别的变量定义')
  }

  return {
    name: structInfo.name || 'Struct',
    registers,
    structName: structInfo.name || 'Struct'
  }
}

module.exports = {
  parseStructDefinition,
  stripComments
}
