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,
  countWorkerTimeoutEvents,
  countWorkerWithdrawRequestsOnDay,
  countWorkCategoryUsages,
  countWorkerLevelUsages,
  countWorkOrderPendingSharingSubmissions,
  countWorkerCancellationsSince,
  countWorkerSmsCodesOnDay,
  cancelWorkerWorkOrder,
  consumeWorkerSmsCode,
  createWorkerSmsCode,
  createWorkOrder,
  createWorkOrderEvent,
  createWorkerFinanceRequest,
  createWorkerUser,
  deleteWorkCategory,
  deleteWorkerLevel,
  getWorkerFinanceRequestById,
  getWorkerFinanceRequestSummary,
  getWorkCategoryById,
  getWorkCategoryByKey,
  getWorkerLevelById,
  getWorkerLevelByKey,
  getLatestWorkerSmsCode,
  getWorkerUserByInviteCode,
  getWorkerUserById,
  getWorkerUserByPhone,
  getWorkerUserByUsername,
  getWorkOrderById,
  getWorkOrderByOrderItemId,
  getWorkOrderShare,
  grabWorkOrder,
  incrementWorkerSessionVersion,
  joinWorkOrderShare,
  listAllWorkCategories,
  listPendingMaterialWorkOrdersByPlatformOrderId,
  listWorkerFinanceRequests,
  listWorkerSharesByWorker,
  listWorkerWalletLedgers,
  listWorkCategories,
  listWorkOrders,
  listWorkProductRules,
  listWorkerLevels,
  listWorkerUsers,
  listWorkOrderShares,
  listWorkOrderSharesByOrderIds,
  listDueDepositUnfreezes,
  listOverdueWorkOrders,
  releaseDepositUnfreeze,
  resolveProblemWorkOrder,
  reviewWorkerFinanceRequest,
  settleOverdueWorkOrder,
  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 { getSmsProvider } from '../sms/index.js'
import {
  normalizeStoredFileUrl,
  refreshStoredFileUrl,
  refreshUploadedFileUrls,
} from '../file-storage/file-storage-service.js'
import {
  getWorkerFinanceConfig,
  saveWorkerFinanceConfig,
} from './worker-finance-config-service.js'

import { DEFAULT_CATEGORY_KEY, DEFAULT_LEVEL_KEY, DEFAULT_LEVEL_NAME, INVITE_CODE_LENGTH, INVITE_CODE_RETRY_LIMIT, WORKER_CANCEL_LIMIT_PER_WINDOW, WORKER_CANCEL_LIMIT_WINDOW_MS, createWorkerSession, ensureWorkerAuthConfigured, hashWorkerPassword, isIgnorableWorkerAuthError, mapCollectLookupOrder, mapFinanceRequest, mapWalletLedger, mapWorkCategory, mapWorkOrderForWorker, mapWorkOrderPublic, mapWorkOrderShare, mapWorkerUser, normalizeAmountFen, normalizeFinanceRequestStatus, normalizeFinanceRequestType, normalizeInteger, normalizeOptionalId, normalizePassword, normalizePositiveInteger, normalizeProofFiles, normalizeSessionVersion, normalizeStringArray, normalizeSubmittedFields, normalizeUploadedFiles, normalizeUsername, normalizeWalletLedgerType, normalizeWithdrawChannel, resolveFreezeDepositAmount, resolveRequirementFields, resolveWorkerPermissions, safeCompare, signWorkerPayload, throwGrabWorkOrderFailure, validateWorkerPassword, validateWorkerUsername, verifyWorkerPassword } from './mappers.js'

export type WorkerSession = {
  sessionId: string
  workerId: number
  username: string
  status: string
  expiresAt: string
  sessionVersion: number
}


export async function ensureWorkerPlatformDefaults() {
  const now = nowIso()
  const existingLevel = await getWorkerLevelByKey(DEFAULT_LEVEL_KEY)
  const level =
    existingLevel ||
    (await upsertWorkerLevel({
      levelKey: DEFAULT_LEVEL_KEY,
      name: DEFAULT_LEVEL_NAME,
      sortOrder: 10,
      status: 'active',
      permissionJson: JSON.stringify({
        depositFreeAmount: 0,
        maxActiveOrders: 1,
        upgradeThreshold: 0,
      }),
      now,
    }))
  const existingCategory = await getWorkCategoryByKey(DEFAULT_CATEGORY_KEY)
  const category =
    existingCategory ||
    (await upsertWorkCategory({
      categoryKey: DEFAULT_CATEGORY_KEY,
      name: '默认分类',
      sortOrder: 100,
      status: 'active',
      now,
    }))
  return { level, category }
}

export async function sendWorkerSmsCode(payload: JsonObject = {}) {
  const phone = normalizeWorkerPhone(payload.phone)
  const config = runtimeConfig.worker.sms
  const cooldownMs = Math.max(10, Number(config.cooldownSeconds || 60)) * 1000
  const dailyLimit = Math.max(1, Number(config.dailyLimit || 10))
  const ttlSeconds = Math.max(60, Number(config.codeTtlSeconds || 300))

  if (await getWorkerUserByPhone(phone)) {
    throw createHttpError('该手机号已注册，请直接登录', {
      statusCode: 409,
      errorCode: 'worker_phone_registered',
    })
  }

  const latest = await getLatestWorkerSmsCode(phone, 'register')
  if (latest && new Date(latest.created_at).getTime() > Date.now() - cooldownMs) {
    throw createHttpError('验证码发送过于频繁，请稍后再试', {
      statusCode: 429,
      errorCode: 'worker_sms_cooldown',
    })
  }

  const dayRange = resolveChinaDayRange()
  const todayCount = await countWorkerSmsCodesOnDay(phone, dayRange.start, dayRange.end)
  if (todayCount >= dailyLimit) {
    throw createHttpError('今日验证码发送次数已达上限，请明天再试', {
      statusCode: 429,
      errorCode: 'worker_sms_daily_limit',
    })
  }

  const code = generateSmsCode()
  await getSmsProvider().sendCode(phone, code)
  await createWorkerSmsCode({
    phone,
    purpose: 'register',
    codeHash: hashSmsCode(code),
    expiresAt: new Date(Date.now() + ttlSeconds * 1000).toISOString(),
    now: nowIso(),
  })
  return {
    sent: true,
    expiresInSeconds: ttlSeconds,
    provider: String(runtimeConfig.worker.sms.provider || 'mock'),
  }
}

export async function registerWorker(payload: JsonObject = {}) {
  ensureWorkerAuthConfigured()
  const phone = normalizeWorkerPhone(payload.phone)
  const code = String(payload.code || payload.smsCode || '').trim()
  const password = normalizePassword(payload.password)
  const displayName = String(payload.displayName || payload.nickname || '').trim()
  const inviteCode = String(payload.inviteCode || '').trim().toUpperCase()

  validateWorkerPassword(password)
  if (!displayName) {
    throw createHttpError('请输入昵称', {
      statusCode: 400,
      errorCode: 'worker_display_name_required',
    })
  }
  if (!code) {
    throw createHttpError('请输入短信验证码', {
      statusCode: 400,
      errorCode: 'worker_sms_code_required',
    })
  }

  const latest = await getLatestWorkerSmsCode(phone, 'register')
  const codeValid = Boolean(
    latest &&
      !latest.consumed_at &&
      new Date(latest.expires_at).getTime() > Date.now() &&
      latest.code_hash === hashSmsCode(code),
  )
  if (!codeValid) {
    throw createHttpError('短信验证码错误或已过期', {
      statusCode: 400,
      errorCode: 'worker_sms_code_invalid',
    })
  }
  await consumeWorkerSmsCode(latest!.id, nowIso())

  if (await getWorkerUserByUsername(phone)) {
    throw createHttpError('该手机号已注册，请直接登录', {
      statusCode: 409,
      errorCode: 'worker_user_exists',
    })
  }
  if (await getWorkerUserByPhone(phone)) {
    throw createHttpError('该手机号已注册，请直接登录', {
      statusCode: 409,
      errorCode: 'worker_phone_exists',
    })
  }

  const inviter = inviteCode ? await getWorkerUserByInviteCode(inviteCode) : null
  if (inviteCode && !inviter) {
    throw createHttpError('邀请码无效', {
      statusCode: 400,
      errorCode: 'worker_invite_code_invalid',
    })
  }
  if (inviter && inviter.status !== 'active') {
    throw createHttpError('邀请人账号不可用', {
      statusCode: 400,
      errorCode: 'worker_inviter_invalid',
    })
  }

  const defaults = await ensureWorkerPlatformDefaults()
  const now = nowIso()
  const created = await createWorkerUser({
    username: phone,
    passwordHash: hashWorkerPassword(password),
    displayName,
    phone,
    levelId: defaults.level?.id || null,
    status: 'active',
    inviterId: inviter ? Number(inviter.id) : null,
    inviteCode: await generateInviteCode(now),
    now,
  })

  if (!created) {
    throw createHttpError('接单账号注册失败', {
      statusCode: 500,
      errorCode: 'worker_register_failed',
    })
  }

  return {
    worker: mapWorkerUser(created),
    reviewRequired: false,
  }
}

function normalizeWorkerPhone(value: unknown): string {
  const phone = String(value || '').trim()
  if (!/^1[3-9]\d{9}$/.test(phone)) {
    throw createHttpError('手机号格式不正确', {
      statusCode: 400,
      errorCode: 'worker_phone_invalid',
    })
  }
  return phone
}

function generateSmsCode(): string {
  let code = ''
  for (let index = 0; index < 6; index += 1) {
    code += String(crypto.randomInt(10))
  }
  return code
}

function hashSmsCode(code: string): string {
  return crypto.createHash('sha256').update(String(code || '')).digest('hex')
}

async function generateInviteCode(now: string): Promise<string> {
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
  for (let attempt = 0; attempt < INVITE_CODE_RETRY_LIMIT; attempt++) {
    const candidate = Array.from(
      { length: INVITE_CODE_LENGTH },
      () => chars[Math.floor(crypto.randomInt(chars.length))],
    ).join('')
    if (!(await getWorkerUserByInviteCode(candidate))) {
      return candidate
    }
  }
  throw createHttpError('邀请码生成失败，请重试', {
    statusCode: 500,
    errorCode: 'worker_invite_code_generation_failed',
  })
}

export async function loginWorker(username: unknown, password: unknown) {
  ensureWorkerAuthConfigured()
  const normalizedUsername = normalizeUsername(username)
  const normalizedPassword = normalizePassword(password)

  if (!normalizedUsername || !normalizedPassword) {
    throw createHttpError('缺少接单账号或密码', {
      statusCode: 400,
      errorCode: 'worker_credentials_required',
    })
  }

  const worker = await getWorkerUserByUsername(normalizedUsername)
  if (!worker || !verifyWorkerPassword(normalizedPassword, worker.password_hash)) {
    throw createHttpError('账号或密码错误', {
      statusCode: 401,
      errorCode: 'worker_login_failed',
    })
  }

  if (worker.status === 'disabled') {
    throw createHttpError('接单账号已停用', {
      statusCode: 403,
      errorCode: 'worker_disabled',
    })
  }

  return createWorkerSession(worker)
}

export async function logoutWorkerSession(token: unknown) {
  const normalizedToken = String(token || '').trim()
  if (!normalizedToken) {
    return { success: true, revoked: false }
  }

  try {
    const session = await verifyWorkerSessionToken(normalizedToken)
    const revokedWorker = await incrementWorkerSessionVersion(session.workerId, nowIso())
    return {
      success: true,
      revoked: Boolean(revokedWorker),
    }
  } catch (error) {
    if (isIgnorableWorkerAuthError(error)) {
      return { success: true, revoked: false }
    }
    throw error
  }
}

export async function verifyWorkerSessionToken(token: unknown): Promise<WorkerSession> {
  ensureWorkerAuthConfigured()
  const normalizedToken = String(token || '').trim()
  if (!normalizedToken) {
    throw createHttpError('未登录或登录已失效', {
      statusCode: 401,
      errorCode: 'worker_auth_required',
    })
  }

  const [encodedPayload, signature] = normalizedToken.split('.')
  if (!encodedPayload || !signature || !safeCompare(signature, signWorkerPayload(encodedPayload))) {
    throw createHttpError('接单登录态无效', {
      statusCode: 401,
      errorCode: 'worker_auth_invalid',
    })
  }

  let payload: JsonObject = {}
  try {
    payload = JSON.parse(Buffer.from(encodedPayload, 'base64url').toString('utf8'))
  } catch {
    throw createHttpError('接单登录态无效', {
      statusCode: 401,
      errorCode: 'worker_auth_invalid',
    })
  }

  if (String(payload.typ || '') !== 'worker') {
    throw createHttpError('接单登录态无效', {
      statusCode: 401,
      errorCode: 'worker_auth_invalid',
    })
  }

  const expiresAt = String(payload.exp || '').trim()
  if (!expiresAt || Date.parse(expiresAt) <= Date.now()) {
    throw createHttpError('登录已过期，请重新登录', {
      statusCode: 401,
      errorCode: 'worker_auth_expired',
    })
  }

  const workerId = Number(payload.uid || 0)
  const worker = await getWorkerUserById(workerId)
  if (!worker || worker.status === 'disabled') {
    throw createHttpError('接单账号已不可用，请重新登录', {
      statusCode: 401,
      errorCode: 'worker_auth_user_invalid',
    })
  }

  const currentSessionVersion = normalizeSessionVersion(worker.session_version)
  if (Number(payload.ver || 0) !== currentSessionVersion) {
    throw createHttpError('登录态已失效，请重新登录', {
      statusCode: 401,
      errorCode: 'worker_auth_stale',
    })
  }

  return {
    sessionId: String(payload.sid || '').trim(),
    workerId: Number(worker.id),
    username: worker.username,
    status: worker.status,
    expiresAt,
    sessionVersion: currentSessionVersion,
  }
}

export async function getWorkerSessionSummary(token: unknown) {
  const session = await verifyWorkerSessionToken(token)
  const worker = await getRequiredWorker(session.workerId)
  return {
    authenticated: true,
    expiresAt: session.expiresAt,
    worker: mapWorkerUser(worker),
  }
}

export function requireActiveWorkerSession(session: WorkerSession | null | undefined) {
  if (session?.status === 'active') {
    return
  }

  throw createHttpError('接单账号尚未通过审核', {
    statusCode: 403,
    errorCode: 'worker_not_active',
  })
}

export async function getWorkerProfile(session: WorkerSession) {
  const worker = await getRequiredWorker(session.workerId)
  const [financeSummary, acceptedOrderCount, timeoutOrderCount, financeConfig] = await Promise.all([
    getWorkerFinanceRequestSummary(session.workerId),
    countWorkerAcceptedOrders(session.workerId),
    countWorkerTimeoutEvents(session.workerId),
    Promise.resolve(getWorkerFinanceConfig()),
  ])
  const permissions = resolveWorkerPermissions(worker)
  const levelProgress = await resolveLevelProgress(worker, acceptedOrderCount)
  return {
    worker: mapWorkerUser(worker),
    permissions,
    summary: {
      acceptedOrderCount,
      timeoutOrderCount,
      pendingWithdrawAmount: financeSummary.pendingWithdrawAmount,
      approvedWithdrawAmount: financeSummary.approvedWithdrawAmount,
      pendingRechargeAmount: financeSummary.pendingRechargeAmount,
    },
    levelProgress,
    financeConfig,
  }
}

async function resolveLevelProgress(
  worker: WorkerUserRow,
  acceptedOrderCount: number,
): Promise<{
  currentThreshold: number
  nextThreshold: number | null
  progressPercent: number
} | null> {
  if (!worker.level_id) return null
  const levels = await listWorkerLevels()
  const active = levels.filter((level) => level.status === 'active')
  const current = active.find((level) => Number(level.id) === Number(worker.level_id))
  if (!current) return null
  const currentThreshold = normalizeInteger(safeParseJson(current.permission_json).upgradeThreshold, 0)
  const next = active
    .filter((level) => normalizeInteger(safeParseJson(level.permission_json).upgradeThreshold, 0) > currentThreshold)
    .sort(
      (a, b) =>
        normalizeInteger(safeParseJson(a.permission_json).upgradeThreshold, 0) -
        normalizeInteger(safeParseJson(b.permission_json).upgradeThreshold, 0),
    )[0]
  const nextThreshold = next
    ? normalizeInteger(safeParseJson(next.permission_json).upgradeThreshold, 0)
    : null
  return {
    currentThreshold,
    nextThreshold,
    progressPercent:
      nextThreshold && nextThreshold > currentThreshold
        ? Math.min(
            100,
            Math.round(
              ((acceptedOrderCount - currentThreshold) / (nextThreshold - currentThreshold)) * 100,
            ),
          )
        : 100,
  }
}

export async function listWorkerProfileWalletLedgers(
  query: JsonObject = {},
  session: WorkerSession,
) {
  const page = normalizePage(query.page)
  const pageSize = normalizePageSize(query.pageSize)
  const ledgerType = normalizeWalletLedgerType(query.ledgerType)
  const { items, total } = await listWorkerWalletLedgers({
    page,
    pageSize,
    workerId: session.workerId,
    ledgerType,
  })
  return {
    items: items.map(mapWalletLedger),
    pagination: { page, pageSize, total },
  }
}

export async function listWorkerProfileFinanceRequests(
  query: JsonObject = {},
  session: WorkerSession,
) {
  const page = normalizePage(query.page)
  const pageSize = normalizePageSize(query.pageSize)
  const status = normalizeFinanceRequestStatus(query.status)
  const requestType = normalizeFinanceRequestType(query.requestType)
  const { items, total } = await listWorkerFinanceRequests({
    page,
    pageSize,
    workerId: session.workerId,
    status,
    requestType,
  })
  return {
    items: items.map(mapFinanceRequest),
    pagination: { page, pageSize, total },
  }
}

export async function createWorkerRechargeRequest(
  payload: JsonObject = {},
  session: WorkerSession,
) {
  requireActiveWorkerSession(session)
  const worker = await getRequiredWorker(session.workerId)
  const financeConfig = getWorkerFinanceConfig()
  if (financeConfig.recharge.enabled === false) {
    throw createHttpError('当前充值入口暂未开放，请联系管理员', {
      statusCode: 409,
      errorCode: 'worker_recharge_disabled',
    })
  }
  const amount = normalizeAmountFen(payload.amount ?? payload.amountYuan, 0)
  if (amount <= 0) {
    throw createHttpError('充值金额必须大于 0', {
      statusCode: 400,
      errorCode: 'worker_recharge_amount_invalid',
    })
  }

  const note = String(payload.note || payload.remark || '').trim()
  const proofFiles = normalizeProofFiles(payload.proofFiles || payload.proofFilesList)
  const paidAt = String(payload.paidAt || '').trim()
  const created = await createWorkerFinanceRequest({
    workerId: worker.id,
    requestType: 'recharge',
    amount,
    accountChannel: 'manual',
    accountName: worker.display_name || worker.username,
    accountNo: worker.phone || '',
    note: note || '个人中心充值申请',
    payloadJson: JSON.stringify({
      source: 'worker_profile_recharge',
      username: worker.username,
      phone: worker.phone || '',
      paidAt: paidAt || null,
      proofFiles,
      financeConfig: {
        channelName: financeConfig.recharge.channelName,
        accountName: financeConfig.recharge.accountName,
        accountNo: financeConfig.recharge.accountNo,
        qrCodeImage: financeConfig.recharge.qrCodeImage,
      },
    }),
    now: nowIso(),
  })
  if (!created) {
    throw createHttpError('充值申请创建失败', {
      statusCode: 500,
      errorCode: 'worker_recharge_request_create_failed',
    })
  }
  return {
    request: mapFinanceRequest(created),
  }
}

export async function createWorkerWithdrawRequest(
  payload: JsonObject = {},
  session: WorkerSession,
) {
  requireActiveWorkerSession(session)
  const worker = await getRequiredWorker(session.workerId)
  const financeConfig = getWorkerFinanceConfig()
  if (financeConfig.withdraw.enabled === false) {
    throw createHttpError('当前提现入口暂未开放，请联系管理员', {
      statusCode: 409,
      errorCode: 'worker_withdraw_disabled',
    })
  }

  await settleDueDepositUnfreezes({ workerId: worker.id, limit: 50 })
  const withdrawCountToday = await countWorkerWithdrawRequestsOnDay(
    worker.id,
    resolveChinaDayRange().start,
    resolveChinaDayRange().end,
  )
  if (withdrawCountToday > 0) {
    throw createHttpError('每天限提现 1 次，今天已提交过提现申请', {
      statusCode: 409,
      errorCode: 'worker_withdraw_daily_limit_reached',
    })
  }

  const amount = normalizeAmountFen(payload.amount ?? payload.amountYuan, 0)
  if (amount <= 0) {
    throw createHttpError('提现金额必须大于 0', {
      statusCode: 400,
      errorCode: 'worker_withdraw_amount_invalid',
    })
  }

  const accountChannel = normalizeWithdrawChannel(
    payload.accountChannel || payload.channel,
  )
  const accountName = String(payload.accountName || payload.realName || '').trim()
  const accountNo = String(payload.accountNo || payload.account || '').trim()
  if (!accountName) {
    throw createHttpError('请填写收款人姓名', {
      statusCode: 400,
      errorCode: 'worker_withdraw_account_name_required',
    })
  }
  if (!accountNo) {
    throw createHttpError('请填写收款账号', {
      statusCode: 400,
      errorCode: 'worker_withdraw_account_no_required',
    })
  }

  const financeSummary = await getWorkerFinanceRequestSummary(worker.id)
  const availableAmount = Number(worker.available_amount || 0)
  const availableForWithdraw = Math.max(
    0,
    availableAmount - financeSummary.pendingWithdrawAmount,
  )
  if (amount > availableForWithdraw) {
    throw createHttpError('可提现余额不足，请先减少提现金额或等待已提交申请处理', {
      statusCode: 409,
      errorCode: 'worker_withdraw_amount_insufficient',
    })
  }

  const note = String(payload.note || payload.remark || '').trim()
  const created = await createWorkerFinanceRequest({
    workerId: worker.id,
    requestType: 'withdraw',
    amount,
    accountChannel,
    accountName,
    accountNo,
    note: note || '个人中心提现申请',
    payloadJson: JSON.stringify({
      source: 'worker_profile_withdraw',
      username: worker.username,
      phone: worker.phone || '',
    }),
    now: nowIso(),
  })
  if (!created) {
    throw createHttpError('提现申请创建失败', {
      statusCode: 500,
      errorCode: 'worker_withdraw_request_create_failed',
    })
  }
  return {
    request: mapFinanceRequest(created),
    availableForWithdraw,
  }
}

export async function changeWorkerPassword(
  payload: JsonObject = {},
  session: WorkerSession,
) {
  const worker = await getRequiredWorker(session.workerId)
  const currentPassword = normalizePassword(
    payload.currentPassword || payload.oldPassword,
  )
  const nextPassword = normalizePassword(payload.newPassword || payload.password)

  if (!currentPassword) {
    throw createHttpError('请输入当前密码', {
      statusCode: 400,
      errorCode: 'worker_password_current_required',
    })
  }

  if (!verifyWorkerPassword(currentPassword, worker.password_hash)) {
    throw createHttpError('当前密码不正确', {
      statusCode: 400,
      errorCode: 'worker_password_current_invalid',
    })
  }

  if (!nextPassword) {
    throw createHttpError('请输入新密码', {
      statusCode: 400,
      errorCode: 'worker_password_new_required',
    })
  }

  validateWorkerPassword(nextPassword)

  if (currentPassword === nextPassword) {
    throw createHttpError('新密码不能与当前密码一致', {
      statusCode: 400,
      errorCode: 'worker_password_unchanged',
    })
  }

  const updated = await updateWorkerPassword({
    workerId: worker.id,
    passwordHash: hashWorkerPassword(nextPassword),
    sessionVersion: normalizeSessionVersion(worker.session_version) + 1,
    now: nowIso(),
  })

  return {
    worker: mapWorkerUser(updated || worker),
    reloginRequired: true,
  }
}

export async function listWorkerHallOrders(query: JsonObject = {}, session: WorkerSession) {
  requireActiveWorkerSession(session)
  const page = normalizePage(query.page)
  const pageSize = normalizePageSize(query.pageSize)
  const categoryId = normalizeOptionalId(query.categoryId)
  const worker = await getRequiredWorker(session.workerId)
  await settleOverdueWorkOrders({ workerId: worker.id, limit: 50 })
  const permissions = resolveWorkerPermissions(worker)
  const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
  const visibleAfterIso =
    visibleDelaySeconds > 0
      ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString()
      : ''
  const { items, total } = await listWorkOrders({
    page,
    pageSize,
    status: WORK_ORDER_STATUS.OPEN,
    keyword: String(query.keyword || '').trim(),
    categoryId: categoryId || 0,
    visibleAfterIso,
  })
  const categories = (await listWorkCategories()).map(mapWorkCategory)
  const myShares = await listWorkerSharesByWorker(worker.id)
  const myShareByOrderId = new Map(
    myShares
      .filter((share) => Number(share.work_order_id))
      .map((share) => [Number(share.work_order_id), share]),
  )
  return {
    items: items.map((item) =>
      mapWorkOrderForWorker(item, permissions, myShareByOrderId.get(Number(item.id))),
    ),
    pagination: { page, pageSize, total },
    categories,
    visibleDelaySeconds,
  }
}

export async function grabWorkerHallOrder(workOrderId: number | string, session: WorkerSession) {
  requireActiveWorkerSession(session)
  const worker = await getRequiredWorker(session.workerId)
  const permissions = resolveWorkerPermissions(worker)
  const activeCount = await countWorkerActiveOrders(worker.id)
  if (activeCount >= permissions.maxActiveOrders) {
    throw createHttpError('已达到当前等级最大同时接单量', {
      statusCode: 409,
      errorCode: 'worker_active_order_limit',
    })
  }

  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',
    })
  }

  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,
    deadlineAt: resolveWorkOrderDeadlineAt(workOrder, nowIso()),
    now: nowIso(),
  })
  if (!grabbed.order) {
    throwGrabWorkOrderFailure(grabbed.failureReason)
  }

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

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()
}

export const WORK_ORDER_TIMEOUT_POLICIES = ['reopen', 'cancel_release', 'cancel_deduct'] as const

export type WorkOrderTimeoutPolicy = (typeof WORK_ORDER_TIMEOUT_POLICIES)[number]

export function isWorkOrderTimeoutPolicy(value: unknown): value is WorkOrderTimeoutPolicy {
  return WORK_ORDER_TIMEOUT_POLICIES.includes(value as WorkOrderTimeoutPolicy)
}

export function normalizeWorkOrderTimeoutPolicy(value: unknown): WorkOrderTimeoutPolicy {
  return isWorkOrderTimeoutPolicy(value) ? value : 'reopen'
}

export async function settleDueDepositUnfreezes(
  options: { limit?: number; workerId?: number } = {},
) {
  const due = await listDueDepositUnfreezes({
    limit: Math.max(1, Number(options.limit || 100)),
    workerId: Number(options.workerId || 0),
  })
  let processedCount = 0
  for (const unfreeze of due) {
    const released = await releaseDepositUnfreeze({
      unfreezeId: unfreeze.id,
      now: nowIso(),
    })
    if (released) processedCount += 1
  }
  return {
    checkedCount: due.length,
    processedCount,
  }
}

function resolveChinaDayRange(): { start: string; end: string } {
  const utcNow = new Date()
  const utcDayStartMs =
    Date.UTC(utcNow.getUTCFullYear(), utcNow.getUTCMonth(), utcNow.getUTCDate()) -
    8 * 3_600_000
  return {
    start: new Date(utcDayStartMs).toISOString(),
    end: new Date(utcDayStartMs + 86_400_000).toISOString(),
  }
}

export async function settleOverdueWorkOrders(
  options: { limit?: number; workerId?: number } = {},
) {
  const overdue = await listOverdueWorkOrders({
    limit: Math.max(1, Number(options.limit || 50)),
    workerId: Number(options.workerId || 0),
  })
  let processedCount = 0
  let skippedCount = 0
  for (const workOrder of overdue) {
    const result = await settleOverdueWorkOrder({
      workOrderId: workOrder.id,
      policy: normalizeWorkOrderTimeoutPolicy(workOrder.timeout_policy),
      now: nowIso(),
    })
    if (result.order) {
      processedCount += 1
    } else {
      skippedCount += 1
    }
  }
  return {
    checkedCount: overdue.length,
    processedCount,
    skippedCount,
  }
}

export async function listWorkerMyOrders(query: JsonObject = {}, session: WorkerSession) {
  requireActiveWorkerSession(session)
  const worker = await getRequiredWorker(session.workerId)
  await settleOverdueWorkOrders({ workerId: worker.id, limit: 50 })
  const page = normalizePage(query.page)
  const pageSize = normalizePageSize(query.pageSize)
  const { items, total } = await listWorkOrders({
    page,
    pageSize,
    status: String(query.status || '').trim(),
    keyword: String(query.keyword || '').trim(),
    workerSharingId: worker.id,
  })
  const permissions = resolveWorkerPermissions(worker)
  const myShares = await listWorkerSharesByWorker(worker.id)
  const myShareByOrderId = new Map(
    myShares.map((share) => [Number(share.work_order_id), share]),
  )
  return {
    items: items.map((item) =>
      mapWorkOrderForWorker(item, permissions, myShareByOrderId.get(Number(item.id))),
    ),
    pagination: { page, pageSize, total },
  }
}

export async function joinWorkerSharingOrder(
  workOrderId: number | string,
  payload: JsonObject = {},
  session: WorkerSession,
) {
  requireActiveWorkerSession(session)
  const worker = await getRequiredWorker(session.workerId)
  const permissions = resolveWorkerPermissions(worker)
  const quantity = normalizePositiveInteger(payload.quantity, 1)
  const now = nowIso()
  const result = await joinWorkOrderShare({
    workOrderId: Number(workOrderId),
    workerId: Number(worker.id),
    quantity,
    maxActiveOrders: permissions.maxActiveOrders,
    now,
  })
  if (!result.share) {
    throw resolveWorkOrderShareJoinError(result.failureReason)
  }
  return { share: mapWorkOrderShare(result.share) }
}

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'}`,
  })
}

export async function cancelWorkerOrder(
  workOrderId: number | string,
  session: WorkerSession,
) {
  requireActiveWorkerSession(session)
  const worker = await getRequiredWorker(session.workerId)
  const now = nowIso()
  const since = new Date(Date.now() - WORKER_CANCEL_LIMIT_WINDOW_MS).toISOString()
  const cancelCount = await countWorkerCancellationsSince(worker.id, since)
  if (cancelCount >= WORKER_CANCEL_LIMIT_PER_WINDOW) {
    throw createHttpError(
      `30 天内取消接单已达 ${WORKER_CANCEL_LIMIT_PER_WINDOW} 次上限，请谨慎接单`,
      {
        statusCode: 409,
        errorCode: 'worker_cancel_limit_reached',
      },
    )
  }
  const result = await cancelWorkerWorkOrder({
    workOrderId: Number(workOrderId),
    workerId: Number(worker.id),
    now,
  })
  if (result.failureReason === 'work_order_owner_required') {
    throw createHttpError('只能取消自己接的订单', {
      statusCode: 403,
      errorCode: 'work_order_owner_required',
    })
  }
  if (!result.order) {
    throw createHttpError('当前订单状态不能取消，仅进行中的订单可取消', {
      statusCode: 409,
      errorCode: 'work_order_cancel_status_invalid',
    })
  }
  return {
    order: mapWorkOrderForWorker(
      result.order,
      resolveWorkerPermissions(await getRequiredWorker(session.workerId)),
    ),
  }
}

export async function submitWorkerOrderAcceptance(
  workOrderId: number | string,
  payload: JsonObject = {},
  session: WorkerSession,
) {
  requireActiveWorkerSession(session)
  const workOrder = await getRequiredWorkOrder(workOrderId)
  const workerId = Number(session.workerId)
  const sharingShare = await getWorkOrderShare(workOrder.id, workerId)
  if (sharingShare) {
    return submitWorkerSharingAcceptance(workOrder, sharingShare, payload, session)
  }
  const now = nowIso()
  if (
    workOrder.status === WORK_ORDER_STATUS.IN_PROGRESS &&
    workOrder.deadline_at &&
    new Date(workOrder.deadline_at).getTime() <= Date.now()
  ) {
    await settleOverdueWorkOrder({
      workOrderId: workOrder.id,
      policy: normalizeWorkOrderTimeoutPolicy(workOrder.timeout_policy),
      now,
    })
    throw createHttpError('任务已超时，系统已按超时策略处置', {
      statusCode: 409,
      errorCode: 'work_order_timeout_expired',
    })
  }
  if (Number(workOrder.assigned_worker_id || 0) !== workerId) {
    throw createHttpError('只能提交自己的订单', {
      statusCode: 403,
      errorCode: 'work_order_owner_required',
    })
  }
  if (
    ![WORK_ORDER_STATUS.IN_PROGRESS, WORK_ORDER_STATUS.PROBLEM].includes(workOrder.status as never)
  ) {
    throw createHttpError('当前状态不能提交验收', {
      statusCode: 409,
      errorCode: 'work_order_submit_status_invalid',
    })
  }

  const files = normalizeUploadedFiles(payload.files)
  const imageUrls = [
    ...files.map((file) => file.url || file.mediumUrl || file.thumbnailUrl).filter(Boolean),
    ...normalizeStringArray(payload.imageUrls),
  ]
  if (imageUrls.length === 0) {
    throw createHttpError('请上传验收图片', {
      statusCode: 400,
      errorCode: 'work_order_acceptance_image_required',
    })
  }
  const acceptance = {
    note: String(payload.note || '').trim(),
    files,
    imageUrls: [...new Set(imageUrls)],
    submittedAt: now,
  }
  const updated = await updateWorkOrder(workOrder.id, {
    status: WORK_ORDER_STATUS.PENDING_ACCEPTANCE,
    acceptance_json: acceptance,
    submitted_at: now,
    updated_at: now,
  })
  await createWorkOrderEvent({
    workOrderId: workOrder.id,
    actorType: 'worker',
    actorId: String(session.workerId),
    eventType: 'acceptance_submitted',
    fromStatus: workOrder.status,
    toStatus: WORK_ORDER_STATUS.PENDING_ACCEPTANCE,
    payloadJson: JSON.stringify(acceptance),
    now,
  })
  return {
    order: mapWorkOrderForWorker(
      updated || workOrder,
      resolveWorkerPermissions(await getRequiredWorker(session.workerId)),
    ),
  }
}

async function submitWorkerSharingAcceptance(
  workOrder: WorkOrderRow,
  sharingShare: WorkOrderShareRow,
  payload: JsonObject = {},
  session: WorkerSession,
) {
  if (workOrder.status === WORK_ORDER_STATUS.ACCEPTED) {
    throw createHttpError('该拼单已验收完成', {
      statusCode: 409,
      errorCode: 'work_order_sharing_accepted',
    })
  }
  const now = nowIso()
  const files = normalizeUploadedFiles(payload.files)
  const imageUrls = [
    ...files.map((file) => file.url || file.mediumUrl || file.thumbnailUrl).filter(Boolean),
    ...normalizeStringArray(payload.imageUrls),
  ]
  if (imageUrls.length === 0) {
    throw createHttpError('请上传验收图片', {
      statusCode: 400,
      errorCode: 'work_order_acceptance_image_required',
    })
  }
  const acceptance = {
    note: String(payload.note || '').trim(),
    files,
    imageUrls: [...new Set(imageUrls)],
    submittedAt: now,
  }
  const result = await submitWorkOrderShareAcceptance({
    workOrderId: Number(workOrder.id),
    workerId: Number(session.workerId),
    acceptanceJson: JSON.stringify(acceptance),
    now,
  })
  if (result.failureReason === 'share_status_invalid') {
    throw createHttpError('当前拼单份额已提交，请勿重复提交', {
      statusCode: 409,
      errorCode: 'work_order_sharing_share_submitted',
    })
  }
  if (!result.share) {
    throw createHttpError('未找到拼单份额', {
      statusCode: 404,
      errorCode: 'work_order_sharing_share_not_found',
    })
  }
  return {
    share: mapWorkOrderShare(result.share),
    sharingPendingCount: await countWorkOrderPendingSharingSubmissions(Number(workOrder.id)),
  }
}

export async function collectLookupWorkOrder(payload: JsonObject = {}) {
  const orderNo = String(payload.orderNo || payload.platformOrderId || '').trim()
  if (!orderNo) {
    throw createHttpError('请填写订单号', {
      statusCode: 400,
      errorCode: 'collect_order_no_required',
    })
  }
  const workOrders = await listPendingMaterialWorkOrdersByPlatformOrderId(orderNo)
  if (workOrders.length === 0) {
    throw createHttpError('未找到待完善订单，请确认订单号是否正确', {
      statusCode: 404,
      errorCode: 'collect_order_not_found',
    })
  }
  const [primaryOrder] = workOrders
  if (!primaryOrder) {
    throw createHttpError('未找到待完善订单，请确认订单号是否正确', {
      statusCode: 404,
      errorCode: 'collect_order_not_found',
    })
  }
  return {
    order: mapWorkOrderPublic(primaryOrder),
    fields: resolveRequirementFields(primaryOrder),
    orders: workOrders.map(mapCollectLookupOrder),
    pendingCount: workOrders.length,
  }
}

export async function collectSubmitWorkOrder(payload: JsonObject = {}) {
  const orderNo = String(payload.orderNo || payload.platformOrderId || '').trim()
  if (!orderNo) {
    throw createHttpError('请填写订单号', {
      statusCode: 400,
      errorCode: 'collect_order_no_required',
    })
  }
  const workOrders = await listPendingMaterialWorkOrdersByPlatformOrderId(orderNo)
  if (workOrders.length === 0) {
    throw createHttpError('未找到待完善订单，请确认订单号是否正确', {
      statusCode: 404,
      errorCode: 'collect_order_not_found',
    })
  }
  const workOrder = resolveCollectSubmitTargetWorkOrder(
    orderNo,
    workOrders,
    payload.workOrderId || payload.work_order_id,
  )

  const fields = resolveRequirementFields(workOrder)
  const submittedFields = normalizeSubmittedFields(payload.fields || payload.material || payload)
  const complete = fields.every(
    (field) => !field.required || String(submittedFields[field.key] || '').trim(),
  )
  const now = nowIso()
  const screenshots = normalizeUploadedFiles(payload.screenshots || payload.files)
  const material = {
    ...safeParseJson(workOrder.material_json),
    collect: {
      fields: submittedFields,
      screenshots,
      submittedAt: now,
      complete,
    },
  }
  const nextStatus = complete ? WORK_ORDER_STATUS.UNASSIGNED : WORK_ORDER_STATUS.PENDING_MATERIAL
  const updated = await updateWorkOrder(workOrder.id, {
    status: nextStatus,
    material_json: material,
    updated_at: now,
  })
  await createWorkOrderEvent({
    workOrderId: workOrder.id,
    actorType: 'collect',
    actorId: orderNo,
    eventType: 'material_submitted',
    fromStatus: workOrder.status,
    toStatus: nextStatus,
    payloadJson: JSON.stringify({ fields: submittedFields, complete }),
    now,
  })
  return {
    order: mapWorkOrderPublic(updated || workOrder),
    complete,
  }
}

export function resolveCollectSubmitTargetWorkOrder(
  orderNo: string,
  workOrders: WorkOrderRow[],
  requestedWorkOrderId: unknown,
): WorkOrderRow {
  const workOrderId = normalizeOptionalId(requestedWorkOrderId)
  if (workOrderId) {
    const matched = workOrders.find((item) => Number(item.id) === workOrderId)
    if (matched) {
      return matched
    }
    throw createHttpError('待完善订单不存在或已更新，请刷新后重试', {
      statusCode: 404,
      errorCode: 'collect_work_order_not_found',
      context: { orderNo, workOrderId },
    })
  }

  if (workOrders.length === 1) {
    const [singleWorkOrder] = workOrders
    if (singleWorkOrder) {
      return singleWorkOrder
    }
  }

  throw createHttpError('该订单存在多个待完善商品，请逐个提交资料', {
    statusCode: 409,
    errorCode: 'collect_work_order_required',
    context: {
      orderNo,
      pendingCount: workOrders.length,
    },
  })
}

export async function getRequiredWorker(workerId: number | string): Promise<WorkerUserRow> {
  const worker = await getWorkerUserById(workerId)
  if (!worker) {
    throw createHttpError('接单账号不存在', {
      statusCode: 404,
      errorCode: 'worker_not_found',
    })
  }
  return worker
}

export async function getRequiredWorkOrder(workOrderId: number | string): Promise<WorkOrderRow> {
  const workOrder = await getWorkOrderById(workOrderId)
  if (!workOrder) {
    throw createHttpError('接单订单不存在', {
      statusCode: 404,
      errorCode: 'work_order_not_found',
    })
  }
  return workOrder
}
