const transport = require('../../transport/ble-core.js')
const {
  buildReadFrame,
  buildWriteMultipleRegistersFrame,
  buildWriteSingleCoilFrame,
  buildWriteSingleRegisterFrame,
  formatHex
} = require('../../protocols/modbus-rtu/index.js')
const {
  parseHexNumber
} = require('../../utils/validation.js')
const {
  DATA_TYPE_OPTIONS,
  getDataType,
  getRegisterEncodedWords,
  isByteRegister,
  isTextRegister,
  normalizeRegister,
  validateRegisterValue
} = require('../../domain/parameter-groups/model.js')

const MANUAL_DATA_TYPE_OPTIONS = DATA_TYPE_OPTIONS.filter((item) => item.key !== 'raw')

const MODBUS_COMMANDS = [
  { key: 'readCoils', label: '01 读取线圈', functionCode: 0x01, inputMode: 'quantity' },
  { key: 'readDiscreteInputs', label: '02 读取离散输入', functionCode: 0x02, inputMode: 'quantity' },
  { key: 'readHolding', label: '03 读取保持寄存器', functionCode: 0x03, inputMode: 'quantity' },
  { key: 'readInput', label: '04 读取输入寄存器', functionCode: 0x04, inputMode: 'quantity' },
  { key: 'writeCoil', label: '05 写单线圈', functionCode: 0x05, inputMode: 'coil' },
  { key: 'writeRegister', label: '06 写单寄存器', functionCode: 0x06, inputMode: 'single' },
  { key: 'writeRegisters', label: '10 写多寄存器', functionCode: 0x10, inputMode: 'multiple' }
]

const state = {
  commandIndex: 2,
  commandRegisterQuantity: '0001',
  commandValue: '0001',
  commandValueLabel: '读取数量',
  coilEnabled: true,
  generatedHex: '',
  protocolCommands: MODBUS_COMMANDS,
  protocolDataTypeOptions: MANUAL_DATA_TYPE_OPTIONS,
  protocolErrorText: '',
  protocolMultipleDialog: {
    visible: false
  },
  protocolMultipleExpanded: false,
  protocolMultipleValues: [],
  protocolResponseText: '',
  protocolStatusText: '',
  registerAddress: '0000',
  showCoilValue: false,
  showCommandValue: true,
  showRegisterQuantity: false,
  slaveAddress: 'F0'
}

const subscribers = []

function parseRegisterValues(value) {
  const text = String(value || '').trim()
  if (!text) throw new Error('请输入寄存器写入值')

  return text.split(/[\s,;]+/)
    .filter(Boolean)
    .map((item) => parseHexNumber(item, '写入值', 0xFFFF))
}

function getCommand(index) {
  return MODBUS_COMMANDS[index] || MODBUS_COMMANDS[0]
}

function getDefaultCommandValue(command) {
  if (command.inputMode === 'quantity') return '0001'
  if (command.inputMode === 'coil') return 'ON'
  if (command.inputMode === 'multiple') return '0000'

  return '0000'
}

function generateModbusFrame(command, slaveAddress, registerAddress, commandValue, coilEnabled, commandRegisterQuantity) {
  const slave = parseHexNumber(slaveAddress, '从站地址', 0xFF)
  const address = parseHexNumber(registerAddress, '起始地址', 0xFFFF)

  if (command.inputMode === 'quantity') {
    const quantity = parseHexNumber(commandValue, '读取数量', 0xFFFF)
    return buildReadFrame(slave, command.functionCode, address, quantity)
  }
  if (command.inputMode === 'coil') {
    return buildWriteSingleCoilFrame(slave, address, coilEnabled)
  }
  if (command.inputMode === 'single') {
    return buildWriteSingleRegisterFrame(slave, address, parseHexNumber(commandValue, '写入值', 0xFFFF))
  }

  const words = parseRegisterValues(commandValue)
  const quantity = parseHexNumber(commandRegisterQuantity, '寄存器个数', 0xFFFF)
  if (quantity !== words.length) {
    throw new Error(`写入值数量应为 ${quantity} 个寄存器`)
  }

  return buildWriteMultipleRegistersFrame(slave, address, words)
}

function createProtocolState(commandIndex, slaveAddress, registerAddress, commandValue, coilEnabled, commandRegisterQuantity) {
  const command = getCommand(commandIndex)
  const commandValueLabel = command.inputMode === 'quantity' ? '读取数量' : '写入值'

  try {
    return {
      commandValueLabel,
      generatedHex: formatHex(generateModbusFrame(command, slaveAddress, registerAddress, commandValue, coilEnabled, commandRegisterQuantity)),
      protocolErrorText: '',
      showCoilValue: command.inputMode === 'coil',
      showRegisterQuantity: command.inputMode === 'multiple',
      showCommandValue: command.inputMode !== 'coil'
    }
  } catch (error) {
    return {
      commandValueLabel,
      generatedHex: '',
      protocolErrorText: error.message,
      showCoilValue: command.inputMode === 'coil',
      showRegisterQuantity: command.inputMode === 'multiple',
      showCommandValue: command.inputMode !== 'coil'
    }
  }
}

function buildExpectedResponse(sourceState = {}) {
  try {
    const command = getCommand(sourceState.commandIndex)
    const address = parseHexNumber(sourceState.registerAddress, '起始地址', 0xFFFF)
    const slaveAddress = parseHexNumber(sourceState.slaveAddress, '从站地址', 0xFF)
    const quantity = command.inputMode === 'quantity'
      ? parseHexNumber(sourceState.commandValue, '读取数量', 0xFFFF)
      : (command.inputMode === 'multiple' ? parseHexNumber(sourceState.commandRegisterQuantity, '寄存器个数', 0xFFFF) : 1)
    const value = command.inputMode === 'coil'
      ? (sourceState.coilEnabled ? 0xFF00 : 0x0000)
      : (command.inputMode === 'single' ? parseHexNumber(sourceState.commandValue, '写入值', 0xFFFF) : undefined)

    return {
      address,
      functionCode: command.functionCode,
      kind: 'manual-rtu',
      protocol: 'modbus-rtu',
      quantity,
      value,
      slaveAddress
    }
  } catch (error) {
    return null
  }
}

function formatResponseHex(value, width) {
  return Number(value || 0).toString(16).toUpperCase().padStart(width, '0')
}

function formatGeneratedResponse(response) {
  if (!response) return ''

  const slave = formatResponseHex(response.slaveAddress, 2)
  const functionCode = formatResponseHex(response.functionCode, 2)

  if (Array.isArray(response.words) && response.words.length) {
    return `从机 0x${slave} / 功能码 0x${functionCode} / 字 ${response.words.map((word) => formatResponseHex(word, 4)).join(' ')}`
  }

  if (Array.isArray(response.dataBytes) && response.dataBytes.length) {
    return `从机 0x${slave} / 功能码 0x${functionCode} / 数据 ${formatHex(response.dataBytes)}`
  }

  if (Number.isInteger(response.address)) {
    return `从机 0x${slave} / 功能码 0x${functionCode} / 地址 0x${formatResponseHex(response.address, 4)} / 值 0x${formatResponseHex(response.quantityOrValue, 4)}`
  }

  return `从机 0x${slave} / 功能码 0x${functionCode}`
}

function normalizeManualMultipleQuantity(value) {
  const text = String(value === undefined || value === null ? '' : value).trim()
  if (!text) return 1
  if (/^[0-9a-fA-F]+$/.test(text)) return Math.max(1, Math.min(parseInt(text, 16), 0x007B))

  const numberValue = Number(text)
  return Number.isFinite(numberValue) ? Math.max(1, Math.min(Math.round(numberValue), 0x007B)) : 1
}

function formatManualMultipleQuantity(quantity) {
  return Number(quantity || 1).toString(16).toUpperCase().padStart(4, '0')
}

function createManualMultipleRegister(index, value = {}) {
  const dataType = getDataType(value.dataType || 'hex')
  const register = normalizeRegister({
    dataType: dataType.key,
    inputValue: value.inputValue === undefined ? '' : value.inputValue,
    name: `寄存器 ${index + 1}`,
    textByteLength: value.textByteLength || (isTextRegister(dataType.key) ? '32' : '')
  }, {
    registerType: 'holding'
  }, index, Number(value.address || 0), 0)

  return {
    ...register,
    dataTypeIndex: Math.max(0, MANUAL_DATA_TYPE_OPTIONS.findIndex((item) => item.key === register.dataType)),
    inputValue: value.inputValue === undefined ? '' : value.inputValue
  }
}

function getManualRegisterWordCount(register) {
  return Math.max(1, Number(register && register.registerCount) || 1)
}

function normalizeManualMultipleValues(wordQuantity, values = [], startAddress = 0) {
  const result = []
  let address = Number(startAddress) || 0
  const endAddress = address + Math.max(1, Number(wordQuantity) || 1)
  let sourceIndex = 0

  while (address < endAddress) {
    const current = values[sourceIndex] || {}
    let register = createManualMultipleRegister(result.length, {
      ...current,
      address
    })
    const remainingWords = endAddress - address
    if (getManualRegisterWordCount(register) > remainingWords) {
      register = createManualMultipleRegister(result.length, {
        ...current,
        address,
        dataType: 'hex',
        inputValue: ''
      })
    }
    result.push(register)
    address += getManualRegisterWordCount(register)
    sourceIndex += 1
  }

  return result
}

function getManualMultipleWords(values = []) {
  const words = []
  values.forEach((register) => {
    if (isByteRegister(register.dataType)) {
      const registerWords = getRegisterEncodedWords(register)
      if (!Array.isArray(registerWords) || !registerWords.length) throw new Error(`${register.name} 输入值无效`)
      words.push(Number(registerWords[0]) & 0x00FF)
      return
    }

    const registerWords = getRegisterEncodedWords(register)
    if (!Array.isArray(registerWords) || !registerWords.length) throw new Error(`${register.name} 输入值无效`)
    registerWords.forEach((word) => words.push(Number(word) & 0xFFFF))
  })

  return words
}

function getManualMultipleValueText(values = []) {
  try {
    return getManualMultipleWords(values).map((word) => word.toString(16).toUpperCase().padStart(4, '0')).join(' ')
  } catch (error) {
    return ''
  }
}

function getManualMultipleDataType(dataTypeIndex) {
  return MANUAL_DATA_TYPE_OPTIONS[Number(dataTypeIndex)] || MANUAL_DATA_TYPE_OPTIONS[0]
}

function updateManualMultipleValue(values = [], index, value) {
  const registerIndex = Number(index)

  return values.map((register, currentIndex) => (
    currentIndex === registerIndex
      ? {
        ...register,
        inputValue: value
      }
      : register
  ))
}

function updateManualMultipleType(values = [], index, dataTypeIndex) {
  const registerIndex = Number(index)
  const dataType = getManualMultipleDataType(dataTypeIndex)

  return values.map((register, currentIndex) => (
    currentIndex === registerIndex
      ? createManualMultipleRegister(currentIndex, {
        ...register,
        dataType: dataType.key,
        inputValue: '',
        textByteLength: isTextRegister(dataType.key) ? (register.textByteLength || '32') : ''
      })
      : register
  ))
}

function updateManualMultipleTextLength(values = [], index, value) {
  const registerIndex = Number(index)

  return values.map((register, currentIndex) => (
    currentIndex === registerIndex
      ? createManualMultipleRegister(currentIndex, {
        ...register,
        textByteLength: value
      })
      : register
  ))
}

function validateManualMultipleValue(values = [], index, value) {
  const register = values[Number(index)]
  if (!register) return false

  return validateRegisterValue(register, value)
}

function setState(changedData) {
  Object.assign(state, changedData)
  subscribers.slice().forEach((subscriber) => {
    subscriber(getState())
  })
}

function getState() {
  return {
    ...state,
    protocolCommands: state.protocolCommands.slice(),
    protocolDataTypeOptions: state.protocolDataTypeOptions.slice(),
    protocolMultipleDialog: {
      ...state.protocolMultipleDialog
    },
    protocolMultipleExpanded: !!state.protocolMultipleExpanded,
    protocolMultipleValues: state.protocolMultipleValues.map((item) => ({
      ...item
    })),
    protocolStatusText: state.protocolStatusText || ''
  }
}

function subscribe(subscriber) {
  if (typeof subscriber !== 'function') return () => {}

  subscribers.push(subscriber)
  subscriber(getState())

  return () => {
    const index = subscribers.indexOf(subscriber)
    if (index >= 0) subscribers.splice(index, 1)
  }
}

function setProtocolInput(changedData) {
  const command = getCommand(changedData.commandIndex === undefined ? state.commandIndex : changedData.commandIndex)
  let nextMultipleValues = changedData.protocolMultipleValues
  let nextCommandValue = changedData.commandValue
  if (
    command.inputMode === 'multiple'
    && Object.prototype.hasOwnProperty.call(changedData, 'registerAddress')
    && !Object.prototype.hasOwnProperty.call(changedData, 'protocolMultipleValues')
  ) {
    try {
      const startAddress = parseHexNumber(changedData.registerAddress, '起始地址', 0xFFFF)
      nextMultipleValues = normalizeManualMultipleValues(
        normalizeManualMultipleQuantity(state.commandRegisterQuantity),
        state.protocolMultipleValues,
        startAddress
      )
      nextCommandValue = getManualMultipleValueText(nextMultipleValues)
    } catch (error) {}
  }
  const nextState = {
    commandIndex: state.commandIndex,
    commandRegisterQuantity: state.commandRegisterQuantity,
    slaveAddress: state.slaveAddress,
    registerAddress: state.registerAddress,
    commandValue: state.commandValue,
    coilEnabled: state.coilEnabled,
    ...changedData,
    ...(nextMultipleValues ? { protocolMultipleValues: nextMultipleValues } : {}),
    ...(nextCommandValue !== undefined ? { commandValue: nextCommandValue } : {})
  }

  setState({
    ...changedData,
    ...(nextMultipleValues ? { protocolMultipleValues: nextMultipleValues } : {}),
    ...(nextCommandValue !== undefined ? { commandValue: nextCommandValue } : {}),
    protocolResponseText: '',
    protocolStatusText: '',
    ...createProtocolState(
      nextState.commandIndex,
      nextState.slaveAddress,
      nextState.registerAddress,
      nextState.commandValue,
      nextState.coilEnabled,
      nextState.commandRegisterQuantity
    )
  })
}

function setCommandIndex(index) {
  const commandIndex = Number(index)
  const command = getCommand(commandIndex)
  let protocolMultipleValues = state.protocolMultipleValues
  let commandValue = getDefaultCommandValue(command)

  if (command.inputMode === 'multiple') {
    let startAddress = 0
    try {
      startAddress = parseHexNumber(state.registerAddress, '起始地址', 0xFFFF)
    } catch (error) {}
    protocolMultipleValues = normalizeManualMultipleValues(
      normalizeManualMultipleQuantity(state.commandRegisterQuantity),
      state.protocolMultipleValues,
      startAddress
    )
    commandValue = getManualMultipleValueText(protocolMultipleValues)
  }

  setProtocolInput({
    commandIndex,
    commandValue,
    coilEnabled: true,
    commandRegisterQuantity: state.commandRegisterQuantity,
    ...(command.inputMode === 'multiple' ? { protocolMultipleValues } : {})
  })
}

function openProtocolMultipleDialog() {
  let startAddress = 0
  try {
    startAddress = parseHexNumber(state.registerAddress, '起始地址', 0xFFFF)
  } catch (error) {
    setState({
      protocolErrorText: error.message || '起始地址无效'
    })
    return
  }

  const quantity = normalizeManualMultipleQuantity(state.commandRegisterQuantity)
  const values = normalizeManualMultipleValues(quantity, state.protocolMultipleValues, startAddress)
  setState({
    commandRegisterQuantity: formatManualMultipleQuantity(quantity),
    protocolMultipleDialog: {
      title: '写多个寄存器',
      visible: true
    },
    protocolMultipleExpanded: false,
    protocolMultipleValues: values
  })
}

function closeProtocolMultipleDialog() {
  setState({
    protocolMultipleDialog: {
      visible: false
    }
  })
}

function toggleProtocolMultipleExpanded() {
  setState({
    protocolMultipleExpanded: !state.protocolMultipleExpanded
  })
}

function setProtocolMultipleQuantity(value) {
  const quantity = normalizeManualMultipleQuantity(value)
  let startAddress = 0
  try {
    startAddress = parseHexNumber(state.registerAddress, '起始地址', 0xFFFF)
  } catch (error) {
    setState({
      commandRegisterQuantity: value,
      protocolErrorText: error.message || '起始地址无效'
    })
    return
  }
  const values = normalizeManualMultipleValues(quantity, state.protocolMultipleValues, startAddress)
  const commandValue = getManualMultipleValueText(values)

  setProtocolInput({
    commandRegisterQuantity: value,
    commandValue,
    protocolMultipleValues: values
  })
}

function setProtocolMultipleValue(index, value) {
  const values = updateManualMultipleValue(state.protocolMultipleValues, index, value)
  let startAddress = 0
  try {
    startAddress = parseHexNumber(state.registerAddress, '起始地址', 0xFFFF)
  } catch (error) {
    setState({ protocolErrorText: error.message || '起始地址无效' })
    return
  }
  const normalizedValues = normalizeManualMultipleValues(normalizeManualMultipleQuantity(state.commandRegisterQuantity), values, startAddress)
  const commandValue = getManualMultipleValueText(normalizedValues)

  setProtocolInput({
    commandValue,
    protocolMultipleValues: normalizedValues
  })
}

function setProtocolMultipleType(index, dataTypeIndex) {
  const changedValues = updateManualMultipleType(state.protocolMultipleValues, index, dataTypeIndex)
  let startAddress = 0
  try {
    startAddress = parseHexNumber(state.registerAddress, '起始地址', 0xFFFF)
  } catch (error) {
    setState({ protocolErrorText: error.message || '起始地址无效' })
    return
  }
  const values = normalizeManualMultipleValues(normalizeManualMultipleQuantity(state.commandRegisterQuantity), changedValues, startAddress)

  setProtocolInput({
    commandValue: getManualMultipleValueText(values),
    protocolMultipleValues: values
  })
}

function setProtocolMultipleTextLength(index, value) {
  const changedValues = updateManualMultipleTextLength(state.protocolMultipleValues, index, value)
  let startAddress = 0
  try {
    startAddress = parseHexNumber(state.registerAddress, '起始地址', 0xFFFF)
  } catch (error) {
    setState({ protocolErrorText: error.message || '起始地址无效' })
    return
  }
  const values = normalizeManualMultipleValues(normalizeManualMultipleQuantity(state.commandRegisterQuantity), changedValues, startAddress)

  setProtocolInput({
    commandValue: getManualMultipleValueText(values),
    protocolMultipleValues: values
  })
}

function validateProtocolMultipleValue(index, value) {
  return validateManualMultipleValue(state.protocolMultipleValues, index, value)
}

function buildGeneratedExpectedResponse() {
  return buildExpectedResponse(state)
}

async function sendGeneratedFrame() {
  if (!state.generatedHex) {
    setState({
      protocolStatusText: state.protocolErrorText || '请先生成有效帧'
    })
    return false
  }

  const expected = buildGeneratedExpectedResponse()
  setState({
    protocolResponseText: '',
    protocolStatusText: ''
  })

  const response = await transport.enqueueSendFrame(state.generatedHex, 'RTU', expected ? {
    expected
  } : {})

  if (response) {
    setState({
      protocolResponseText: formatGeneratedResponse(response),
      protocolStatusText: ''
    })
  } else {
    setState({
      protocolStatusText: '未收到回复'
    })
  }

  return response
}

setState(createProtocolState(
  state.commandIndex,
  state.slaveAddress,
  state.registerAddress,
  state.commandValue,
  state.coilEnabled,
  state.commandRegisterQuantity
))

module.exports = {
  MODBUS_COMMANDS,
  buildGeneratedExpectedResponse,
  closeProtocolMultipleDialog,
  formatGeneratedResponse,
  generateModbusFrame,
  getState,
  openProtocolMultipleDialog,
  sendGeneratedFrame,
  setCommandIndex,
  setProtocolInput,
  setProtocolMultipleQuantity,
  setProtocolMultipleTextLength,
  setProtocolMultipleType,
  setProtocolMultipleValue,
  subscribe,
  toggleProtocolMultipleExpanded,
  validateProtocolMultipleValue
}
