import crypto from 'node:crypto'

import { runtimeConfig } from '../../config/runtime.js'
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import { listOrderItemsByOrderId } from '../../repositories/order-item-repo.js'
import { findLatestOrderByAnyPlatformOrderId, getOrderById } from '../../repositories/order-repo.js'
import {
  acceptWorkOrderAndSettle,
  addWorkerWalletCredit,
  countWorkerAcceptedOrders,
  countWorkerActiveOrders,
  countWorkCategoryUsages,
  countWorkerLevelUsages,
  countWorkOrderPendingSharingSubmissions,
  cancelWorkerWorkOrder,
  countWorkerCancellationsSince,
  createWorkOrder,
  createWorkOrderEvent,
  createWorkerFinanceRequest,
  createWorkerUser,
  deleteWorkCategory,
  deleteWorkerLevel,
  getWorkerFinanceRequestById,
  getWorkerFinanceRequestSummary,
  getWorkCategoryById,
  getWorkCategoryByKey,
  getWorkerLevelById,
  getWorkerLevelByKey,
  getWorkerUserByInviteCode,
  getWorkerUserById,
  getWorkerUserByUsername,
  getWorkOrderById,
  getWorkOrderByOrderItemId,
  getWorkOrderShare,
  grabWorkOrder,
  incrementWorkerSessionVersion,
  joinWorkOrderShare,
  listAllWorkCategories,
  listPendingMaterialWorkOrdersByPlatformOrderId,
  listWorkerFinanceRequests,
  listWorkerSharesByWorker,
  listWorkerWalletLedgers,
  listWorkCategories,
  listWorkOrders,
  listWorkProductRules,
  listWorkerLevels,
  listWorkerUsers,
  listWorkOrderShares,
  listWorkOrderSharesByOrderIds,
  resolveProblemWorkOrder,
  reviewWorkerFinanceRequest,
  submitWorkOrderShareAcceptance,
  updateWorkOrder,
  updateWorkerPassword,
  updateWorkerUser,
  upsertWorkCategory,
  upsertWorkProductRule,
  upsertWorkerLevel,
  type WorkCategoryRow,
  type WorkerFinanceRequestRow,
  type WorkOrderRow,
  type WorkProductRuleRow,
  type WorkerLevelRow,
  type WorkOrderShareRow,
  type WorkerWalletLedgerRow,
  type WorkerUserRow,
  type GrabWorkOrderFailureReason,
  type ProblemWorkOrderResolutionAction,
} from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import type { OrderItemRow, OrderRow } from '../../types/repository/rows.js'
import { createHttpError } from '../../utils/http.js'
import { randomId } from '../../utils/random.js'
import { nowIso, addHours } from '../../utils/time.js'
import { normalizePage, normalizePageSize, safeParseJson } from '../admin/admin-query-utils.js'
import {
  normalizeStoredFileUrl,
  refreshStoredFileUrl,
  refreshUploadedFileUrls,
} from '../file-storage/file-storage-service.js'
import {
  getWorkerFinanceConfig,
  saveWorkerFinanceConfig,
} from './worker-finance-config-service.js'



export const DEFAULT_LEVEL_KEY = 'vip1'
export const DEFAULT_LEVEL_NAME = 'VIP1'
export const DEFAULT_CATEGORY_KEY = 'default'
export const DEFAULT_DEPOSIT_THRESHOLD_AMOUNT = 20_000
export const INVITE_CODE_LENGTH = 8
export const INVITE_CODE_RETRY_LIMIT = 5
export const WORKER_CANCEL_LIMIT_PER_WINDOW = 5
export const WORKER_CANCEL_LIMIT_WINDOW_MS = 30 * 24 * 60 * 60 * 1000
export function createWorkerSession(worker: WorkerUserRow) {
  const issuedAt = nowIso()
  const expiresAt = addHours(issuedAt, Number(runtimeConfig.admin?.sessionTtlHours || 12))
  const payload = {
    typ: 'worker',
    sid: crypto.randomBytes(12).toString('hex'),
    uid: Number(worker.id),
    usr: worker.username,
    status: worker.status,
    ver: normalizeSessionVersion(worker.session_version),
    iat: issuedAt,
    exp: expiresAt,
  }
  const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url')
  return {
    token: `${encodedPayload}.${signWorkerPayload(encodedPayload)}`,
    expiresAt,
    worker: mapWorkerUser(worker),
  }
}

export function ensureWorkerAuthConfigured() {
  if (String(runtimeConfig.admin?.sessionSecret || '').trim()) {
    return
  }

  throw createHttpError('接单登录态签名密钥未配置，请设置 ADMIN_SESSION_SECRET', {
    statusCode: 503,
    errorCode: 'worker_auth_not_configured',
  })
}

export function hashWorkerPassword(password: string): string {
  const salt = crypto.randomBytes(16).toString('hex')
  const derived = crypto.scryptSync(password, salt, 64).toString('hex')
  return `scrypt$${salt}$${derived}`
}

export function verifyWorkerPassword(password: string, storedHash: string): boolean {
  const [algorithm, salt, expectedHash] = String(storedHash || '').split('$')
  if (algorithm !== 'scrypt' || !salt || !expectedHash) return false
  return safeCompare(crypto.scryptSync(password, salt, 64).toString('hex'), expectedHash)
}

export function signWorkerPayload(encodedPayload: string): string {
  return crypto
    .createHmac('sha256', String(runtimeConfig.admin?.sessionSecret || ''))
    .update(`worker:${encodedPayload}`)
    .digest('base64url')
}

export function safeCompare(input: unknown, expected: unknown): boolean {
  const left = Buffer.from(String(input || ''), 'utf8')
  const right = Buffer.from(String(expected || ''), 'utf8')
  if (left.length !== right.length) return false
  return crypto.timingSafeEqual(left, right)
}

export function resolveWorkerPermissions(worker: WorkerUserRow) {
  const permission = safeParseJson(worker.level_permission_json)
  return {
    depositFreeAmount: normalizeInteger(permission.depositFreeAmount, 0),
    maxActiveOrders: normalizePositiveInteger(permission.maxActiveOrders, 1),
    upgradeThreshold: normalizeInteger(permission.upgradeThreshold, 0),
    visibleDelaySeconds: resolveVisibleDelaySeconds(
      worker.level_key || '',
      permission.visibleDelaySeconds,
    ),
  }
}

export function resolveVisibleDelaySeconds(levelKey: string, configured?: unknown): number {
  const configuredValue = normalizeInteger(configured, -1)
  if (configuredValue >= 0) return configuredValue
  const defaultDelays: Record<string, number> = {
    vip1: 20,
    vip2: 15,
    vip3: 10,
    vip4: 5,
    vip5: 0,
  }
  return defaultDelays[String(levelKey || '').toLowerCase()] ?? 0
}

export function resolveFreezeDepositAmount(
  workOrder: WorkOrderRow,
  permissions: { depositFreeAmount: number },
) {
  return Math.max(
    0,
    Number(workOrder.required_deposit_amount || 0) - Number(permissions.depositFreeAmount || 0),
  )
}

export function mapWorkerLevel(level: WorkerLevelRow | null | undefined) {
  if (!level) return null
  const permission = safeParseJson(level.permission_json)
  return {
    levelId: Number(level.id),
    levelKey: level.level_key,
    name: level.name,
    sortOrder: Number(level.sort_order || 100),
    status: level.status,
    depositFreeAmount: normalizeInteger(permission.depositFreeAmount, 0),
    maxActiveOrders: normalizePositiveInteger(permission.maxActiveOrders, 1),
    upgradeThreshold: normalizeInteger(permission.upgradeThreshold, 0),
    visibleDelaySeconds: resolveVisibleDelaySeconds(
      level.level_key,
      permission.visibleDelaySeconds,
    ),
    createdAt: level.created_at,
    updatedAt: level.updated_at,
  }
}

export function mapWorkCategory(category: WorkCategoryRow | null | undefined) {
  if (!category) return null
  return {
    categoryId: Number(category.id),
    categoryKey: category.category_key,
    name: category.name,
    sortOrder: Number(category.sort_order || 100),
    status: category.status,
    createdAt: category.created_at,
    updatedAt: category.updated_at,
  }
}

export function mapWorkProductRule(rule: WorkProductRuleRow | null | undefined) {
  if (!rule) return null
  const requirement = safeParseJson(rule.requirement_json)
  return {
    ruleId: Number(rule.id),
    ruleKey: rule.rule_key,
    provider: rule.provider || '',
    platform: rule.platform || '',
    shopId: rule.shop_id || '',
    skuCode: rule.sku_code || '',
    productName: rule.product_name || '',
    matchType: rule.match_type || 'contains',
    categoryId: rule.category_id ? Number(rule.category_id) : null,
    categoryName: rule.category_name || '',
    enabled: Boolean(rule.enabled),
    autoCreate: Boolean(rule.auto_create),
    rewardAmount: Number(rule.reward_amount || 0),
    requiredDepositAmount: Number(rule.required_deposit_amount || 0),
    depositThresholdAmount: Number(
      rule.deposit_threshold_amount || DEFAULT_DEPOSIT_THRESHOLD_AMOUNT,
    ),
    sharing: {
      enabled: rule.sharing_enabled === true,
      totalQuantity: Number(rule.sharing_total_quantity || 1),
      unitReward: Number(rule.sharing_unit_reward || 0),
    },
    timeoutMinutes: Number(rule.timeout_minutes || 0),
    timeoutPolicy: String(rule.timeout_policy || 'reopen').trim(),
    requirement: {
      fields: normalizeRequirementFields(requirement.fields),
    },
    sortOrder: Number(rule.sort_order || 100),
    createdAt: rule.created_at,
    updatedAt: rule.updated_at,
  }
}

export function mapWorkerUser(worker: WorkerUserRow) {
  return {
    workerId: Number(worker.id),
    username: worker.username,
    displayName: worker.display_name || worker.username,
    phone: worker.phone,
    status: worker.status,
    reviewNote: worker.review_note || '',
    inviteCode: worker.invite_code || '',
    inviter: worker.inviter_id
      ? {
          workerId: Number(worker.inviter_id),
          username: worker.inviter_username || '',
          displayName: worker.inviter_display_name || worker.inviter_username || '',
        }
      : null,
    level: worker.level_id
      ? {
          levelId: Number(worker.level_id),
          levelKey: worker.level_key || '',
          name: worker.level_name || '',
          permissions: resolveWorkerPermissions(worker),
        }
      : null,
    wallet: {
      availableAmount: Number(worker.available_amount || 0),
      frozenDepositAmount: Number(worker.frozen_deposit_amount || 0),
      pendingUnfreezeAmount: Number(worker.pending_unfreeze_amount || 0),
      totalCreditedAmount: Number(worker.total_credited_amount || 0),
      totalSettledAmount: Number(worker.total_settled_amount || 0),
    },
    createdAt: worker.created_at,
    updatedAt: worker.updated_at,
    reviewedAt: worker.reviewed_at,
  }
}

export function mapWalletLedger(ledger: WorkerWalletLedgerRow | null | undefined) {
  if (!ledger) return null
  return {
    ledgerId: Number(ledger.id),
    ledgerType: ledger.ledger_type,
    amount: Number(ledger.amount || 0),
    balanceAfter: Number(ledger.balance_after || 0),
    frozenAfter: Number(ledger.frozen_after || 0),
    relatedWorkOrderId: ledger.related_work_order_id
      ? Number(ledger.related_work_order_id)
      : null,
    auditStatus: ledger.audit_status || '',
    note: ledger.note || '',
    payload: safeParseJson(ledger.payload_json),
    createdAt: ledger.created_at,
  }
}

export function mapFinanceRequest(request: WorkerFinanceRequestRow | null | undefined) {
  if (!request) return null
  return {
    requestId: Number(request.id),
    requestType: request.request_type || '',
    status: request.status || '',
    amount: Number(request.amount || 0),
    accountChannel: request.account_channel || '',
    accountName: request.account_name || '',
    accountNo: request.account_no || '',
    note: request.note || '',
    reviewedNote: request.reviewed_note || '',
    payload: safeParseJson(request.payload_json),
    createdAt: request.created_at,
    updatedAt: request.updated_at,
    reviewedAt: request.reviewed_at,
    worker: request.worker_id
      ? {
          workerId: Number(request.worker_id),
          username: request.worker_username || '',
          displayName: request.worker_display_name || request.worker_username || '',
          phone: request.worker_phone || '',
        }
      : null,
  }
}

export function mapWorkOrderAdmin(workOrder: WorkOrderRow) {
  return {
    workOrderId: Number(workOrder.id),
    orderId: workOrder.order_id ? Number(workOrder.order_id) : null,
    orderItemId: workOrder.order_item_id ? Number(workOrder.order_item_id) : null,
    workOrderNo: workOrder.work_order_no,
    platformOrderId: workOrder.platform_order_id,
    productName: workOrder.product_name,
    categoryId: workOrder.category_id ? Number(workOrder.category_id) : null,
    categoryName: workOrder.category_name || '',
    status: workOrder.status,
    rewardAmount: Number(workOrder.reward_amount || 0),
    requiredDepositAmount: Number(workOrder.required_deposit_amount || 0),
    sharing: {
      enabled: workOrder.sharing_enabled === true,
      totalQuantity: Number(workOrder.sharing_total_quantity || 1),
      unitReward: Number(workOrder.sharing_unit_reward || 0),
    },
    timeoutMinutes: Number(workOrder.timeout_minutes || 0),
    timeoutPolicy: String(workOrder.timeout_policy || 'reopen').trim(),
    deadlineAt: workOrder.deadline_at,
    depositThresholdAmount: Number(
      workOrder.deposit_threshold_amount || DEFAULT_DEPOSIT_THRESHOLD_AMOUNT,
    ),
    material: safeParseJson(workOrder.material_json),
    requirement: safeParseJson(workOrder.requirement_json),
    acceptance: mapAcceptanceForResponse(workOrder.acceptance_json),
    problemNote: workOrder.problem_note || '',
    worker: workOrder.assigned_worker_id
      ? {
          workerId: Number(workOrder.assigned_worker_id),
          username: workOrder.worker_username || '',
          displayName: workOrder.worker_display_name || '',
        }
      : null,
    publishedAt: workOrder.published_at,
    assignedAt: workOrder.assigned_at,
    submittedAt: workOrder.submitted_at,
    acceptedAt: workOrder.accepted_at,
    createdAt: workOrder.created_at,
    updatedAt: workOrder.updated_at,
  }
}

export function mapWorkOrderForWorker(
  workOrder: WorkOrderRow,
  permissions: { depositFreeAmount: number },
  myShare?: WorkOrderShareRow | null,
) {
  const mapped = mapWorkOrderAdmin(workOrder)
  const {
    requiredDepositAmount,
    depositThresholdAmount,
    worker: _worker,
    orderId: _orderId,
    orderItemId: _orderItemId,
    ...visibleOrder
  } = mapped
  return {
    ...visibleOrder,
    freezeDepositAmount: resolveFreezeDepositAmount(workOrder, permissions),
    myShare: myShare ? mapWorkOrderShare(myShare) : null,
  }
}

export function mapWorkOrderShare(share: WorkOrderShareRow) {
  return {
    shareId: Number(share.id),
    workOrderId: Number(share.work_order_id),
    workerId: Number(share.worker_id),
    quantity: Number(share.quantity || 0),
    unitReward: Number(share.unit_reward || 0),
    shareReward: Number(share.share_reward || 0),
    shareDeposit: Number(share.share_deposit || 0),
    status: share.status,
    acceptance: safeParseJson(share.acceptance_json),
    submittedAt: share.submitted_at,
    acceptedAt: share.accepted_at,
    createdAt: share.created_at,
    updatedAt: share.updated_at,
    worker: share.worker_id
      ? {
          workerId: Number(share.worker_id),
          username: share.worker_username || '',
          displayName: share.worker_display_name || share.worker_username || '',
        }
      : null,
  }
}

export function mapWorkOrderPublic(workOrder: WorkOrderRow) {
  return {
    workOrderId: Number(workOrder.id),
    workOrderNo: workOrder.work_order_no,
    platformOrderId: workOrder.platform_order_id,
    productName: workOrder.product_name,
    status: workOrder.status,
  }
}

export function mapCollectLookupOrder(workOrder: WorkOrderRow) {
  return {
    ...mapWorkOrderPublic(workOrder),
    fields: resolveRequirementFields(workOrder),
    values: resolveCollectSubmittedFieldValues(workOrder),
  }
}

export function mapWallet(wallet: Awaited<ReturnType<typeof addWorkerWalletCredit>>) {
  return {
    workerId: Number(wallet?.worker_id || 0),
    availableAmount: Number(wallet?.available_amount || 0),
    frozenDepositAmount: Number(wallet?.frozen_deposit_amount || 0),
    pendingUnfreezeAmount: Number(wallet?.pending_unfreeze_amount || 0),
    totalCreditedAmount: Number(wallet?.total_credited_amount || 0),
    totalSettledAmount: Number(wallet?.total_settled_amount || 0),
  }
}

export function resolveRequirementFields(workOrder: WorkOrderRow) {
  return normalizeRequirementFields(safeParseJson(workOrder.requirement_json).fields)
}

export type RequirementField = {
  key: string
  label: string
  required: boolean
  type: 'text' | 'select'
  options: string[]
  mockValue: string
}

export function normalizeRequirementFields(value: unknown) {
  const rawFields = Array.isArray(value) ? value : []
  const fields = rawFields
    .map((item) => {
      const source =
        item && typeof item === 'object' && !Array.isArray(item) ? (item as JsonObject) : {}
      const key = String(source.key || '').trim()
      if (!key) return null
      const type = source.type === 'select' ? 'select' : 'text'
      const options = Array.isArray(source.options)
        ? source.options.map((option) => String(option || '').trim()).filter(Boolean)
        : []
      return {
        key,
        label: String(source.label || key).trim(),
        required: source.required !== false,
        type,
        options,
        mockValue: String(source.mockValue || '').trim(),
      }
    })
    .filter((item): item is RequirementField => Boolean(item))

  if (fields.length > 0 && !isDefaultTemplateRequirementFields(fields)) {
    return fields
  }
  return [
    { key: 'gameId', label: '游戏编号', required: true, type: 'text', options: [], mockValue: 'test_uid' },
    { key: 'gameNickname', label: '游戏昵称', required: true, type: 'text', options: [], mockValue: '测试角色' },
    { key: 'system', label: '系统', required: true, type: 'select', options: ['安卓', '苹果'], mockValue: '安卓' },
    { key: 'serverZone', label: '区服', required: true, type: 'select', options: ['QQ区', '微信区'], mockValue: 'QQ区' },
  ]
}

function isDefaultTemplateRequirementFields(fields: RequirementField[]): boolean {
  if (fields.length === 0) return true
  const defaultKeys = new Set(['gameId', 'gameNickname', 'system', 'serverZone'])
  const legacyKeys = new Set(['gameAccount', 'serverName', 'roleName'])
  return fields.every((field) => defaultKeys.has(field.key) || legacyKeys.has(field.key))
}

export function normalizeRequirementFieldsFromPayload(payload: JsonObject) {
  const directFields = Array.isArray(payload.fields) ? payload.fields : null
  const requirement =
    payload.requirement &&
    typeof payload.requirement === 'object' &&
    !Array.isArray(payload.requirement)
      ? (payload.requirement as JsonObject)
      : {}
  if (directFields) {
    return normalizeRequirementFields(directFields)
  }
  if (Array.isArray(requirement.fields)) {
    return normalizeRequirementFields(requirement.fields)
  }

  const textFields = normalizeRequirementFieldsText(
    payload.fieldsText || payload.requiredFieldsText,
  )
  return textFields.length > 0 ? textFields : normalizeRequirementFields([])
}

export function normalizeRequirementFieldsText(value: unknown) {
  return String(value || '')
    .split(/\r?\n/)
    .map((line, index) => {
      const text = line.trim()
      if (!text) return null
      const delimiter = text.includes('：') ? '：' : ':'
      if (text.includes(delimiter)) {
        const [rawKey, ...labelParts] = text.split(delimiter)
        const key = String(rawKey || '').trim()
        if (!key) return null
        const labelWithOptions = labelParts.join(delimiter).trim() || key
        const [label, rawOptions] = splitFieldOptions(labelWithOptions)
        return {
          key,
          label: label || key,
          required: true,
          type: rawOptions.length > 0 ? 'select' : 'text',
          options: rawOptions,
          mockValue: '',
        }
      }
      const [plainLabel, plainOptions] = splitFieldOptions(text)
      return {
        key: `field${index + 1}`,
        label: plainLabel,
        required: true,
        type: plainOptions.length > 0 ? 'select' : 'text',
        options: plainOptions,
        mockValue: '',
      }
    })
    .filter((item): item is RequirementField => Boolean(item))
}

function splitFieldOptions(value: string): [string, string[]] {
  const text = String(value || '').trim()
  if (!text.includes('#')) {
    return [text, []]
  }
  const [label, ...optionParts] = text.split('#')
  const options = optionParts
    .join('#')
    .split(/[,，、]/)
    .map((option) => String(option || '').trim())
    .filter(Boolean)
  return [String(label || '').trim(), options]
}

export function resolveMatchingProductRule(
  order: OrderRow,
  item: OrderItemRow,
  rules: WorkProductRuleRow[],
) {
  return (
    rules.find((rule) => {
      if (!matchesOptionalText(rule.provider, order.provider)) return false
      if (!matchesOptionalText(rule.platform, order.platform)) return false
      if (!matchesOptionalText(rule.shop_id, order.shop_id)) return false

      const skuCode = String(item.sku_code || '')
        .trim()
        .toLowerCase()
      const skuName = String(item.sku_name || '')
        .trim()
        .toLowerCase()
      const ruleSkuCode = String(rule.sku_code || '')
        .trim()
        .toLowerCase()
      const ruleProductName = String(rule.product_name || '')
        .trim()
        .toLowerCase()

      if (ruleSkuCode && ruleSkuCode !== skuCode) {
        return false
      }
      if (!ruleProductName) {
        return Boolean(ruleSkuCode)
      }
      if (rule.match_type === 'exact') {
        return ruleProductName === skuName || ruleProductName === skuCode
      }
      return skuName.includes(ruleProductName) || skuCode.includes(ruleProductName)
    }) || null
  )
}

export function matchesOptionalText(ruleValue: unknown, sourceValue: unknown) {
  const ruleText = String(ruleValue || '')
    .trim()
    .toLowerCase()
  if (!ruleText) {
    return true
  }
  return (
    ruleText ===
    String(sourceValue || '')
      .trim()
      .toLowerCase()
  )
}

export function normalizeSubmittedFields(value: unknown) {
  const source =
    value && typeof value === 'object' && !Array.isArray(value) ? (value as JsonObject) : {}
  return Object.fromEntries(
    Object.entries(source)
      .filter(([key]) => !['orderNo', 'platformOrderId', 'fields', 'material'].includes(key))
      .map(([key, currentValue]) => [key, String(currentValue || '').trim()]),
  )
}

export function normalizeStringArray(value: unknown): string[] {
  if (!Array.isArray(value)) return []
  return value.map((item) => String(item || '').trim()).filter(Boolean)
}

export function normalizeUploadedFiles(value: unknown) {
  if (!Array.isArray(value)) return []
  return value
    .map((item) => {
      const source =
        item && typeof item === 'object' && !Array.isArray(item) ? (item as JsonObject) : {}
      const objectKey = String(source.objectKey || source.object_key || '').trim()
      const url = String(source.url || '').trim()
      if (!objectKey && !url) return null
      return {
        objectKey,
        url: normalizeStoredFileUrl(url, objectKey),
        thumbnailUrl: normalizeStoredFileUrl(
          source.thumbnailUrl || source.thumbnail_url || url,
          objectKey,
        ),
        mediumUrl: normalizeStoredFileUrl(source.mediumUrl || source.medium_url || url, objectKey),
        filename: String(source.filename || '').trim(),
        contentType: String(source.contentType || source.content_type || '').trim(),
        size: normalizeInteger(source.size, 0),
      }
    })
    .filter(
      (
        item,
      ): item is {
        objectKey: string
        url: string
        thumbnailUrl: string
        mediumUrl: string
        filename: string
        contentType: string
        size: number
      } => Boolean(item),
    )
}

export function mapAcceptanceForResponse(value: unknown) {
  const acceptance = safeParseJson(value)
  const files = normalizeUploadedFiles(acceptance.files).map((file) =>
    refreshUploadedFileUrls(file),
  )
  const imageUrls = normalizeStringArray(acceptance.imageUrls).map((url) =>
    refreshStoredFileUrl(url),
  )
  return {
    ...acceptance,
    files,
    imageUrls,
  }
}

export function resolveCollectSubmittedFieldValues(workOrder: WorkOrderRow) {
  const material = safeParseJson(workOrder.material_json)
  const collect =
    material.collect && typeof material.collect === 'object' && !Array.isArray(material.collect)
      ? safeParseJson(material.collect)
      : {}
  return normalizeSubmittedFields(collect.fields)
}

export function normalizeReviewStatus(value: unknown) {
  const status = String(value || '').trim()
  if (['active', 'rejected', 'disabled', 'pending_review'].includes(status)) return status
  return 'pending_review'
}

export function normalizeProblemResolutionAction(value: unknown): ProblemWorkOrderResolutionAction {
  const action = String(value || '').trim()
  if (['return_to_worker', 'reopen', 'cancel_release', 'cancel_deduct'].includes(action)) {
    return action as ProblemWorkOrderResolutionAction
  }
  throw createHttpError('问题单处置方式不正确', {
    statusCode: 400,
    errorCode: 'work_order_problem_resolution_action_invalid',
  })
}

export function throwGrabWorkOrderFailure(failureReason: GrabWorkOrderFailureReason | null): never {
  if (failureReason === 'worker_active_order_limit') {
    throw createHttpError('已达到当前等级最大同时接单量', {
      statusCode: 409,
      errorCode: 'worker_active_order_limit',
    })
  }
  if (failureReason === 'worker_deposit_insufficient') {
    throw createHttpError('余额不足，无法冻结所需押金', {
      statusCode: 409,
      errorCode: 'worker_deposit_insufficient',
    })
  }
  throw createHttpError('抢单失败，订单可能已被抢走', {
    statusCode: 409,
    errorCode: 'work_order_grab_conflict',
  })
}

export function normalizeEnabledStatus(value: unknown) {
  return String(value || 'active').trim() === 'disabled' ? 'disabled' : 'active'
}

export function normalizeWalletLedgerType(value: unknown) {
  const ledgerType = String(value || '').trim()
  if (!ledgerType) return ''
  if (
    [
      'manual_credit',
      'withdraw_paid',
      'deposit_freeze',
      'deposit_release',
      'deposit_pending_unfreeze',
      'deposit_unfreeze',
      'deposit_deduction',
      'reward_settlement',
    ].includes(ledgerType)
  ) {
    return ledgerType
  }
  throw createHttpError('钱包流水类型不正确', {
    statusCode: 400,
    errorCode: 'worker_wallet_ledger_type_invalid',
  })
}

export function normalizeFinanceRequestType(value: unknown) {
  const requestType = String(value || '').trim()
  if (!requestType) return ''
  if (['recharge', 'withdraw'].includes(requestType)) {
    return requestType
  }
  throw createHttpError('资金申请类型不正确', {
    statusCode: 400,
    errorCode: 'worker_finance_request_type_invalid',
  })
}

export function normalizeFinanceRequestStatus(value: unknown) {
  const status = String(value || '').trim()
  if (!status) return ''
  if (['pending', 'approved', 'rejected', 'cancelled'].includes(status)) {
    return status
  }
  throw createHttpError('资金申请状态不正确', {
    statusCode: 400,
    errorCode: 'worker_finance_request_status_invalid',
  })
}

export function normalizeAdminFinanceReviewStatus(value: unknown) {
  const status = String(value || '').trim()
  if (['approved', 'rejected', 'cancelled'].includes(status)) {
    return status
  }
  throw createHttpError('审核状态不正确', {
    statusCode: 400,
    errorCode: 'worker_finance_review_status_invalid',
  })
}

export function normalizeWithdrawChannel(value: unknown) {
  const channel = String(value || '').trim()
  if (['alipay', 'wechat', 'bank'].includes(channel)) {
    return channel
  }
  throw createHttpError('提现方式不正确', {
    statusCode: 400,
    errorCode: 'worker_withdraw_channel_invalid',
  })
}

export function normalizeMatchType(value: unknown) {
  const matchType = String(value || '').trim()
  if (matchType === 'exact') return 'exact'
  return 'contains'
}

export function normalizeBoolean(value: unknown, fallback: boolean) {
  if (typeof value === 'boolean') return value
  const text = String(value ?? '')
    .trim()
    .toLowerCase()
  if (!text) return fallback
  if (['true', '1', 'yes', 'enabled', 'active', 'on'].includes(text)) return true
  if (['false', '0', 'no', 'disabled', 'off'].includes(text)) return false
  return fallback
}

export function normalizeSlugKey(value: unknown, fallback: string) {
  const key = String(value || '')
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9._-]+/g, '-')
    .replace(/^-+|-+$/g, '')
  return key || fallback
}

export function normalizeUsername(value: unknown): string {
  return String(value || '')
    .trim()
    .toLowerCase()
}

export function normalizePassword(value: unknown): string {
  return String(value || '').trim()
}

export function validateWorkerUsername(username: string) {
  if (!/^[a-zA-Z0-9._-]{3,32}$/.test(username)) {
    throw createHttpError('接单账号格式无效，需为 3-32 位字母数字或 ._-', {
      statusCode: 400,
      errorCode: 'worker_username_invalid',
    })
  }
}

export function validateWorkerPassword(password: string) {
  if (password.length < 6) {
    throw createHttpError('接单密码至少 6 位', {
      statusCode: 400,
      errorCode: 'worker_password_invalid',
    })
  }
}

export function normalizeSessionVersion(value: unknown): number {
  const parsed = Number(value)
  return Number.isInteger(parsed) && parsed > 0 ? parsed : 1
}

export function normalizeOptionalId(value: unknown): number | null {
  const parsed = Number(value)
  return Number.isInteger(parsed) && parsed > 0 ? parsed : null
}

export function normalizeInteger(value: unknown, fallback: number): number {
  const parsed = Number(value)
  return Number.isInteger(parsed) ? parsed : fallback
}

export function normalizePositiveInteger(value: unknown, fallback: number): number {
  const parsed = Number(value)
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
}

export function normalizeAmountFen(value: unknown, fallback: number): number {
  const text = String(value ?? '').trim()
  if (!text) return fallback
  const parsed = Number(text)
  if (!Number.isFinite(parsed) || parsed < 0) return fallback
  return Math.round(parsed * 100)
}

export function normalizeProofFiles(value: unknown): Array<Record<string, unknown>> {
  if (!Array.isArray(value)) return []
  const files: Array<Record<string, unknown>> = []
  for (const item of value) {
    if (!item || typeof item !== 'object') continue
    const file = item as Record<string, unknown>
    const url = String(file.url || '').trim()
    if (!url) continue
    files.push({
      url,
      thumbnailUrl: String(file.thumbnailUrl || file.thumbnail_url || '').trim(),
      mediumUrl: String(file.mediumUrl || file.medium_url || '').trim(),
      objectKey: String(file.objectKey || file.object_key || '').trim(),
      originalFilename: String(
        file.originalFilename || file.original_filename || file.name || '',
      ).trim(),
      contentType: String(file.contentType || file.content_type || '').trim(),
      sizeBytes: Number(file.sizeBytes || file.size_bytes || 0),
    })
    if (files.length >= 10) break
  }
  return files
}

export function isIgnorableWorkerAuthError(error: unknown) {
  return IGNORABLE_WORKER_AUTH_ERROR_CODES.has(
    String((error as { errorCode?: string })?.errorCode || '').trim(),
  )
}

export const IGNORABLE_WORKER_AUTH_ERROR_CODES = new Set([
  'worker_auth_required',
  'worker_auth_invalid',
  'worker_auth_expired',
  'worker_auth_user_invalid',
  'worker_auth_stale',
])
