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 normalizeLookupName(value) {
  return String(value || '')
    .replace(/^_+/, '')
    .replace(/[^A-Za-z0-9]/g, '')
    .toLowerCase()
}

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 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 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
  }

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

  while ((structTypedefMatch = typedefStructPattern.exec(source))) {
    const tagName = structTypedefMatch[1]
    const typedefName = structTypedefMatch[2]
    if (tagName && typedefName) aliases[`struct ${tagName}`] = typedefName
  }

  return aliases
}

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 findStructs(source) {
  const structs = []

  STRUCT_PATTERNS.forEach((pattern) => {
    pattern.lastIndex = 0
    let match

    while ((match = pattern.exec(source))) {
      if (pattern === STRUCT_PATTERNS[0]) {
        structs.push({
          body: match[1],
          name: match[2]
        })
      } else {
        structs.push({
          body: match[2],
          name: match[1],
          tagName: match[1]
        })
      }
    }
  })

  const seen = {}

  return structs.filter((item) => {
    const key = item.name
    if (!key || seen[key]) return false
    seen[key] = true

    return true
  })
}

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 rawText = String(text || '')
  const bitWidthMatch = rawText.match(/:\s*(\d+)\s*$/)
  const cleaned = rawText
    .replace(/=.*/, '')
    .replace(/:\s*\d+\s*$/, '')
    .replace(/\*/g, '')
    .trim()

  if (!cleaned && bitWidthMatch) {
    return {
      arrayDimensions: [],
      bitWidth: Number(bitWidthMatch[1]),
      name: ''
    }
  }

  const match = cleaned.match(/^([A-Za-z_]\w*)\s*((?:\[[^\]]*\])*)$/)
  if (!match) return null

  return {
    arrayDimensions: parseArrayDimensions(match[2]),
    bitWidth: bitWidthMatch ? Number(bitWidthMatch[1]) : null,
    name: match[1]
  }
}

module.exports = {
  createAliasMap,
  findStruct,
  findStructs,
  normalizeLookupName,
  normalizeTypeText,
  parseDeclarator,
  parseFirstDeclarator,
  resolveType,
  splitDeclarations,
  splitDeclarators,
  stripComments
}
