import { computed, inject, onMounted, ref, watch } from 'vue'
import { useMutation, useQuery } from '@tanstack/vue-query'
import type {
  KbxBarcodeEvent,
  KbxWmsExceptionType,
  KbxWmsPickingTask,
  KbxWmsScanCommand,
  KbxWmsSetQuantityCommand,
  KbxAsyncState,
} from '@kbx/contracts'
import { isKbxProblem, isRetryableKbxProblem } from '@kbx/contracts'
import { playKbxWmsFeedback, useKbxNetworkState } from '@kbx/ui'
import {
  getPickingTask,
  reportPickingException,
  scanPickingBarcode,
  setPickingQuantity,
  startPickingTask,
} from './pickingApi'
import { kbxTelemetry, startKbxTask } from '../../../telemetry/kbxTelemetryClient'
import { pickingScreen } from './picking.definition'
import { queuePickingScan, readPickingRetryQueue, removePickingScan } from './wmsRetryQueue'
import { KbxScreenPreferenceScopeKey } from '../../../preferences/screenPreferenceStore'

function newIdempotencyKey(taskId: string) {
  return `${taskId}:${crypto.randomUUID()}`
}

type ScopedScanMutation = { command: KbxWmsScanCommand; scope: string }

export function useWmsPicking(taskId: string) {
  const { online } = useKbxNetworkState()
  const retryScope = inject(KbxScreenPreferenceScopeKey, ref(''))
  const task = ref<KbxWmsPickingTask | null>(null)
  const message = ref('')
  const pendingCommands = ref(0)
  const syncing = ref(false)
  const exceptionOpen = ref(false)
  const quantityOpen = ref(false)
  const lastConfirmedScan = ref<{ barcode:string; confirmedAt:string } | null>(null)
  let taskTelemetry:ReturnType<typeof startKbxTask>|undefined

  const query = useQuery({
    queryKey: ['wms-picking-task', taskId],
    queryFn: () => getPickingTask(taskId),
  })

  const contentState = computed<KbxAsyncState>(() => query.isLoading.value
    ? 'loading'
    : query.isError.value
      ? 'error'
      : task.value
        ? 'ready'
        : 'empty')

  watch(() => query.data.value, value => {
    if (value) task.value = value
  }, { immediate: true })

  const startMutation = useMutation({
    mutationFn: () => startPickingTask(taskId, task.value?.version ?? 0),
    onSuccess(result) {
      taskTelemetry=startKbxTask(pickingScreen.id,pickingScreen.version,'wms-picking')
      task.value = result
      message.value = result.message ?? '피킹을 시작합니다.'
      playKbxWmsFeedback('neutral')
    },
    onError(error) {
      message.value = isKbxProblem(error) ? error.title : '작업을 시작하지 못했습니다.'
      playKbxWmsFeedback('error')
      void query.refetch()
    },
  })

  const scanMutation = useMutation({
    mutationFn: ({ command }: ScopedScanMutation) => scanPickingBarcode(command),
    onSuccess(result, input) {
      removePickingScan(input.command.idempotencyKey, input.scope)
      if (input.scope !== retryScope.value) return
      pendingCommands.value = readPickingRetryQueue(input.scope).filter(x => x.command.taskId === taskId).length
      task.value = result.task
      message.value = result.message
      lastConfirmedScan.value = { barcode: input.command.barcode, confirmedAt: new Date().toISOString() }
      playKbxWmsFeedback(result.feedback)
      if(result.task.stage==='completed'){taskTelemetry?.complete('success');taskTelemetry=undefined}
    },
    onError(error, input) {
      const sameScope = input.scope === retryScope.value
      // 4xx is an authoritative server rejection. Only network/5xx ambiguity is safe-retried.
      if (isKbxProblem(error) && !isRetryableKbxProblem(error)) {
        if (sameScope) {
          message.value = error.title ?? '현재 작업 상태를 다시 확인하세요.'
          playKbxWmsFeedback('error')
          void query.refetch()
        }
        return
      }

      // The request may have committed even if the response was lost. Preserve the scope that issued
      // the command so a tenant/user switch cannot move an ambiguous scan into another operator queue.
      queuePickingScan(input.command, input.scope)
      if (!sameScope) return
      pendingCommands.value = readPickingRetryQueue(input.scope).filter(x => x.command.taskId === taskId).length
      message.value = '서버 확인이 필요합니다. 연결되면 같은 작업을 안전하게 재전송합니다.'
      playKbxWmsFeedback('warning')
    },
  })


  const quantityMutation = useMutation({
    mutationFn: (pickedQty: number) => {
      if (!task.value?.currentLine) throw new Error('Current line not loaded')
      const command: KbxWmsSetQuantityCommand = {
        taskId,
        lineId: task.value.currentLine.lineId,
        pickedQty,
        idempotencyKey: newIdempotencyKey(taskId),
        expectedVersion: task.value.version,
      }
      return setPickingQuantity(command)
    },
    onSuccess(result) {
      task.value = result.task
      message.value = result.message
      quantityOpen.value = false
      playKbxWmsFeedback(result.feedback)
    },
    onError(error) {
      message.value = isKbxProblem(error)
        ? error.title
        : '수량을 반영하지 못했습니다.'
      playKbxWmsFeedback('error')
      void query.refetch()
    },
  })

  const exceptionMutation = useMutation({
    mutationFn: ({ type, memo }: { type: KbxWmsExceptionType; memo?: string }) => {
      if (!task.value) throw new Error('Task not loaded')
      return reportPickingException({
        taskId,
        lineId: task.value.currentLine?.lineId,
        type,
        memo,
        idempotencyKey: newIdempotencyKey(taskId),
        expectedVersion: task.value.version,
      })
    },
    onSuccess(result, input) {
      task.value = result
      kbxTelemetry.track('manual.intervention',{screenId:pickingScreen.id,screenVersion:pickingScreen.version,taskSessionId:taskTelemetry?.id,attributes:{workType:'wms-picking',reasonCode:input.type,exceptionType:input.type}})
      exceptionOpen.value = false
      message.value = result.message ?? '예외를 등록했습니다.'
      playKbxWmsFeedback('warning')
    },
    onError(error) {
      message.value = isKbxProblem(error) ? error.title : '예외를 등록하지 못했습니다.'
      playKbxWmsFeedback('error')
      void query.refetch()
    },
  })

  const scanEnabled = computed(() =>
    Boolean(
      task.value &&
      online.value &&
      pendingCommands.value === 0 &&
      !scanMutation.isPending.value &&
      ['await-location', 'await-item'].includes(task.value.stage),
    ),
  )

  const scanLabel = computed(() => {
    switch (task.value?.stage) {
      case 'await-location': return '위치 스캔'
      case 'await-item': return '상품 스캔'
      case 'processing': return '서버 확인 중'
      case 'completed': return '피킹 완료'
      default: return '스캔 대기'
    }
  })

  async function handleScan(event: KbxBarcodeEvent) {
    if (!task.value || !scanEnabled.value) return
    taskTelemetry?.interaction('scan')
    kbxTelemetry.track('command.execute',{screenId:pickingScreen.id,screenVersion:pickingScreen.version,taskSessionId:taskTelemetry?.id,attributes:{commandId:'scan',operationKind:'command'}})

    const command: KbxWmsScanCommand = {
      taskId,
      barcode: event.normalizedValue,
      source: event.source,
      idempotencyKey: newIdempotencyKey(taskId),
      expectedVersion: task.value.version,
      occurredAt: new Date(event.occurredAt).toISOString(),
    }

    const scope = retryScope.value
    await scanMutation.mutateAsync({ command, scope }).catch(() => undefined)
  }

  async function flushRetryQueue() {
    if (!online.value || syncing.value) return
    const scope = retryScope.value
    const queued = readPickingRetryQueue(scope)
      .filter(x => x.command.taskId === taskId)
      .sort((a, b) => a.queuedAt.localeCompare(b.queuedAt))
    if (!queued.length) return

    syncing.value = true
    try {
      // Authoritative picking is sequence-sensitive. Replay one-by-one and stop at first failure.
      for (const item of queued) {
        try {
          const result = await scanPickingBarcode(item.command)
          removePickingScan(item.command.idempotencyKey, scope)
          if (scope !== retryScope.value) break
          task.value = result.task
          message.value = result.message
          lastConfirmedScan.value = { barcode: item.command.barcode, confirmedAt: new Date().toISOString() }
        } catch (error) {
          if (isKbxProblem(error) && !isRetryableKbxProblem(error)) {
            removePickingScan(item.command.idempotencyKey, scope)
            if (scope === retryScope.value) {
              message.value = error.title ?? '작업 상태가 변경되었습니다. 최신 상태를 확인하세요.'
              playKbxWmsFeedback('error')
              await query.refetch()
            }
          }
          break
        }
      }
    } finally {
      const scopeChanged = scope !== retryScope.value
      pendingCommands.value = readPickingRetryQueue(retryScope.value).filter(x => x.command.taskId === taskId).length
      syncing.value = false
      if (scopeChanged && online.value) void flushRetryQueue()
    }
  }

  watch(online, value => {
    if (value) void flushRetryQueue()
  })

  watch(retryScope, (scope, previous) => {
    if (scope === previous) return
    // Shared-PDA context switch is a hard UX/security boundary. Never carry prior operator scan truth
    // or overlay state into the next tenant/user scope, and only count/replay that new scope's queue.
    taskTelemetry?.abandon('scope_changed')
    taskTelemetry = undefined
    lastConfirmedScan.value = null
    exceptionOpen.value = false
    quantityOpen.value = false
    pendingCommands.value = readPickingRetryQueue(scope).filter(x => x.command.taskId === taskId).length
    message.value = pendingCommands.value > 0 ? '현재 사용자 범위의 미확인 스캔을 서버와 다시 확인합니다.' : ''
    if (online.value) void flushRetryQueue()
  })

  onMounted(() => {
    pendingCommands.value = readPickingRetryQueue(retryScope.value).filter(x => x.command.taskId === taskId).length
    if (online.value) void flushRetryQueue()
  })

  return {
    task,
    message,
    online,
    pendingCommands,
    syncing,
    lastConfirmedScan,
    scanEnabled,
    scanLabel,
    exceptionOpen,
    quantityOpen,
    loading: query.isLoading,
    contentState,
    reload: () => query.refetch(),
    starting: startMutation.isPending,
    scanning: scanMutation.isPending,
    reportingException: exceptionMutation.isPending,
    settingQuantity: quantityMutation.isPending,
    start: () => startMutation.mutateAsync(),
    handleScan,
    openException: () => { exceptionOpen.value = true },
    openQuantity: () => { quantityOpen.value = true },
    closeQuantity: () => { quantityOpen.value = false },
    setQuantity: (quantity: number) => quantityMutation.mutateAsync(quantity),
    closeException: () => { exceptionOpen.value = false },
    reportException: (type: KbxWmsExceptionType, memo?: string) => exceptionMutation.mutateAsync({ type, memo }),
  }
}
