import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import {
  countWorkerActiveOrders,
  createWorkOrderEvent,
  grabWorkOrder,
  joinWorkOrderShare,
  type WorkOrderRow,
} from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import { createHttpError } from '../../utils/http.js'
import { nowIso } from '../../utils/time.js'
import {
  mapWorkOrderForWorker,
  mapWorkOrderShare,
  normalizePositiveInteger,
  resolveFreezeDepositAmount,
  resolveWorkerPermissions,
  throwGrabWorkOrderFailure,
} from './mappers.js'
import { getWorkerHallConfig } from './worker-hall-config-service.js'
import { publishWorkOrderParticipantChange } from './worker-order-realtime-service.js'
import {
  getRequiredWorkOrder,
  getRequiredWorker,
  requireActiveWorkerSession,
  type WorkerSession,
} from './worker-session-context-service.js'
import {
  publishWorkerWalletRealtimeChange,
  publishWorkOrderRealtimeChange,
} from '../realtime/realtime-event-service.js'

export async function grabWorkerHallOrder(workOrderId: number | string, session: WorkerSession) {
  requireActiveWorkerSession(session)
  const worker = await getRequiredWorker(session.workerId)
  const permissions = resolveWorkerPermissions(worker)
  const hallConfig = getWorkerHallConfig()
  const now = nowIso()
  const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
  const visibleAfterIso =
    visibleDelaySeconds > 0 ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString() : ''
  const workOrder = await getRequiredWorkOrder(workOrderId)
  if (workOrder.status !== WORK_ORDER_STATUS.OPEN || workOrder.assigned_worker_id) {
    throw createHttpError('该订单已不可抢', {
      statusCode: 409,
      errorCode: 'work_order_not_open',
    })
  }

  if (workOrder.sharing_enabled) {
    const result = await joinWorkOrderShare({
      workOrderId: Number(workOrder.id),
      workerId: Number(worker.id),
      quantity: 0,
      takeRemaining: true,
      depositFreeAmount: permissions.depositFreeAmount,
      maxActiveOrders: permissions.maxActiveOrders,
      hallCandidateLimit: hallConfig.maxVisibleOrders,
      hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
      visibleAfterIso,
      now,
    })
    if (!result.share) {
      throw resolveWorkOrderShareJoinError(result.failureReason)
    }
    await publishWorkOrderParticipantChange(Number(workOrder.id), {
      workerIds: [Number(worker.id)],
      hallChanged: true,
      walletChangedWorkerIds: [Number(worker.id)],
    })
    return { order: mapWorkOrderForWorker(workOrder, permissions, result.share) }
  }

  const activeCount = await countWorkerActiveOrders(worker.id)
  if (activeCount >= permissions.maxActiveOrders) {
    throw createHttpError('已达到当前等级最大同时接单量', {
      statusCode: 409,
      errorCode: 'worker_active_order_limit',
    })
  }

  const freezeAmount = resolveFreezeDepositAmount(workOrder, permissions)
  if (Number(worker.available_amount || 0) < freezeAmount) {
    throw createHttpError('余额不足，无法冻结所需押金', {
      statusCode: 409,
      errorCode: 'worker_deposit_insufficient',
    })
  }

  const grabbed = await grabWorkOrder({
    workOrderId: Number(workOrder.id),
    workerId: Number(worker.id),
    depositAmount: freezeAmount,
    maxActiveOrders: permissions.maxActiveOrders,
    hallCandidateLimit: hallConfig.maxVisibleOrders,
    hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
    visibleAfterIso,
    deadlineAt: resolveWorkOrderDeadlineAt(workOrder, now),
    now,
  })
  if (!grabbed.order) {
    throwGrabWorkOrderFailure(grabbed.failureReason)
  }

  await createWorkOrderEvent({
    workOrderId: workOrder.id,
    actorType: 'worker',
    actorId: String(worker.id),
    eventType: 'grabbed',
    fromStatus: workOrder.status,
    toStatus: grabbed.order.status,
    payloadJson: JSON.stringify({
      workerId: worker.id,
      workerName: worker.display_name || worker.username,
      internal: worker.worker_type === 'internal',
    }),
    now: nowIso(),
  })
  publishWorkOrderRealtimeChange({
    workOrderId: Number(workOrder.id),
    workerIds: [Number(worker.id)],
    hallChanged: true,
  })
  publishWorkerWalletRealtimeChange(Number(worker.id))

  return { order: mapWorkOrderForWorker(grabbed.order, permissions) }
}

export async function joinWorkerSharingOrder(
  workOrderId: number | string,
  payload: JsonObject = {},
  session: WorkerSession,
) {
  requireActiveWorkerSession(session)
  const worker = await getRequiredWorker(session.workerId)
  const permissions = resolveWorkerPermissions(worker)
  const hallConfig = getWorkerHallConfig()
  const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
  const visibleAfterIso =
    visibleDelaySeconds > 0 ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString() : ''
  const quantity = normalizePositiveInteger(payload.quantity, 1)
  const now = nowIso()
  const result = await joinWorkOrderShare({
    workOrderId: Number(workOrderId),
    workerId: Number(worker.id),
    quantity,
    depositFreeAmount: permissions.depositFreeAmount,
    maxActiveOrders: permissions.maxActiveOrders,
    hallCandidateLimit: hallConfig.maxVisibleOrders,
    hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
    visibleAfterIso,
    now,
  })
  if (!result.share) {
    throw resolveWorkOrderShareJoinError(result.failureReason)
  }
  await publishWorkOrderParticipantChange(Number(workOrderId), {
    workerIds: [Number(worker.id)],
    hallChanged: true,
    walletChangedWorkerIds: [Number(worker.id)],
  })
  return { share: mapWorkOrderShare(result.share) }
}

function resolveWorkOrderDeadlineAt(workOrder: WorkOrderRow, now: string): string | null {
  const timeoutMinutes = Number(workOrder.timeout_minutes || 0)
  if (timeoutMinutes <= 0) return null
  return new Date(new Date(now).getTime() + timeoutMinutes * 60 * 1000).toISOString()
}

function resolveWorkOrderShareJoinError(
  failureReason:
    | 'work_order_not_open'
    | 'sharing_disabled'
    | 'work_order_owner_conflict'
    | 'sharing_quantity_full'
    | 'worker_active_order_limit'
    | 'worker_deposit_insufficient'
    | null,
): Error {
  const messages: Record<string, string> = {
    work_order_not_open: '该订单当前不可拼单',
    sharing_disabled: '该订单未启用拼单',
    work_order_owner_conflict: '你已整单接取该订单，无需拼单',
    sharing_quantity_full: '拼单数量已满或超出剩余数量',
    worker_active_order_limit: '已达到当前等级最大同时接单量',
    worker_deposit_insufficient: '余额不足，无法冻结所需押金',
  }
  return createHttpError(messages[failureReason || ''] || '拼单失败', {
    statusCode: 409,
    errorCode: `work_order_sharing_${failureReason || 'failed'}`,
  })
}
