function toArray(value) {
  if (value instanceof Set) {
    return [...value]
  }
  return Array.isArray(value) ? value : []
}

function toText(value) {
  return String(value ?? '').trim()
}

function uniqueTaskIds(value) {
  return [...new Set(toArray(value).map(toText).filter(Boolean))]
}

function unwrapTask(item = {}) {
  return item?.task && typeof item.task === 'object' ? item.task : item
}

export function resolveApprovalTaskId(item = {}) {
  return toText(unwrapTask(item)?.id ?? item?.task_id ?? item?.taskId)
}

export function resolveApprovalTaskSelectionMeta(item = {}) {
  const task = unwrapTask(item) || {}
  const taskId = resolveApprovalTaskId(item)
  const readOnlyReason = toText(task.readOnlyReason ?? task.read_only_reason)
  const batchBlockReasons = toArray(
    task.batchBlockReasons ?? task.batch_block_reasons_json
  ).map(toText).filter(Boolean)
  const availableActions = new Set(
    toArray(task.availableActions ?? task.available_actions).map(toText).filter(Boolean)
  )

  if (!taskId) {
    return { taskId: '', selectable: false, reason: '审批任务缺少任务标识。' }
  }
  if ((task.canAct ?? task.can_act) !== true) {
    return {
      taskId,
      selectable: false,
      reason: readOnlyReason || '当前用户没有处理该审批任务的权限。'
    }
  }
  if (toText(task.status).toLowerCase() !== 'pending') {
    return { taskId, selectable: false, reason: '该审批任务已不在待处理状态。' }
  }
  if (!availableActions.has('approve')) {
    return {
      taskId,
      selectable: false,
      reason: readOnlyReason || '当前任务不允许执行审批通过。'
    }
  }
  if ((task.batchEligible ?? task.batch_eligible) !== true) {
    return {
      taskId,
      selectable: false,
      reason: batchBlockReasons.join('；') || '该任务需要单独核对，不能批量审批。'
    }
  }
  if (Number(task.version) < 1) {
    return { taskId, selectable: false, reason: '任务版本无效，请刷新后重试。' }
  }
  return { taskId, selectable: true, reason: '' }
}

export function isApprovalTaskSelectable(item = {}) {
  return resolveApprovalTaskSelectionMeta(item).selectable
}

export function resolveCurrentPageApprovalSelection(items = [], selectedTaskIds = []) {
  const eligibleIds = toArray(items)
    .map(resolveApprovalTaskSelectionMeta)
    .filter((meta) => meta.selectable)
    .map((meta) => meta.taskId)
  const selectedSet = new Set(uniqueTaskIds(selectedTaskIds))
  const selectedEligibleIds = eligibleIds.filter((taskId) => selectedSet.has(taskId))
  const allSelected = eligibleIds.length > 0 && selectedEligibleIds.length === eligibleIds.length
  const someSelected = selectedEligibleIds.length > 0
  return {
    eligibleIds,
    selectedEligibleIds,
    eligibleCount: eligibleIds.length,
    selectedCount: selectedEligibleIds.length,
    allSelected,
    someSelected,
    indeterminate: someSelected && !allSelected
  }
}

export function toggleApprovalTaskSelection(selectedTaskIds, item, selected) {
  const current = uniqueTaskIds(selectedTaskIds)
  const currentSet = new Set(current)
  const meta = resolveApprovalTaskSelectionMeta(item)
  if (!meta.taskId) {
    return current
  }

  const shouldSelect = typeof selected === 'boolean' ? selected : !currentSet.has(meta.taskId)
  if (!shouldSelect) {
    currentSet.delete(meta.taskId)
    return [...currentSet]
  }
  if (!meta.selectable) {
    return current
  }
  currentSet.add(meta.taskId)
  return [...currentSet]
}

export function toggleCurrentPageApprovalSelection(selectedTaskIds, items = [], selected) {
  const current = uniqueTaskIds(selectedTaskIds)
  const currentSet = new Set(current)
  const pageState = resolveCurrentPageApprovalSelection(items, current)
  const pageTaskIds = new Set(toArray(items).map(resolveApprovalTaskId).filter(Boolean))
  const shouldSelect = typeof selected === 'boolean' ? selected : !pageState.allSelected

  if (shouldSelect) {
    pageState.eligibleIds.forEach((taskId) => currentSet.add(taskId))
  } else {
    pageTaskIds.forEach((taskId) => currentSet.delete(taskId))
  }
  return [...currentSet]
}

export function reconcileApprovalTaskSelectionWithPage(selectedTaskIds, items = []) {
  const current = uniqueTaskIds(selectedTaskIds)
  const eligibleIds = new Set(
    toArray(items)
      .map(resolveApprovalTaskSelectionMeta)
      .filter((meta) => meta.selectable)
      .map((meta) => meta.taskId)
  )
  const pageIds = new Set(toArray(items).map(resolveApprovalTaskId).filter(Boolean))
  return current.filter((taskId) => !pageIds.has(taskId) || eligibleIds.has(taskId))
}

function stableFilterValue(value) {
  if (Array.isArray(value)) {
    return value.map(stableFilterValue).sort((left, right) => String(left).localeCompare(String(right)))
  }
  if (value && typeof value === 'object') {
    return Object.fromEntries(
      Object.keys(value)
        .sort()
        .map((key) => [key, stableFilterValue(value[key])])
    )
  }
  return value ?? null
}

export function buildApprovalTaskFilterSignature(filters = {}) {
  const ignoredKeys = new Set(['page', 'pageSize', 'page_size'])
  const normalized = Object.fromEntries(
    Object.entries(filters || {})
      .filter(([key]) => !ignoredKeys.has(key))
      .sort(([left], [right]) => left.localeCompare(right))
      .map(([key, value]) => [key, stableFilterValue(value)])
  )
  return JSON.stringify(normalized)
}

export function createApprovalTaskSelectionState(options = {}) {
  return {
    selectedTaskIds: uniqueTaskIds(options.selectedTaskIds),
    filterSignature: buildApprovalTaskFilterSignature(options.filters)
  }
}

export function syncApprovalTaskSelectionFilters(state = {}, filters = {}) {
  const nextSignature = buildApprovalTaskFilterSignature(filters)
  const currentSignature = toText(state.filterSignature)
  return {
    ...state,
    selectedTaskIds: currentSignature === nextSignature
      ? uniqueTaskIds(state.selectedTaskIds)
      : [],
    filterSignature: nextSignature
  }
}

export function reconcileApprovalTaskSelectionAfterBatch(selectedTaskIds, batchResult = {}) {
  const completedTaskIds = new Set(
    toArray(batchResult.items)
      .filter((item) => ['succeeded', 'replayed'].includes(toText(item?.status).toLowerCase()))
      .map((item) => toText(item?.taskId ?? item?.task_id))
      .filter(Boolean)
  )
  return uniqueTaskIds(selectedTaskIds).filter((taskId) => !completedTaskIds.has(taskId))
}
