const {
  formatExportStamp,
  isCancelError,
  loadSelectedFile,
  saveTextFileToChat
} = require('../../repositories/file.js')
const {
  getWxApi
} = require('../../utils/platform-utils.js')
const {
  cloneImportedGroup,
  getRegisterJsonValue
} = require('../../domain/parameter-groups/model.js')

const STORAGE_KEY = 'parameter-groups-json'
const STORAGE_MIGRATION_KEY = 'parameter-groups-json-protocol-migration'
const JSON_DOCUMENT_TYPE = 'parameter-groups'
const JSON_SCHEMA_VERSION = 2
const PROTOCOL_MODE = {
  MODBUS_RTU: 'modbus-rtu',
  STORAGE_ACCESS: 'storage-access'
}
const GROUP_SOURCE_FIELDS = [
  'addressUnit',
  'sourceAddress',
  'sourceAddressText',
  'sourceByteLength',
  'sourceMemoryArea',
  'sourceMemoryClass',
  'sourceSegment',
  'sourceSegmentModule',
  'sourceSymbolName'
]
const REGISTER_SOURCE_FIELDS = [
  'conversionFormula',
  'sourceAddress',
  'sourceAddressText',
  'sourceByteLength',
  'sourceBitOffset',
  'sourceBitWidth',
  'sourceMemoryArea',
  'sourceMemoryClass',
  'sourceSymbolName',
  'sourceSymbolType'
]
const REGISTER_STRUCT_FIELDS = [
  'bitOffset',
  'bitWidth',
  'byteStart',
  'isPlaceholderByteField',
  'isBitField',
  'structByteLength'
]

function pickFields(source, fields) {
  return fields.reduce((result, field) => {
    if (source && source[field] !== undefined && source[field] !== null && source[field] !== '') {
      result[field] = source[field]
    }

    return result
  }, {})
}

function toPersistedGroups(groups = []) {
  return groups.map((group) => ({
    layout: group.layout,
    name: group.name,
    registerType: group.registerType,
    startAddress: group.startAddress,
    quantity: group.quantity,
    registers: group.registers.map((register) => ({
      dataType: register.dataType,
      defaultValue: register.defaultValue,
      conversionFormula: register.conversionFormula,
      isStructField: register.isStructField,
      name: register.name,
      maxValue: register.maxValue,
      minValue: register.minValue,
      textByteLength: register.textByteLength,
      remark: register.remark,
      unit: register.unit,
      value: getRegisterJsonValue(register),
      ...pickFields(register, REGISTER_STRUCT_FIELDS),
      ...pickFields(register, REGISTER_SOURCE_FIELDS)
    })),
    ...pickFields(group, GROUP_SOURCE_FIELDS)
  }))
}

function toJsonData(groups = [], options = {}) {
  const jsonData = {
    groups: toPersistedGroups(groups),
    type: JSON_DOCUMENT_TYPE,
    version: JSON_SCHEMA_VERSION
  }

  if (options.includeExportedAt) {
    jsonData.exportedAt = new Date().toISOString()
  }

  return jsonData
}

function toJsonText(groups = [], options = {}) {
  return JSON.stringify(toJsonData(groups, options), null, 2)
}

function normalizeProtocolMode(protocolMode) {
  return protocolMode === PROTOCOL_MODE.MODBUS_RTU
    ? PROTOCOL_MODE.MODBUS_RTU
    : PROTOCOL_MODE.STORAGE_ACCESS
}

function getProtocolStorageKey(protocolMode) {
  return `${STORAGE_KEY}:${normalizeProtocolMode(protocolMode)}`
}

function parseJsonGroups(jsonText) {
  const parsed = typeof jsonText === 'string' ? JSON.parse(jsonText) : jsonText
  const groups = Array.isArray(parsed)
    ? parsed
    : (Array.isArray(parsed && parsed.groups) ? parsed.groups : parsed && parsed.parameterGroups)

  if (parsed && parsed.type && parsed.type !== JSON_DOCUMENT_TYPE) {
    throw new Error('JSON 文件不是参数组配置')
  }

  if (parsed && parsed.version && parsed.version !== JSON_SCHEMA_VERSION) {
    throw new Error('JSON 版本不兼容')
  }

  if (!Array.isArray(groups)) {
    throw new Error('JSON 中没有找到寄存器组数组')
  }

  return groups
}

function readStoredGroups(protocolMode = PROTOCOL_MODE.STORAGE_ACCESS) {
  const wxApi = getWxApi()
  if (typeof wxApi.getStorageSync !== 'function') return []

  try {
    const storageKey = getProtocolStorageKey(protocolMode)
    const jsonText = wxApi.getStorageSync(storageKey)
    if (jsonText) return parseJsonGroups(jsonText).map(cloneImportedGroup)
  } catch (error) {
    return []
  }

  return []
}

function persistGroups(groups = [], protocolMode = PROTOCOL_MODE.STORAGE_ACCESS) {
  const wxApi = getWxApi()
  if (typeof wxApi.setStorageSync !== 'function') return

  try {
    wxApi.setStorageSync(getProtocolStorageKey(protocolMode), toJsonText(groups))
  } catch (error) {}
}

function legacyGroupsNeedMigration() {
  const wxApi = getWxApi()
  if (typeof wxApi.getStorageSync !== 'function') return false

  try {
    return !!wxApi.getStorageSync(STORAGE_KEY) && !wxApi.getStorageSync(STORAGE_MIGRATION_KEY)
  } catch (error) {
    return false
  }
}

function migrateLegacyGroupsToProtocol(protocolMode = PROTOCOL_MODE.STORAGE_ACCESS) {
  const wxApi = getWxApi()
  if (typeof wxApi.getStorageSync !== 'function' || typeof wxApi.setStorageSync !== 'function') return false

  try {
    if (!legacyGroupsNeedMigration()) return false

    const normalizedProtocolMode = normalizeProtocolMode(protocolMode)
    const legacyJsonText = wxApi.getStorageSync(STORAGE_KEY)
    const targetStorageKey = getProtocolStorageKey(normalizedProtocolMode)
    const targetJsonText = wxApi.getStorageSync(targetStorageKey)

    if (legacyJsonText && !targetJsonText) {
      persistGroups(parseJsonGroups(legacyJsonText).map(cloneImportedGroup), normalizedProtocolMode)
    }

    wxApi.setStorageSync(STORAGE_MIGRATION_KEY, normalizedProtocolMode)
    return true
  } catch (error) {
    return false
  }
}

function getShareFileName() {
  return `parameter-groups-${formatExportStamp()}.json`
}

async function loadGroupsFromMessageFile() {
  const file = await loadSelectedFile('message', {
    encoding: 'utf8',
    extensionMessage: '请选择 .json 寄存器配置文件',
    extensions: ['json'],
    fallbackName: 'parameter-groups.json'
  })

  return parseJsonGroups(file.text).map(cloneImportedGroup)
}

async function saveGroupsToChat(groups = []) {
  if (!groups.length) {
    throw new Error('没有可保存的寄存器组')
  }

  const jsonText = toJsonText(groups, {
    includeExportedAt: true
  })

  await saveTextFileToChat(getShareFileName(), jsonText)

  return groups.length
}

module.exports = {
  JSON_DOCUMENT_TYPE,
  JSON_SCHEMA_VERSION,
  STORAGE_KEY,
  isCancelError,
  legacyGroupsNeedMigration,
  loadGroupsFromMessageFile,
  migrateLegacyGroupsToProtocol,
  parseJsonGroups,
  persistGroups,
  readStoredGroups,
  saveGroupsToChat,
  toJsonData,
  toJsonText
}
