const parameterGroupService = require('./service.js')
const {
  validateValueFormula
} = require('../../domain/parameter-groups/value-formula.js')
const {
  normalizeEnumOptions
} = require('../../domain/parameter-groups/value-codec.js')
const settingsService = require('../../store/settings-store.js')
const themeService = require('../../store/theme-store.js')
const transport = require('../../transport/ble-core.js')

function getOption(options, index) {
  return options[Number(index)] || options[0] || {}
}

function getCodeInfoVariableByteLength(dialog = {}) {
  const entryKind = String(dialog.sourceEntryKind || '').trim().toLowerCase()
  const byteLength = Number(dialog.sourceByteLength)

  if ((entryKind !== 'variable' && entryKind !== 'enum') || !Number.isFinite(byteLength) || byteLength <= 0) return 0

  return Math.floor(byteLength)
}

function getDataTypeConfigByteLength(dataType = {}, dialog = {}) {
  if (dataType.kind === 'raw') return getCodeInfoVariableByteLength(dialog)
  if (dataType.kind === 'text') return Number(dialog.textByteLength) || Number(dataType.byteLength) || 0

  return Number(dataType.byteLength) || Number(dataType.wordCount || 0) * 2
}

function validateCodeInfoVariableDataType(dialog = {}, dataType = {}) {
  const sourceByteLength = getCodeInfoVariableByteLength(dialog)
  if (!sourceByteLength || dataType.kind === 'raw') return

  if (dataType.kind === 'text' || dataType.kind === 'hex') {
    throw new Error('变量类型请选择整数、浮点或 enum 对应整数类型')
  }

  const dataTypeByteLength = getDataTypeConfigByteLength(dataType, dialog)
  if (dataTypeByteLength !== sourceByteLength) {
    throw new Error(`变量长度为 ${sourceByteLength}B，不能选择 ${dataType.label || dataType.key || '该类型'}`)
  }
}

function getStorageSourceDisplayText(sourceMemoryArea) {
  const text = String(sourceMemoryArea || '').trim().toUpperCase()
  if (text === 'XDATA' || text === 'IDATA' || text === 'DATA' || text === 'CODE') {
    return text.toLowerCase()
  }

  return ''
}

function getStorageSourceTypeText(source = {}) {
  return String(
    source.sourceDefinitionName
      || source.sourceSymbolType
      || source.sourceValueType
      || source.sourceElementType
      || source.dataTypeText
      || source.dataType
      || ''
  ).trim()
}

function getStorageRegisterTypeText(register = {}) {
  const entryKind = String(register.sourceEntryKind || '').trim().toLowerCase()
  const sourceValueType = String(register.sourceValueType || '').trim()
  const sourceSymbolType = String(register.sourceSymbolType || '').trim()
  const dataTypeText = String(register.dataTypeText || register.dataType || '').trim()
  const isStructFieldSource = entryKind === 'struct'
    || (entryKind === 'array' && sourceValueType === 'struct')
    || sourceValueType === 'struct'

  return String(
    register.enumName
      || (sourceValueType && sourceValueType !== 'struct' ? sourceValueType : '')
      || (!isStructFieldSource && sourceSymbolType && sourceSymbolType !== 'struct' ? sourceSymbolType : '')
      || dataTypeText
  ).trim()
}

function isEnumTypeSource(source = {}) {
  const entryKind = String(source.sourceEntryKind || '').trim().toLowerCase()
  const valueTypes = [
    source.sourceValueType,
    source.sourceElementType,
    source.sourceSymbolType,
    source.sourceDefinitionName
  ]

  if (entryKind === 'enum') return true
  if (String(source.enumName || '').trim()) return true
  if (normalizeEnumOptions(source).length) return true

  return valueTypes.some((value) => {
    const text = String(value || '').trim().toLowerCase()

    return text === 'enum' || /^enum\b/.test(text) || /\benum\b/.test(text)
  })
}

function getStorageDetailTitle(source = {}) {
  const entryKind = String(source.sourceEntryKind || '').trim().toLowerCase()
  const valueType = String(source.sourceValueType || source.sourceElementType || source.sourceSymbolType || '').trim().toLowerCase()
  if (isEnumTypeSource(source)) return '枚举详情'
  if (entryKind === 'array') return valueType === 'struct' ? '结构体数组详情' : '数组详情'
  if (entryKind === 'struct' || valueType === 'struct') return '结构体详情'
  if (entryKind === 'variable') return '变量详情'

  return '存储详情'
}

function createEnumOptionRows(register = {}) {
  return normalizeEnumOptions(register).map((option) => ({
    text: `${option.label || option.name} = ${Number(option.value)}`
  }))
}

function getPageState() {
  const settingsState = settingsService.getState()
  const transportState = transport.getState()
  const isNoProtocol = settingsService.isNoProtocol(settingsState.protocolMode)
  const isModbusProtocol = settingsService.isModbusProtocol(settingsState.protocolMode)
  const isStorageAccessProtocol = settingsService.isStorageAccessProtocol(settingsState.protocolMode)

  return {
    ...parameterGroupService.getState(),
    ...themeService.getState(),
    ...settingsState,
    connectedDevice: transportState.connectedDevice,
    isNoProtocol,
    isModbusProtocol,
    isStorageAccessProtocol
  }
}

function resolveActiveParamView(currentView, settingsState = settingsService.getState()) {
  if (settingsState.parameterCardControlEnabled === false) return 'parameterGroups'

  return currentView === 'parameterGroup' ? currentView : 'parameterGroups'
}

function getSettingsPageState(currentData, settingsState) {
  const isNoProtocol = settingsService.isNoProtocol(settingsState.protocolMode)
  const isModbusProtocol = settingsService.isModbusProtocol(settingsState.protocolMode)
  const isStorageAccessProtocol = settingsService.isStorageAccessProtocol(settingsState.protocolMode)

  return {
    ...settingsState,
    activeParamView: resolveActiveParamView(currentData.activeParamView, settingsState),
    isNoProtocol,
    isModbusProtocol,
    isStorageAccessProtocol
  }
}

function getVisiblePageState(currentData) {
  const settingsState = settingsService.getState()
  const transportState = transport.getState()
  const isNoProtocol = settingsService.isNoProtocol(settingsState.protocolMode)
  const isModbusProtocol = settingsService.isModbusProtocol(settingsState.protocolMode)
  const isStorageAccessProtocol = settingsService.isStorageAccessProtocol(settingsState.protocolMode)
  const pageState = {
    ...parameterGroupService.getState(),
    ...themeService.getState(),
    ...settingsState,
    connectedDevice: transportState.connectedDevice,
    isNoProtocol,
    isModbusProtocol,
    isStorageAccessProtocol
  }

  return {
    ...pageState,
    activeParamView: resolveActiveParamView(currentData.activeParamView, settingsState)
  }
}

function getTransportPageState(transportState = transport.getState()) {
  return {
    connectedDevice: transportState.connectedDevice
  }
}

function createParameterDialogState(overrides = {}) {
  const registerType = getOption(parameterGroupService.REGISTER_TYPE_OPTIONS, 0)
  const dataType = getOption(parameterGroupService.DATA_TYPE_OPTIONS, 0)

  return {
    cancelText: '取消',
    confirmText: '确认',
    dataTypeIndex: 0,
    dataTypeText: dataType.label || '',
    groupId: '',
    groupName: '',
    mode: '',
    name: '',
    quantity: '1',
    registerIndex: -1,
    registerTypeIndex: 0,
    registerTypeText: registerType.label || '',
    remark: '',
    startAddress: '0000',
    title: '',
    textByteLength: '32',
    showTextLength: false,
    unit: '',
    visible: false,
    maxValue: '',
    minValue: '',
    conversionFormula: '',
    conversionFormulaErrorText: '',
    addressText: '',
    displayName: '',
    displayValue: '',
    enumExpanded: false,
    enumOptions: [],
    isEnumType: false,
    rawValueText: '',
    showDataType: false,
    showRange: false,
    showUnit: false,
    readOnly: false,
    metadataLocked: false,
    parsedStructRegisters: [],
    pollEnabled: true,
    showPollEnabled: false,
    sourceAddressText: '',
    sourceByteLength: '',
    sourceDefinitionName: '',
    sourceDisplayText: '',
    sourceEntryKind: '',
    sourceElementType: '',
    sourceMemoryArea: '',
    sourceSymbolName: '',
    sourceSymbolType: '',
    sourceTypeText: '',
    sourceValueType: '',
    structDefinition: '',
    structParsedSummary: '',
    ...overrides
  }
}

function createParameterGroupDialogState(group) {
  const isEdit = !!group
  const isStorageGroup = !!(group && group.sourceMemoryArea)
  const metadataLocked = !!(group && group.sourceMetadataLocked)
  const firstRegister = isEdit && Array.isArray(group.registers) ? (group.registers[0] || {}) : {}
  const registerTypeIndex = isEdit ? (group.registerTypeIndex || 0) : 0
  const registerType = getOption(parameterGroupService.REGISTER_TYPE_OPTIONS, registerTypeIndex)
  return createParameterDialogState({
    confirmText: isEdit ? '保存' : '确认',
    displayName: isEdit ? (group.displayName || group.name) : '',
    enumOptions: createEnumOptionRows(firstRegister),
    groupId: isEdit ? group.id : '',
    groupName: isEdit ? group.name : (isStorageGroup ? '结构体组' : '寄存器组'),
    layout: isEdit ? (group.layout || 'register') : 'register',
    metadataLocked,
    mode: isEdit ? 'editGroup' : 'createGroup',
    quantity: isEdit ? String(group.quantity || 1) : '1',
    pollEnabled: isEdit ? group.pollEnabled !== false : true,
    registerTypeIndex,
    registerTypeText: registerType.label || '',
    showPollEnabled: true,
    sourceAddressText: isEdit ? (group.sourceAddressText || group.startAddressText || '') : '',
    sourceByteLength: isEdit ? (group.sourceByteLength || group.byteLength || '') : '',
    sourceDefinitionName: isEdit ? (group.sourceDefinitionName || '') : '',
    sourceDisplayText: isEdit ? getStorageSourceDisplayText(group.sourceMemoryArea) : '',
    sourceEntryKind: isEdit ? (group.sourceEntryKind || '') : '',
    sourceElementType: isEdit ? (group.sourceElementType || '') : '',
    sourceMemoryArea: isEdit ? (group.sourceMemoryArea || '') : '',
    sourceMetaText: isEdit ? (group.sourceMetaText || '') : '',
    sourceSymbolName: isEdit ? (group.sourceSymbolName || '') : '',
    sourceSymbolType: isEdit ? (group.sourceSymbolType || '') : '',
    sourceTypeText: isEdit ? getStorageSourceTypeText(group) : '',
    sourceValueType: isEdit ? (group.sourceValueType || '') : '',
    startAddress: isEdit && group.startAddressText ? group.startAddressText.replace(/^0x/i, '') : '0000',
    title: metadataLocked
      ? getStorageDetailTitle(group)
      : (isStorageGroup
      ? (isEdit ? '编辑结构体组' : '添加结构体组')
      : (isEdit ? '编辑寄存器组' : '添加寄存器组')),
    visible: true
  })
}

function createParameterRegisterDialogState(mode, group, register, registerIndex) {
  const isView = mode === 'viewRegister'
  const metadataLocked = !!(register && register.sourceMetadataLocked) || !!(group && group.sourceMetadataLocked)
  const dataTypeIndex = register.dataTypeIndex || 0
  const dataType = getOption(parameterGroupService.DATA_TYPE_OPTIONS, dataTypeIndex)
  const isEnumType = isEnumTypeSource(register)
    || isEnumTypeSource(group)
    || !!(group && group.isStorageEnumScalar)

  return createParameterDialogState({
    cancelText: isView ? '关闭' : '取消',
    confirmText: isView ? '' : '保存',
    dataTypeIndex,
    dataTypeText: register.dataTypeText || dataType.label || '',
    displayName: register.displayName || register.name,
    groupId: group.id,
    groupName: group.name,
    metadataLocked,
    mode,
    name: register.name,
    registerIndex,
    registerTypeIndex: group.registerTypeIndex || 0,
    remark: register.remark || '',
    startAddress: group.startAddressText ? group.startAddressText.replace(/^0x/i, '') : '0000',
    textByteLength: String(register.textByteLength || '32'),
    showTextLength: !!register.showTextLength,
    unit: register.unit || '',
    visible: true,
    conversionFormula: isEnumType ? '' : (register.conversionFormula || ''),
    conversionFormulaErrorText: isEnumType ? '' : (register.conversionFormulaErrorText || ''),
    maxValue: isEnumType ? '' : (register.maxValue || ''),
    minValue: isEnumType ? '' : (register.minValue || ''),
    enumOptions: createEnumOptionRows(register),
    isEnumType,
    addressText: register.addressRangeText || register.addressText || '',
    displayValue: register.displayValue || '',
    rawValueText: register.rawValueText || '--',
    sourceAddressText: register.sourceAddressText || register.addressText || '',
    sourceByteLength: register.sourceByteLength || '',
    sourceDefinitionName: register.sourceDefinitionName || '',
    sourceDisplayText: getStorageSourceDisplayText(register.sourceMemoryArea || group.sourceMemoryArea),
    sourceEntryKind: register.sourceEntryKind || '',
    sourceElementType: register.sourceElementType || '',
    sourceMemoryArea: register.sourceMemoryArea || group.sourceMemoryArea || '',
    sourceMetaText: register.sourceMetaText || '',
    sourceSymbolName: register.sourceSymbolName || '',
    sourceSymbolType: register.sourceSymbolType || '',
    sourceTypeText: getStorageRegisterTypeText(register),
    sourceValueType: register.sourceValueType || '',
    showDataType: !!register.showDataType,
    showRange: !isEnumType && !!register.showRange,
    showPollEnabled: metadataLocked && isEnumType,
    showUnit: !isEnumType && !!register.showUnit,
    pollEnabled: group.pollEnabled !== false,
    readOnly: isView,
    title: metadataLocked ? getStorageDetailTitle(register) : (isView ? '寄存器信息' : '寄存器配置')
  })
}

function getDialogDataTypeState(dialog, dataTypeOptions, dataTypeIndex) {
  const dataType = getOption(dataTypeOptions, dataTypeIndex)
  const isTextType = dataType.kind === 'text'
  const showUnit = dataType.kind === 'number' && dataType.key !== 'hex'

  return {
    dataTypeIndex,
    dataTypeText: dataType.label || '',
    maxValue: isTextType ? '' : dialog.maxValue,
    minValue: isTextType ? '' : dialog.minValue,
    showRange: !isTextType,
    showTextLength: isTextType,
    showUnit,
    textByteLength: isTextType ? (dialog.textByteLength || '32') : dialog.textByteLength,
    unit: showUnit ? dialog.unit : ''
  }
}

function createParameterGroupConfig(dialog) {
  const registers = Array.isArray(dialog.parsedStructRegisters)
    ? dialog.parsedStructRegisters
    : []

  return {
    groupName: dialog.groupName,
    layout: registers.length ? 'struct' : (dialog.layout || 'register'),
    pollEnabled: dialog.pollEnabled !== false,
    quantity: registers.length ? String(registers.length) : dialog.quantity,
    registerTypeIndex: dialog.registerTypeIndex,
    startAddress: dialog.startAddress,
    ...(registers.length ? {
      registers: registers.map((register) => ({
        bitOffset: register.bitOffset,
        bitWidth: register.bitWidth,
        byteStart: register.byteStart,
        dataType: register.dataType,
        isBitField: !!register.isBitField,
        isStructField: true,
        name: register.name,
        structByteLength: register.structByteLength,
        textByteLength: register.textByteLength
      }))
    } : {})
  }
}

function createParameterRegisterChangedData(dialog, dataTypeOptions) {
  const dataType = getOption(dataTypeOptions, dialog.dataTypeIndex)
  const isTextType = dataType.kind === 'text'
  const isEnumType = !!dialog.isEnumType
  const showUnit = !isEnumType && dataType.kind === 'number' && dataType.key !== 'hex'
  const conversionFormula = isEnumType ? '' : String(dialog.conversionFormula || '').trim()

  validateValueFormula(conversionFormula)

  const changedData = {
    conversionFormula,
    maxValue: isTextType || isEnumType ? '' : dialog.maxValue,
    minValue: isTextType || isEnumType ? '' : dialog.minValue,
    remark: dialog.remark,
    unit: showUnit ? dialog.unit : ''
  }

  if (dialog.metadataLocked) return changedData

  validateCodeInfoVariableDataType(dialog, dataType)

  return {
    ...changedData,
    name: dialog.name,
    dataType: dataType.key,
    textByteLength: isTextType ? dialog.textByteLength : ''
  }
}

function findParameterGroup(groups, groupId) {
  return (groups || []).find((item) => item.id === groupId) || null
}

function findParameterRegister(groups, groupId, registerIndex) {
  const group = findParameterGroup(groups, groupId)
  const register = group && group.registers ? group.registers[registerIndex] : null

  return {
    group,
    register
  }
}

function getActiveParameterGroup(groups, groupId) {
  return findParameterGroup(groups, groupId) || null
}

module.exports = {
  createParameterGroupConfig,
  createParameterGroupDialogState,
  createParameterDialogState,
  createParameterRegisterChangedData,
  createParameterRegisterDialogState,
  findParameterGroup,
  findParameterRegister,
  getActiveParameterGroup,
  getDialogDataTypeState,
  getOption,
  getPageState,
  getSettingsPageState,
  getTransportPageState,
  getVisiblePageState,
  resolveActiveParamView
}
