const {
  isCancelError,
  loadGroupsFromMessageFile,
  saveGroupsToChat
} = require('./persistence.js')
const {
  completeStructInstanceGroups,
  mergeImportedGroups,
  parseStructDefinition
} = require('./imports.js')
const storageAccessService = require('../storage-access/service.js')
const parameterGroupIo = require('./io.js')
const settingsService = require('../../store/settings-store.js')
const store = require('./store.js')
const {
  loadSelectedFile
} = require('../../repositories/file.js')
const transport = require('../../transport/ble-core.js')
const {
  DATA_TYPE_OPTIONS,
  MAX_STORAGE_ADDRESS,
  REGISTER_TYPE_OPTIONS,
  cloneImportedGroup,
  getDataType,
  isAddressRangeOverflow,
  normalizeGroup,
  normalizeGroupConfig,
  validateRegisterValue
} = require('../../domain/parameter-groups/model.js')
const {
  findGroup,
  getGroups,
  getState,
  init,
  setGroups,
  setStorageCodeInfo,
  switchProtocolMode,
  subscribe,
  updateGroups
} = store

let initialized = false

function getActiveProtocolMode() {
  return settingsService.getState().protocolMode
}

function isStorageAccessProtocolMode(protocolMode) {
  return settingsService.isStorageAccessProtocol(protocolMode)
}

function isGroupAddressRangeOverflow(group = {}, protocolMode = getActiveProtocolMode()) {
  if (!isStorageAccessProtocolMode(protocolMode)) {
    return isAddressRangeOverflow(group.startAddress, group.quantity)
  }

  const startAddress = Math.max(0, Math.floor(Number(group.startAddress) || 0))
  const addressSpan = Math.max(1, Math.floor(Number(group.byteLength || group.sourceByteLength || group.quantity) || 1))

  return startAddress + addressSpan - 1 > MAX_STORAGE_ADDRESS
}

function getAddressOverflowText(protocolMode = getActiveProtocolMode()) {
  return isStorageAccessProtocolMode(protocolMode)
    ? '地址范围超出 0xFFFFFFFF'
    : '地址范围超出 0xFFFF'
}

function isUnconfiguredRegister(register = {}) {
  return getDataType(register.dataType).key === 'raw'
}

function getUnconfiguredRegisterName(register = {}, index = 0) {
  return register.name || `变量 ${index + 1}`
}

function findUnconfiguredRegister(group = {}) {
  const registers = Array.isArray(group.registers) ? group.registers : []
  const index = registers.findIndex(isUnconfiguredRegister)

  return index >= 0
    ? {
      index,
      register: registers[index]
    }
    : null
}

function initParameterGroups() {
  settingsService.init()
  init(getActiveProtocolMode())
  switchProtocolMode(getActiveProtocolMode(), {
    notify: false
  })

  if (initialized) return

  settingsService.subscribe((settingsState) => {
    switchProtocolMode(settingsState.protocolMode)
  })
  initialized = true
}

async function importJsonFromMessageFile() {
  try {
    const importedGroups = (await loadGroupsFromMessageFile()).map(normalizeGroup)
    if (!importedGroups.length) throw new Error('JSON 中没有可导入的寄存器组')
    const merged = mergeImportedGroups(getGroups(), importedGroups)

    setGroups(merged.groups)

    return merged.changedCount || 0
  } catch (error) {
    const message = error && error.message ? error.message : '导入参数组配置失败'
    transport.showCommandAlert('参数组导入', message)
    return 0
  }
}

function completeStructInstanceGroupsWithStructSource(sourceText, options = {}) {
  const completed = completeStructInstanceGroups(getGroups(), sourceText, options)

  if (!completed.completedCount) {
    throw new Error('没有找到可匹配的结构体实例')
  }

  setGroups(completed.groups)

  return completed
}

function mergeImportedGroupsIntoState(importedGroups = [], options = {}) {
  const normalizedGroups = importedGroups.map(cloneImportedGroup).map(normalizeGroup)
  const merged = mergeImportedGroups(getGroups(), normalizedGroups, options)

  if (normalizedGroups.length) {
    setGroups(merged.groups)
  }

  return merged
}

function clearStorageAccessGroups() {
  setGroups([], {
    protocolMode: settingsService.PROTOCOL_MODE.STORAGE_ACCESS
  })
  setStorageCodeInfo(null)

  return true
}

async function completeStructInstanceGroupsWithStructFile(options = {}) {
  try {
    const file = await loadSelectedFile('auto', {
      encoding: 'utf8',
      extensionMessage: '请选择 .h 或 .c 结构体/枚举定义文件',
      extensions: ['h', 'c', 'txt'],
      fallbackName: 'structs.h'
    })

    return completeStructInstanceGroupsWithStructSource(file.text, options)
  } catch (error) {
    const message = error && error.message ? error.message : '结构体/枚举补全失败'
    transport.showCommandAlert('结构体/枚举补全', message)

    return {
      completedCount: 0,
      skippedCount: 0,
      structCount: 0,
      variableCount: 0
    }
  }
}

async function saveJsonToChat() {
  try {
    const result = await saveGroupsToChat(getGroups())

    return result && result.count
      ? result
      : {
        count: 0
      }
  } catch (error) {
    const message = error && error.message ? error.message : '保存参数组配置失败'

    if (!isCancelError(error)) {
      transport.showCommandAlert('参数组保存', message)
    }

    return {
      count: 0
    }
  }
}

async function syncFromStorageAccessCodeInfo(options = {}) {
  const result = await storageAccessService.syncCodeInfo(options)
  if (!result || !result.ok) return result

  setStorageCodeInfo(result.codeInfo)
  const merged = mergeImportedGroupsIntoState(result.importedGroups || [], {
    preserveExistingRemarks: true,
    preserveExistingPollEnabled: true,
    preserveExistingStructLayout: true
  })

  return {
    ...result,
    addedGroups: merged.addedGroupCount,
    addedRegisters: merged.addedRegisterCount,
    updatedGroups: merged.updatedGroupCount,
    updatedRegisters: merged.updatedRegisterCount
  }
}

function addGroupFromConfig(config = {}) {
  const protocolMode = getActiveProtocolMode()
  let groupConfig

  try {
    groupConfig = normalizeGroupConfig({
      ...(isStorageAccessProtocolMode(protocolMode) ? { addressUnit: 'byte', sourceMemoryArea: config.sourceMemoryArea || 'XDATA' } : {}),
      ...config
    })
  } catch (error) {
    transport.showCommandAlert('参数组添加', error.message || '寄存器组配置无效')
    return null
  }

  if (isGroupAddressRangeOverflow(groupConfig, protocolMode)) {
    transport.showCommandAlert('参数组添加', getAddressOverflowText(protocolMode))
    return null
  }

  const registers = Array.isArray(config.registers) ? config.registers : []
  const group = normalizeGroup({
    ...groupConfig,
    layout: config.layout,
    ...(registers.length ? { registers } : {}),
    expanded: false
  })

  if (group.addressOverflow) {
    transport.showCommandAlert('参数组添加', getAddressOverflowText(protocolMode))
    return null
  }

  setGroups(getGroups().concat(group))

  return group
}

function updateGroupConfig(groupId, config = {}) {
  const protocolMode = getActiveProtocolMode()
  const group = findGroup(groupId)
  if (!group) return null

  let nextConfig
  try {
    nextConfig = normalizeGroupConfig({
      ...group,
      ...config
    })
  } catch (error) {
    transport.showCommandAlert('参数组更新', error.message || '寄存器组配置无效')
    return null
  }

  if (isGroupAddressRangeOverflow(nextConfig, protocolMode)) {
    transport.showCommandAlert('参数组更新', getAddressOverflowText(protocolMode))
    return null
  }

  const registers = Array.isArray(config.registers) ? config.registers : group.registers
  const updatedGroup = normalizeGroup({
    ...group,
    ...nextConfig,
    registers
  })

  if (updatedGroup.addressOverflow) {
    transport.showCommandAlert('参数组更新', getAddressOverflowText(protocolMode))
    return null
  }

  setGroups(getGroups().map((item) => (
    item.id === groupId ? updatedGroup : item
  )))

  return updatedGroup
}

function setGroupExpanded(groupId, expanded) {
  updateGroups((group) => group.id === groupId
    ? {
      ...group,
      deleteVisible: false,
      expanded
    }
    : group)
}

function setGroupDeleteVisible(groupId, deleteVisible) {
  updateGroups((group) => group.id === groupId
    ? {
      ...group,
      deleteVisible
    }
    : group)
}

function removeGroup(groupId) {
  setGroups(getGroups().filter((group) => group.id !== groupId))
}

function reorderRegister(groupId, fromIndex, toIndex) {
  const group = findGroup(groupId)
  if (!group) return null
  if (group.isStructLayout) return group

  const registers = group.registers.slice()
  const sourceIndex = Number(fromIndex)
  const targetIndex = Number(toIndex)
  if (!Number.isInteger(sourceIndex) || !Number.isInteger(targetIndex)) return null
  if (sourceIndex < 0 || sourceIndex >= registers.length) return null

  const safeTargetIndex = Math.min(Math.max(targetIndex, 0), registers.length - 1)
  if (safeTargetIndex === sourceIndex) return group

  const moved = registers.splice(sourceIndex, 1)[0]
  registers.splice(safeTargetIndex, 0, moved)

  const updatedGroup = normalizeGroup({
    ...group,
    quantity: registers.length,
    registers
  })

  setGroups(getGroups().map((item) => (
    item.id === groupId ? updatedGroup : item
  )))

  return updatedGroup
}

function updateRegister(groupId, registerIndex, changedData) {
  updateGroups((group) => {
    if (group.id !== groupId) return group
    const shouldResetReadState = Object.prototype.hasOwnProperty.call(changedData, 'dataType')
      || Object.prototype.hasOwnProperty.call(changedData, 'textByteLength')

    return {
      ...group,
      registers: group.registers.map((register, currentIndex) => (
        currentIndex === registerIndex
          ? {
            ...register,
            ...(shouldResetReadState ? { rawBytes: [], rawValue: null, rawWords: [] } : {}),
            ...changedData
          }
          : register
      ))
    }
  })
}

function updateRegisterValue(groupId, registerIndex, value) {
  updateRegister(groupId, registerIndex, {
    inputValue: value,
    isDirty: true
  })
}

function validateRegisterInputValue(groupId, registerIndex, value) {
  const group = findGroup(groupId)
  if (!group) return false

  const register = group.registers[registerIndex]
  if (!register) return false

  return validateRegisterValue(register, value)
}

async function readGroup(groupId, options = {}) {
  const protocolMode = options.protocolMode || getActiveProtocolMode()
  const group = findGroup(groupId, protocolMode)
  if (!group) return false
  if (group.addressOverflow) {
    transport.showCommandAlert('参数组读取', getAddressOverflowText(protocolMode))
    return false
  }

  const readResult = await parameterGroupIo.readGroup(group, {
    ...options,
    useStorageAccess: isStorageAccessProtocolMode(protocolMode)
  })
  if (!readResult) return false

  updateGroups((item) => {
    if (item.id !== groupId) return item

    return readResult.applyToGroup(item)
  }, {
    protocolMode
  })

  return true
}

async function writeRegister(groupId, registerIndex) {
  const protocolMode = getActiveProtocolMode()
  const group = findGroup(groupId, protocolMode)
  const register = group && group.registers ? group.registers[registerIndex] : null
  if (!group || !register) return false
  if (!group.writable) {
    transport.showCommandAlert('参数组写入', '当前变量为只读')
    return false
  }
  if (group.addressOverflow) {
    transport.showCommandAlert('参数组写入', getAddressOverflowText(protocolMode))
    return false
  }
  if (isUnconfiguredRegister(register)) {
    transport.showCommandAlert('参数组写入', `${getUnconfiguredRegisterName(register, registerIndex)} 需要先配置数据类型`)
    return false
  }

  const writeResult = await parameterGroupIo.writeRegister(group, registerIndex, {
    useStorageAccess: isStorageAccessProtocolMode(protocolMode)
  })
  if (!writeResult) return false

  updateGroups((item) => {
    if (item.id !== groupId) return item

    return writeResult.applyToGroup(item)
  }, {
    protocolMode
  })

  return true
}

async function writeGroup(groupId, options = {}) {
  const protocolMode = options.protocolMode || getActiveProtocolMode()
  const group = findGroup(groupId, protocolMode)
  if (!group) return false
  if (!group.writable) {
    transport.showCommandAlert('参数组写入', '当前寄存器组为只读')
    return false
  }
  if (group.addressOverflow) {
    transport.showCommandAlert('参数组写入', getAddressOverflowText(protocolMode))
    return false
  }
  const unconfigured = findUnconfiguredRegister(group)
  if (unconfigured) {
    transport.showCommandAlert('参数组写入', `${getUnconfiguredRegisterName(unconfigured.register, unconfigured.index)} 需要先配置数据类型`)
    return false
  }

  const writeResult = await parameterGroupIo.writeGroup(group, {
    ...options,
    useStorageAccess: isStorageAccessProtocolMode(protocolMode)
  })
  if (!writeResult) return false

  updateGroups((item) => {
    if (item.id !== groupId) return item

    return writeResult.applyToGroup(item)
  }, {
    protocolMode
  })

  return true
}

module.exports = {
  DATA_TYPE_OPTIONS,
  REGISTER_TYPE_OPTIONS,
  addGroupFromConfig,
  clearStorageAccessGroups,
  completeStructInstanceGroups,
  completeStructInstanceGroupsWithStructFile,
  completeStructInstanceGroupsWithStructSource,
  getState,
  importJsonFromMessageFile,
  init: initParameterGroups,
  mergeImportedGroups: mergeImportedGroupsIntoState,
  parseStructDefinition,
  readGroup,
  removeGroup,
  reorderRegister,
  saveJsonToChat,
  setGroupDeleteVisible,
  setGroupExpanded,
  subscribe,
  syncFromStorageAccessCodeInfo,
  updateGroupConfig,
  updateRegister,
  updateRegisterValue,
  validateRegisterInputValue,
  writeRegister,
  writeGroup
}
