import { query, withTransaction } from '../../db/client.js'
import { ensureWorkerWalletWithClient, getWorkerWalletWithClient, toJsonString, toPositiveInteger } from './shared.js'
import type { CreateWorkOrderInput, GrabWorkOrderResult, ListInput, ProblemWorkOrderResolutionAction, ProductRuleListInput, WorkCategoryRow, WorkerDepositUnfreezeRow, WorkOrderRow, WorkOrderShareRow, WorkProductRuleRow } from './types.js'
import type { PoolClient } from 'pg'
import { maybeUpgradeWorkerLevelWithClient } from './worker-repo.js'


const WORK_ORDER_SELECT = `
  SELECT
    wo.*,
    wc.name AS category_name,
    wu.username AS worker_username,
    wu.display_name AS worker_display_name
  FROM work_orders wo
  LEFT JOIN work_categories wc ON wc.id = wo.category_id
  LEFT JOIN worker_users wu ON wu.id = wo.assigned_worker_id
`

const WORK_PRODUCT_RULE_SELECT = `
  SELECT
    wpr.*,
    wc.name AS category_name
  FROM work_product_rules wpr
  LEFT JOIN work_categories wc ON wc.id = wpr.category_id
`

const WORK_ORDER_SHARE_SELECT = `
  SELECT
    wos.*,
    wu.username AS worker_username,
    wu.display_name AS worker_display_name
  FROM work_order_shares wos
  LEFT JOIN worker_users wu ON wu.id = wos.worker_id
`

export async function listWorkOrderShares(
  workOrderId: number | string,
): Promise<WorkOrderShareRow[]> {
  const result = await query<WorkOrderShareRow>(
    `${WORK_ORDER_SHARE_SELECT} WHERE wos.work_order_id = $1 ORDER BY wos.id ASC`,
    [Number(workOrderId)],
  )
  return result.rows
}

export async function listWorkOrderSharesByOrderIds(
  workOrderIds: number[],
): Promise<WorkOrderShareRow[]> {
  if (workOrderIds.length === 0) return []
  const result = await query<WorkOrderShareRow>(
    `${WORK_ORDER_SHARE_SELECT}
     WHERE wos.work_order_id = ANY($1::bigint[])
     ORDER BY wos.id ASC`,
    [workOrderIds],
  )
  return result.rows
}

export async function listWorkerSharesByWorker(
  workerId: number | string,
): Promise<WorkOrderShareRow[]> {
  const result = await query<WorkOrderShareRow>(
    `${WORK_ORDER_SHARE_SELECT}
     WHERE wos.worker_id = $1 AND wos.status != 'cancelled'
     ORDER BY wos.id DESC`,
    [Number(workerId)],
  )
  return result.rows
}

export async function countWorkOrderPendingSharingSubmissions(
  workOrderId: number | string,
): Promise<number> {
  const result = await query<{ total: number }>(
    `
      SELECT COUNT(*)::int AS total
      FROM work_order_shares
      WHERE work_order_id = $1 AND status = 'joined'
    `,
    [Number(workOrderId)],
  )
  return Number(result.rows[0]?.total || 0)
}

export async function getWorkOrderShare(
  workOrderId: number | string,
  workerId: number | string,
): Promise<WorkOrderShareRow | null> {
  const result = await query<WorkOrderShareRow>(
    `${WORK_ORDER_SHARE_SELECT}
     WHERE wos.work_order_id = $1 AND wos.worker_id = $2 LIMIT 1`,
    [Number(workOrderId), Number(workerId)],
  )
  return result.rows[0] || null
}

export async function joinWorkOrderShare(input: {
  workOrderId: number
  workerId: number
  quantity: number
  maxActiveOrders: number
  now: string
}): Promise<{
  share: WorkOrderShareRow | null
  failureReason:
    | 'work_order_not_open'
    | 'sharing_disabled'
    | 'work_order_owner_conflict'
    | 'sharing_quantity_full'
    | 'worker_active_order_limit'
    | 'worker_deposit_insufficient'
    | null
}> {
  return withTransaction(async (client) => {
    await ensureWorkerWalletWithClient(client, input.workerId, input.now)

    const orderResult = await client.query<WorkOrderRow>(
      `
        SELECT *
        FROM work_orders
        WHERE id = $1
        FOR UPDATE
      `,
      [input.workOrderId],
    )
    const workOrder = orderResult.rows[0] || null
    if (!workOrder || workOrder.status !== 'open') {
      return { share: null, failureReason: 'work_order_not_open' }
    }
    if (workOrder.sharing_enabled !== true) {
      return { share: null, failureReason: 'sharing_disabled' }
    }
    if (Number(workOrder.assigned_worker_id || 0) === input.workerId) {
      return { share: null, failureReason: 'work_order_owner_conflict' }
    }

    const totalQuantity = toPositiveInteger(workOrder.sharing_total_quantity, 1)
    const joinedResult = await client.query<{ total: number }>(
      `
        SELECT COALESCE(SUM(quantity), 0)::int AS total
        FROM work_order_shares
        WHERE work_order_id = $1 AND status != 'cancelled'
      `,
      [input.workOrderId],
    )
    const joinedQuantity = Number(joinedResult.rows[0]?.total || 0)
    const remaining = Math.max(0, totalQuantity - joinedQuantity)
    if (input.quantity <= 0 || input.quantity > remaining) {
      return { share: null, failureReason: 'sharing_quantity_full' }
    }

    const activeOrderCount = await countWorkerActiveOrdersWithClient(client, input.workerId)
    if (activeOrderCount >= input.maxActiveOrders) {
      return { share: null, failureReason: 'worker_active_order_limit' }
    }

    const existingShare = await client.query<{ id: number }>(
      'SELECT id FROM work_order_shares WHERE work_order_id = $1 AND worker_id = $2',
      [input.workOrderId, input.workerId],
    )
    if (existingShare.rows[0]) {
      return { share: null, failureReason: 'sharing_quantity_full' }
    }

    const unitReward = toPositiveInteger(workOrder.sharing_unit_reward, 0)
    const shareReward = input.quantity * unitReward
    const shareDeposit = Math.round(
      (Number(workOrder.required_deposit_amount || 0) * input.quantity) / totalQuantity,
    )

    const wallet = await getWorkerWalletWithClient(client, input.workerId)
    const available = Number(wallet?.available_amount || 0)
    if (available < shareDeposit) {
      return { share: null, failureReason: 'worker_deposit_insufficient' }
    }

    const shareResult = await client.query<{ id: number }>(
      `
        INSERT INTO work_order_shares (
          work_order_id, worker_id, quantity, unit_reward, share_reward,
          share_deposit, status, acceptance_json, created_at, updated_at
        ) VALUES ($1, $2, $3, $4, $5, $6, 'joined', '{}'::jsonb, $7, $7)
        RETURNING id
      `,
      [
        input.workOrderId,
        input.workerId,
        input.quantity,
        unitReward,
        shareReward,
        shareDeposit,
        input.now,
      ],
    )

    if (shareDeposit > 0) {
      const nextAvailable = available - shareDeposit
      const nextFrozen = Number(wallet?.frozen_deposit_amount || 0) + shareDeposit
      await client.query(
        `
          UPDATE worker_wallets
          SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
          WHERE worker_id = $4
        `,
        [nextAvailable, nextFrozen, input.now, input.workerId],
      )
      await client.query(
        `
          INSERT INTO worker_wallet_ledgers (
            worker_id, ledger_type, amount, balance_after, frozen_after,
            related_work_order_id, note, payload_json, created_at
          ) VALUES ($1, 'deposit_freeze', $2, $3, $4, $5, $6, $7::jsonb, $8)
        `,
        [
          input.workerId,
          shareDeposit,
          nextAvailable,
          nextFrozen,
          input.workOrderId,
          `拼单冻结押金 ${input.quantity} 份`,
          JSON.stringify({ workOrderId: input.workOrderId, quantity: input.quantity }),
          input.now,
        ],
      )
    }

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'worker',
      actorId: String(input.workerId),
      eventType: 'sharing_joined',
      fromStatus: 'open',
      toStatus: 'open',
      payloadJson: JSON.stringify({ workerId: input.workerId, quantity: input.quantity }),
      now: input.now,
    })

    const share = await client.query<WorkOrderShareRow>(
      `${WORK_ORDER_SHARE_SELECT} WHERE wos.id = $1 LIMIT 1`,
      [Number(shareResult.rows[0]?.id || 0)],
    )
    return { share: share.rows[0] || null, failureReason: null }
  })
}

export async function submitWorkOrderShareAcceptance(input: {
  workOrderId: number
  workerId: number
  acceptanceJson: string
  now: string
}): Promise<{ share: WorkOrderShareRow | null; failureReason: 'share_not_found' | 'share_status_invalid' | null }> {
  return withTransaction(async (client) => {
    const shareResult = await client.query<WorkOrderShareRow>(
      `${WORK_ORDER_SHARE_SELECT}
       WHERE wos.work_order_id = $1 AND wos.worker_id = $2
       FOR UPDATE`,
      [input.workOrderId, input.workerId],
    )
    const share = shareResult.rows[0] || null
    if (!share) {
      return { share: null, failureReason: 'share_not_found' }
    }
    if (share.status !== 'joined') {
      return { share, failureReason: 'share_status_invalid' }
    }
    await client.query(
      `
        UPDATE work_order_shares
        SET status = 'submitted', acceptance_json = $1::jsonb, submitted_at = $2, updated_at = $2
        WHERE id = $3
      `,
      [input.acceptanceJson, input.now, Number(share.id)],
    )
    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'worker',
      actorId: String(input.workerId),
      eventType: 'sharing_acceptance_submitted',
      fromStatus: 'joined',
      toStatus: 'submitted',
      payloadJson: JSON.stringify({ workerId: input.workerId, quantity: share.quantity }),
      now: input.now,
    })
    const updated = await client.query<WorkOrderShareRow>(
      `${WORK_ORDER_SHARE_SELECT} WHERE wos.id = $1 LIMIT 1`,
      [Number(share.id)],
    )
    return { share: updated.rows[0] || null, failureReason: null }
  })
}

export async function getWorkCategoryByKey(categoryKey: string): Promise<WorkCategoryRow | null> {
  const result = await query<WorkCategoryRow>(
    'SELECT * FROM work_categories WHERE category_key = $1 LIMIT 1',
    [String(categoryKey || '').trim()],
  )
  return result.rows[0] || null
}

export async function getWorkCategoryById(categoryId: number | string): Promise<WorkCategoryRow | null> {
  const result = await query<WorkCategoryRow>(
    'SELECT * FROM work_categories WHERE id = $1 LIMIT 1',
    [Number(categoryId)],
  )
  return result.rows[0] || null
}

export async function listWorkCategories(): Promise<WorkCategoryRow[]> {
  const result = await query<WorkCategoryRow>(
    `SELECT * FROM work_categories WHERE status = 'active' ORDER BY sort_order ASC, id ASC`,
  )
  return result.rows
}

export async function listAllWorkCategories(): Promise<WorkCategoryRow[]> {
  const result = await query<WorkCategoryRow>(
    `SELECT * FROM work_categories ORDER BY sort_order ASC, id ASC`,
  )
  return result.rows
}

export async function upsertWorkCategory(input: {
  categoryKey: string
  name: string
  sortOrder: number
  status: string
  now: string
}): Promise<WorkCategoryRow | null> {
  const result = await query<WorkCategoryRow>(
    `
      INSERT INTO work_categories (category_key, name, sort_order, status, created_at, updated_at)
      VALUES ($1, $2, $3, $4, $5, $6)
      ON CONFLICT (category_key) DO UPDATE
      SET name = EXCLUDED.name, sort_order = EXCLUDED.sort_order, status = EXCLUDED.status, updated_at = EXCLUDED.updated_at
      RETURNING *
    `,
    [input.categoryKey, input.name, input.sortOrder, input.status, input.now, input.now],
  )
  return result.rows[0] || null
}

export async function countWorkCategoryUsages(categoryId: number | string): Promise<{
  workOrderCount: number
  productRuleCount: number
}> {
  const [orders, rules] = await Promise.all([
    query<{ total: number }>(
      'SELECT COUNT(*)::int AS total FROM work_orders WHERE category_id = $1',
      [Number(categoryId)],
    ),
    query<{ total: number }>(
      'SELECT COUNT(*)::int AS total FROM work_product_rules WHERE category_id = $1',
      [Number(categoryId)],
    ),
  ])
  return {
    workOrderCount: Number(orders.rows[0]?.total || 0),
    productRuleCount: Number(rules.rows[0]?.total || 0),
  }
}

export async function deleteWorkCategory(
  categoryId: number | string,
): Promise<{ deleted: boolean }> {
  const result = await query<{ id: number }>(
    'DELETE FROM work_categories WHERE id = $1 RETURNING id',
    [Number(categoryId)],
  )
  return { deleted: Boolean(result.rows[0]) }
}

export async function listWorkProductRules({
  enabled = null,
  keyword = '',
}: ProductRuleListInput = {}): Promise<WorkProductRuleRow[]> {
  const { whereClause, params } = buildWorkProductRuleWhere({ enabled, keyword })
  const result = await query<WorkProductRuleRow>(
    `${WORK_PRODUCT_RULE_SELECT}
     ${whereClause}
     ORDER BY wpr.sort_order ASC, wpr.id DESC`,
    params,
  )
  return result.rows
}

export async function getWorkProductRuleByKey(ruleKey: string): Promise<WorkProductRuleRow | null> {
  const result = await query<WorkProductRuleRow>(
    `${WORK_PRODUCT_RULE_SELECT} WHERE wpr.rule_key = $1 LIMIT 1`,
    [String(ruleKey || '').trim()],
  )
  return result.rows[0] || null
}

export async function upsertWorkProductRule(input: {
  ruleKey: string
  provider: string
  platform: string
  shopId: string
  skuCode: string
  productName: string
  matchType: string
  categoryId: number | null
  enabled: boolean
  autoCreate: boolean
  rewardAmount: number
  requiredDepositAmount: number
  depositThresholdAmount: number
  sharingEnabled?: boolean
  sharingTotalQuantity?: number
  sharingUnitReward?: number
  timeoutMinutes?: number
  timeoutPolicy?: string
  requirementJson: string
  sortOrder: number
  now: string
}): Promise<WorkProductRuleRow | null> {
  const result = await query<WorkProductRuleRow>(
    `
      INSERT INTO work_product_rules (
        rule_key, provider, platform, shop_id, sku_code, product_name,
        match_type, category_id, enabled, auto_create, reward_amount,
        required_deposit_amount, deposit_threshold_amount,
        sharing_enabled, sharing_total_quantity, sharing_unit_reward,
        timeout_minutes, timeout_policy,
        requirement_json,
        sort_order, created_at, updated_at
      ) VALUES (
        $1, $2, $3, $4, $5, $6,
        $7, $8, $9, $10, $11,
        $12, $13,
        $14, $15, $16,
        $17, $18,
        $19::jsonb,
        $20, $21, $22
      )
      ON CONFLICT (rule_key) DO UPDATE
      SET
        provider = EXCLUDED.provider,
        platform = EXCLUDED.platform,
        shop_id = EXCLUDED.shop_id,
        sku_code = EXCLUDED.sku_code,
        product_name = EXCLUDED.product_name,
        match_type = EXCLUDED.match_type,
        category_id = EXCLUDED.category_id,
        enabled = EXCLUDED.enabled,
        auto_create = EXCLUDED.auto_create,
        reward_amount = EXCLUDED.reward_amount,
        required_deposit_amount = EXCLUDED.required_deposit_amount,
        deposit_threshold_amount = EXCLUDED.deposit_threshold_amount,
        sharing_enabled = EXCLUDED.sharing_enabled,
        sharing_total_quantity = EXCLUDED.sharing_total_quantity,
        sharing_unit_reward = EXCLUDED.sharing_unit_reward,
        timeout_minutes = EXCLUDED.timeout_minutes,
        timeout_policy = EXCLUDED.timeout_policy,
        requirement_json = EXCLUDED.requirement_json,
        sort_order = EXCLUDED.sort_order,
        updated_at = EXCLUDED.updated_at
      RETURNING *
    `,
    [
      input.ruleKey,
      input.provider,
      input.platform,
      input.shopId,
      input.skuCode,
      input.productName,
      input.matchType,
      input.categoryId,
      input.enabled,
      input.autoCreate,
      input.rewardAmount,
      input.requiredDepositAmount,
      input.depositThresholdAmount,
      input.sharingEnabled === true,
      toPositiveInteger(input.sharingTotalQuantity, 1),
      toPositiveInteger(input.sharingUnitReward, 0),
      toPositiveInteger(input.timeoutMinutes, 0),
      String(input.timeoutPolicy || 'reopen').trim(),
      input.requirementJson,
      input.sortOrder,
      input.now,
      input.now,
    ],
  )
  return getWorkProductRuleByKey(result.rows[0]?.rule_key || input.ruleKey)
}

export async function createWorkOrder(input: CreateWorkOrderInput): Promise<WorkOrderRow | null> {
  const result = await query<{ id: number }>(
    `
      INSERT INTO work_orders (
        work_order_no, order_id, order_item_id, task_id, platform_order_id,
        product_name, category_id, status, reward_amount, required_deposit_amount,
        deposit_threshold_amount, sharing_enabled, sharing_total_quantity,
        sharing_unit_reward, timeout_minutes, timeout_policy,
        material_json, requirement_json, created_at, updated_at
      ) VALUES (
        $1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
        $11, $12, $13, $14, $15, $16, $17::jsonb, $18::jsonb, $19, $20
      )
      RETURNING id
    `,
    [
      input.workOrderNo,
      input.orderId || null,
      input.orderItemId || null,
      input.taskId || null,
      input.platformOrderId,
      input.productName,
      input.categoryId || null,
      input.status,
      input.rewardAmount,
      input.requiredDepositAmount,
      input.depositThresholdAmount,
      input.sharingEnabled === true,
      toPositiveInteger(input.sharingTotalQuantity, 1),
      toPositiveInteger(input.sharingUnitReward, 0),
      toPositiveInteger(input.timeoutMinutes, 0),
      String(input.timeoutPolicy || 'reopen').trim() || 'reopen',
      input.materialJson,
      input.requirementJson,
      input.now,
      input.now,
    ],
  )
  return getWorkOrderById(result.rows[0]?.id || 0)
}

export async function getWorkOrderByOrderItemId(
  orderItemId: number | string,
): Promise<WorkOrderRow | null> {
  const result = await query<WorkOrderRow>(
    `${WORK_ORDER_SELECT} WHERE wo.order_item_id = $1 LIMIT 1`,
    [Number(orderItemId)],
  )
  return result.rows[0] || null
}

export async function getWorkOrderById(workOrderId: number | string): Promise<WorkOrderRow | null> {
  const result = await query<WorkOrderRow>(`${WORK_ORDER_SELECT} WHERE wo.id = $1 LIMIT 1`, [
    Number(workOrderId),
  ])
  return result.rows[0] || null
}

export async function findPendingMaterialWorkOrderByPlatformOrderId(
  platformOrderId: string,
): Promise<WorkOrderRow | null> {
  const result = await query<WorkOrderRow>(
    `${WORK_ORDER_SELECT}
     WHERE wo.platform_order_id = $1 AND wo.status = 'pending_material'
     ORDER BY wo.id DESC
     LIMIT 1`,
    [String(platformOrderId || '').trim()],
  )
  return result.rows[0] || null
}

export async function listPendingMaterialWorkOrdersByPlatformOrderId(
  platformOrderId: string,
): Promise<WorkOrderRow[]> {
  const result = await query<WorkOrderRow>(
    `${WORK_ORDER_SELECT}
     WHERE wo.platform_order_id = $1 AND wo.status = 'pending_material'
     ORDER BY wo.id ASC`,
    [String(platformOrderId || '').trim()],
  )
  return result.rows
}

export async function listWorkOrdersByPlatformOrderId(
  platformOrderId: string,
): Promise<WorkOrderRow[]> {
  const result = await query<WorkOrderRow>(
    `${WORK_ORDER_SELECT}
     WHERE wo.platform_order_id = $1
     ORDER BY wo.id ASC`,
    [String(platformOrderId || '').trim()],
  )
  return result.rows
}

export async function listWorkOrders({
  page = 1,
  pageSize = 20,
  status = '',
  keyword = '',
  workerId = 0,
  categoryId = 0,
  workerSharingId = 0,
  visibleAfterIso = '',
}: ListInput = {}): Promise<{ items: WorkOrderRow[]; total: number }> {
  const { whereClause, params } = buildWorkOrderWhere({
    status,
    keyword,
    workerId,
    categoryId,
    workerSharingId,
    visibleAfterIso,
  })
  const totalResult = await query<{ total: number }>(
    `
      SELECT COUNT(*)::int AS total
      FROM work_orders wo
      ${whereClause}
    `,
    params,
  )
  const offset = (page - 1) * pageSize
  params.push(pageSize, offset)
  const itemsResult = await query<WorkOrderRow>(
    `${WORK_ORDER_SELECT}
     ${whereClause}
     ORDER BY wo.id DESC
     LIMIT $${params.length - 1} OFFSET $${params.length}`,
    params,
  )
  return { items: itemsResult.rows, total: Number(totalResult.rows[0]?.total || 0) }
}

export async function updateWorkOrder(
  workOrderId: number | string,
  patch: Partial<
    Pick<
      WorkOrderRow,
      | 'status'
      | 'assigned_worker_id'
      | 'material_json'
      | 'acceptance_json'
      | 'problem_note'
      | 'reward_amount'
      | 'sharing_enabled'
      | 'sharing_total_quantity'
      | 'sharing_unit_reward'
      | 'published_at'
      | 'assigned_at'
      | 'submitted_at'
      | 'accepted_at'
      | 'updated_at'
    >
  >,
): Promise<WorkOrderRow | null> {
  const current = await getWorkOrderById(workOrderId)
  if (!current) return null
  const next = { ...current, ...patch }
  await query(
    `
      UPDATE work_orders
      SET
        status = $1,
        assigned_worker_id = $2,
        material_json = $3::jsonb,
        acceptance_json = $4::jsonb,
        problem_note = $5,
        reward_amount = $6,
        sharing_enabled = $7,
        sharing_total_quantity = $8,
        sharing_unit_reward = $9,
        published_at = $10,
        assigned_at = $11,
        submitted_at = $12,
        accepted_at = $13,
        updated_at = $14
      WHERE id = $15
    `,
    [
      next.status,
      next.assigned_worker_id || null,
      toJsonString(next.material_json),
      toJsonString(next.acceptance_json),
      next.problem_note || '',
      toPositiveInteger(next.reward_amount, 0),
      next.sharing_enabled === true,
      toPositiveInteger(next.sharing_total_quantity, 1),
      toPositiveInteger(next.sharing_unit_reward, 0),
      next.published_at || null,
      next.assigned_at || null,
      next.submitted_at || null,
      next.accepted_at || null,
      next.updated_at,
      Number(workOrderId),
    ],
  )
  return getWorkOrderById(workOrderId)
}

export async function updateWorkOrderBasic(
  workOrderId: number | string,
  patch: {
    productName?: string
    platformOrderId?: string
    categoryId?: number | null
    rewardAmount?: number
    requiredDepositAmount?: number
    depositThresholdAmount?: number
    requirementJson?: string
    timeoutMinutes?: number
    timeoutPolicy?: string
    updatedAt: string
  },
): Promise<WorkOrderRow | null> {
  const current = await getWorkOrderById(workOrderId)
  if (!current) return null
  await query(
    `
      UPDATE work_orders
      SET
        platform_order_id = $1,
        product_name = $2,
        category_id = $3,
        reward_amount = $4,
        required_deposit_amount = $5,
        deposit_threshold_amount = $6,
        requirement_json = $7::jsonb,
        timeout_minutes = $8,
        timeout_policy = $9,
        updated_at = $10
      WHERE id = $11
    `,
    [
      String((patch.platformOrderId ?? current.platform_order_id) || '').trim(),
      String((patch.productName ?? current.product_name) || '').trim(),
      patch.categoryId === undefined ? current.category_id : patch.categoryId,
      toPositiveInteger(patch.rewardAmount ?? current.reward_amount, 0),
      toPositiveInteger(
        patch.requiredDepositAmount ?? current.required_deposit_amount,
        0,
      ),
      toPositiveInteger(
        patch.depositThresholdAmount ?? current.deposit_threshold_amount,
        0,
      ),
      toJsonString(patch.requirementJson ?? current.requirement_json),
      toPositiveInteger(patch.timeoutMinutes ?? current.timeout_minutes, 0),
      String((patch.timeoutPolicy ?? current.timeout_policy) || 'reopen').trim(),
      patch.updatedAt,
      Number(workOrderId),
    ],
  )
  return getWorkOrderById(workOrderId)
}

export async function deleteWorkOrder(workOrderId: number | string): Promise<boolean> {
  const result = await query(
    `
      DELETE FROM work_orders
      WHERE id = $1
    `,
    [Number(workOrderId)],
  )
  return Number(result.rowCount || 0) > 0
}

export async function grabWorkOrder(input: {
  workOrderId: number
  workerId: number
  depositAmount: number
  maxActiveOrders: number
  deadlineAt: string | null
  now: string
}): Promise<GrabWorkOrderResult> {
  return withTransaction(async (client) => {
    await ensureWorkerWalletWithClient(client, input.workerId, input.now)
    const wallet = await getWorkerWalletWithClient(client, input.workerId)
    const activeOrderCount = await countWorkerActiveOrdersWithClient(client, input.workerId)
    if (activeOrderCount >= input.maxActiveOrders) {
      return {
        order: null,
        failureReason: 'worker_active_order_limit',
      }
    }

    const available = Number(wallet?.available_amount || 0)
    if (available < input.depositAmount) {
      return {
        order: null,
        failureReason: 'worker_deposit_insufficient',
      }
    }

    const result = await client.query<{ id: number }>(
      `
        UPDATE work_orders
        SET
          status = 'in_progress',
          assigned_worker_id = $1,
          last_assigned_worker_id = $1,
          assigned_at = $2,
          deadline_at = $3,
          updated_at = $2
        WHERE id = $4
          AND status = 'open'
          AND assigned_worker_id IS NULL
        RETURNING id
      `,
      [input.workerId, input.now, input.deadlineAt, input.workOrderId],
    )
    if (!result.rows[0]) {
      return {
        order: null,
        failureReason: 'work_order_not_open',
      }
    }

    if (input.depositAmount > 0) {
      const nextAvailable = available - input.depositAmount
      const nextFrozen = Number(wallet?.frozen_deposit_amount || 0) + input.depositAmount
      await client.query(
        `
          UPDATE worker_wallets
          SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
          WHERE worker_id = $4
        `,
        [nextAvailable, nextFrozen, input.now, input.workerId],
      )
      await client.query(
        `
          INSERT INTO worker_wallet_ledgers (
            worker_id, ledger_type, amount, balance_after, frozen_after,
            related_work_order_id, note, payload_json, created_at
          ) VALUES ($1, 'deposit_freeze', $2, $3, $4, $5, '抢单冻结押金', '{}'::jsonb, $6)
        `,
        [
          input.workerId,
          -input.depositAmount,
          nextAvailable,
          nextFrozen,
          input.workOrderId,
          input.now,
        ],
      )
    }

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'worker',
      actorId: String(input.workerId),
      eventType: 'grabbed',
      fromStatus: 'open',
      toStatus: 'in_progress',
      payloadJson: '{}',
      now: input.now,
    })

    return {
      order: await getWorkOrderByIdWithClient(client, input.workOrderId),
      failureReason: null,
    }
  })
}

export async function cancelWorkerWorkOrder(input: {
  workOrderId: number
  workerId: number
  now: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason: 'work_order_not_in_progress' | 'work_order_owner_required' | null
}> {
  return withTransaction(async (client) => {
    const currentResult = await client.query<WorkOrderRow>(
      `
        SELECT *
        FROM work_orders
        WHERE id = $1
        FOR UPDATE
      `,
      [input.workOrderId],
    )
    const workOrder = currentResult.rows[0] || null
    if (!workOrder || workOrder.status !== 'in_progress') {
      return { order: null, failureReason: 'work_order_not_in_progress' }
    }
    if (Number(workOrder.assigned_worker_id || 0) !== input.workerId) {
      return { order: null, failureReason: 'work_order_owner_required' }
    }

    await ensureWorkerWalletWithClient(client, input.workerId, input.now)
    const wallet = await getWorkerWalletWithClient(client, input.workerId)
    const releaseAmount = Math.min(
      await getOutstandingDepositAmountWithClient(client, input.workerId, input.workOrderId),
      Number(wallet?.frozen_deposit_amount || 0),
    )

    await client.query(
      `
        UPDATE work_orders
        SET
          status = 'open',
          assigned_worker_id = NULL,
          assigned_at = NULL,
          deadline_at = NULL,
          updated_at = $1
        WHERE id = $2
      `,
      [input.now, input.workOrderId],
    )

    if (releaseAmount > 0) {
      const nextAvailable = Number(wallet?.available_amount || 0) + releaseAmount
      const nextFrozen = Math.max(
        0,
        Number(wallet?.frozen_deposit_amount || 0) - releaseAmount,
      )
      await client.query(
        `
          UPDATE worker_wallets
          SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
          WHERE worker_id = $4
        `,
        [nextAvailable, nextFrozen, input.now, input.workerId],
      )
      await client.query(
        `
          INSERT INTO worker_wallet_ledgers (
            worker_id, ledger_type, amount, balance_after, frozen_after,
            related_work_order_id, note, payload_json, created_at
          ) VALUES ($1, 'deposit_release', $2, $3, $4, $5, '取消接单退还押金', '{}'::jsonb, $6)
        `,
        [input.workerId, releaseAmount, nextAvailable, nextFrozen, input.workOrderId, input.now],
      )
    }

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'worker',
      actorId: String(input.workerId),
      eventType: 'cancelled_by_worker',
      fromStatus: 'in_progress',
      toStatus: 'open',
      payloadJson: JSON.stringify({ releaseAmount }),
      now: input.now,
    })

    return {
      order: await getWorkOrderByIdWithClient(client, input.workOrderId),
      failureReason: null,
    }
  })
}

export async function countWorkerCancellationsSince(
  workerId: number | string,
  sinceIso: string,
): Promise<number> {
  const result = await query<{ total: number }>(
    `
      SELECT COUNT(*)::int AS total
      FROM work_order_events
      WHERE actor_type = 'worker'
        AND actor_id = $1
        AND event_type = 'cancelled_by_worker'
        AND created_at >= $2
    `,
    [String(workerId), sinceIso],
  )
  return Number(result.rows[0]?.total || 0)
}

export async function acceptWorkOrderAndSettle(input: {
  workOrderId: number
  unfreezeDays: number
  now: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason: 'sharing_pending_submissions' | null
}> {
  return withTransaction(async (client) => {
    const currentResult = await client.query<WorkOrderRow>(
      `
        SELECT *
        FROM work_orders
        WHERE id = $1
          AND status = 'pending_acceptance'
        FOR UPDATE
      `,
      [input.workOrderId],
    )
    const workOrder = currentResult.rows[0] || null
    if (!workOrder) {
      return { order: null, failureReason: null }
    }

    const sharesResult = await client.query<WorkOrderShareRow>(
      `
        SELECT *
        FROM work_order_shares
        WHERE work_order_id = $1
          AND status != 'cancelled'
        ORDER BY id ASC
        FOR UPDATE
      `,
      [input.workOrderId],
    )
    const shares = sharesResult.rows
    if (shares.length > 0) {
      const pendingShares = shares.filter((share) => share.status === 'joined')
      if (pendingShares.length > 0) {
        return {
          order: null,
          failureReason: 'sharing_pending_submissions',
        }
      }
    }

    await client.query(
      `
        UPDATE work_orders
        SET status = 'accepted', accepted_at = $1, updated_at = $1
        WHERE id = $2
      `,
      [input.now, input.workOrderId],
    )

    if (shares.length > 0) {
      for (const share of shares) {
        const shareWorkerId = Number(share.worker_id || 0)
        if (!shareWorkerId) continue
        await ensureWorkerWalletWithClient(client, shareWorkerId, input.now)
        const wallet = await getWorkerWalletWithClient(client, shareWorkerId)
        const releaseAmount = Math.min(
          Number(share.share_deposit || 0),
          Number(wallet?.frozen_deposit_amount || 0),
        )
        const shareReward = Number(share.share_reward || 0)
        const shouldDelayUnfreeze = input.unfreezeDays > 0
        const nextAvailable =
          Number(wallet?.available_amount || 0) +
          shareReward +
          (shouldDelayUnfreeze ? 0 : releaseAmount)
        const nextFrozen = Math.max(
          0,
          Number(wallet?.frozen_deposit_amount || 0) - releaseAmount,
        )

        await client.query(
          `
            UPDATE worker_wallets
            SET
              available_amount = $1,
              frozen_deposit_amount = $2,
              pending_unfreeze_amount = pending_unfreeze_amount + $3,
              total_settled_amount = total_settled_amount + $4,
              updated_at = $5
            WHERE worker_id = $6
          `,
          [
            nextAvailable,
            nextFrozen,
            shouldDelayUnfreeze ? releaseAmount : 0,
            shareReward,
            input.now,
            shareWorkerId,
          ],
        )

        if (releaseAmount > 0) {
          if (shouldDelayUnfreeze) {
            await enqueueDepositUnfreezeWithClient(client, {
              workerId: shareWorkerId,
              workOrderId: input.workOrderId,
              amount: releaseAmount,
              unfreezeDays: input.unfreezeDays,
              now: input.now,
            })
            await client.query(
              `
                INSERT INTO worker_wallet_ledgers (
                  worker_id, ledger_type, amount, balance_after, frozen_after,
                  related_work_order_id, note, payload_json, created_at
                ) VALUES ($1, 'deposit_pending_unfreeze', $2, $3, $4, $5, '拼单验收押金待解冻', $6::jsonb, $7)
              `,
              [
                shareWorkerId,
                releaseAmount,
                nextAvailable,
                nextFrozen,
                input.workOrderId,
                JSON.stringify({
                  workOrderId: input.workOrderId,
                  shareId: Number(share.id),
                  unfreezeDays: input.unfreezeDays,
                }),
                input.now,
              ],
            )
          } else {
            await client.query(
              `
                INSERT INTO worker_wallet_ledgers (
                  worker_id, ledger_type, amount, balance_after, frozen_after,
                  related_work_order_id, note, payload_json, created_at
                ) VALUES ($1, 'deposit_release', $2, $3, $4, $5, '拼单验收释放押金', $6::jsonb, $7)
              `,
              [
                shareWorkerId,
                releaseAmount,
                nextAvailable,
                nextFrozen,
                input.workOrderId,
                JSON.stringify({ workOrderId: input.workOrderId, shareId: Number(share.id) }),
                input.now,
              ],
            )
          }
        }

        if (shareReward > 0) {
          await client.query(
            `
              INSERT INTO worker_wallet_ledgers (
                worker_id, ledger_type, amount, balance_after, frozen_after,
                related_work_order_id, note, payload_json, created_at
              ) VALUES ($1, 'sharing_reward', $2, $3, $4, $5, $6, $7::jsonb, $8)
            `,
            [
              shareWorkerId,
              shareReward,
              nextAvailable,
              nextFrozen,
              input.workOrderId,
              `拼单结算报酬 ${share.quantity} 份`,
              JSON.stringify({
                workOrderId: input.workOrderId,
                shareId: Number(share.id),
                quantity: share.quantity,
              }),
              input.now,
            ],
          )
        }

        await client.query(
          `
            UPDATE work_order_shares
            SET status = 'accepted', accepted_at = $1, updated_at = $1
            WHERE id = $2
          `,
          [input.now, Number(share.id)],
        )
        await maybeUpgradeWorkerLevelWithClient(client, shareWorkerId, input.now)
      }
    }

    const workerId = Number(workOrder.assigned_worker_id || 0)
    if (workerId > 0) {
      await ensureWorkerWalletWithClient(client, workerId, input.now)
      const wallet = await getWorkerWalletWithClient(client, workerId)
      const releaseAmount = Math.min(
        await getOutstandingDepositAmountWithClient(client, workerId, input.workOrderId),
        Number(wallet?.frozen_deposit_amount || 0),
      )
      const rewardAmount = Number(workOrder.reward_amount || 0)
      const shouldDelayUnfreeze = input.unfreezeDays > 0
      const nextAvailable =
        Number(wallet?.available_amount || 0) +
        rewardAmount +
        (shouldDelayUnfreeze ? 0 : releaseAmount)
      const nextFrozen = Math.max(0, Number(wallet?.frozen_deposit_amount || 0) - releaseAmount)

      await client.query(
        `
          UPDATE worker_wallets
          SET
            available_amount = $1,
            frozen_deposit_amount = $2,
            pending_unfreeze_amount = pending_unfreeze_amount + $3,
            total_settled_amount = total_settled_amount + $4,
            updated_at = $5
          WHERE worker_id = $6
        `,
        [
          nextAvailable,
          nextFrozen,
          shouldDelayUnfreeze ? releaseAmount : 0,
          rewardAmount,
          input.now,
          workerId,
        ],
      )

      if (releaseAmount > 0) {
        if (shouldDelayUnfreeze) {
          await enqueueDepositUnfreezeWithClient(client, {
            workerId,
            workOrderId: input.workOrderId,
            amount: releaseAmount,
            unfreezeDays: input.unfreezeDays,
            now: input.now,
          })
          await client.query(
            `
              INSERT INTO worker_wallet_ledgers (
                worker_id, ledger_type, amount, balance_after, frozen_after,
                related_work_order_id, note, payload_json, created_at
              ) VALUES ($1, 'deposit_pending_unfreeze', $2, $3, $4, $5, '验收通过押金待解冻', $6::jsonb, $7)
            `,
            [
              workerId,
              releaseAmount,
              nextAvailable,
              nextFrozen,
              input.workOrderId,
              JSON.stringify({
                workOrderId: input.workOrderId,
                unfreezeDays: input.unfreezeDays,
              }),
              input.now,
            ],
          )
        } else {
          await client.query(
            `
              INSERT INTO worker_wallet_ledgers (
                worker_id, ledger_type, amount, balance_after, frozen_after,
                related_work_order_id, note, payload_json, created_at
              ) VALUES ($1, 'deposit_release', $2, $3, $4, $5, '验收通过释放押金', '{}'::jsonb, $6)
            `,
            [workerId, releaseAmount, nextAvailable, nextFrozen, input.workOrderId, input.now],
          )
        }
      }

      if (rewardAmount > 0) {
        await client.query(
          `
            INSERT INTO worker_wallet_ledgers (
              worker_id, ledger_type, amount, balance_after, frozen_after,
              related_work_order_id, note, payload_json, created_at
            ) VALUES ($1, 'reward_settlement', $2, $3, $4, $5, '验收通过结算报酬', '{}'::jsonb, $6)
          `,
          [workerId, rewardAmount, nextAvailable, nextFrozen, input.workOrderId, input.now],
        )
      }
    }

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'admin',
      actorId: '',
      eventType: 'accepted',
      fromStatus: 'pending_acceptance',
      toStatus: 'accepted',
      payloadJson: JSON.stringify({ shareCount: shares.length }),
      now: input.now,
    })

    if (workerId > 0) {
      await maybeUpgradeWorkerLevelWithClient(client, workerId, input.now)
    }

    return { order: await getWorkOrderByIdWithClient(client, input.workOrderId), failureReason: null }
  })
}

export async function resolveProblemWorkOrder(input: {
  workOrderId: number
  action: ProblemWorkOrderResolutionAction
  note: string
  now: string
}): Promise<WorkOrderRow | null> {
  return withTransaction(async (client) => {
    const currentResult = await client.query<WorkOrderRow>(
      `
        SELECT *
        FROM work_orders
        WHERE id = $1
          AND status = 'problem'
        FOR UPDATE
      `,
      [input.workOrderId],
    )
    const workOrder = currentResult.rows[0] || null
    if (!workOrder) return null

    const shouldReleaseDeposit = ['reopen', 'cancel_release'].includes(input.action)
    const shouldDeductDeposit = input.action === 'cancel_deduct'
    const workerId = Number(workOrder.assigned_worker_id || 0)
    let resolvedDepositAmount = 0

    if (workerId > 0 && (shouldReleaseDeposit || shouldDeductDeposit)) {
      await ensureWorkerWalletWithClient(client, workerId, input.now)
      const wallet = await getWorkerWalletWithClient(client, workerId)
      resolvedDepositAmount = Math.min(
        await getOutstandingDepositAmountWithClient(client, workerId, input.workOrderId),
        Number(wallet?.frozen_deposit_amount || 0),
      )

      if (resolvedDepositAmount > 0) {
        const nextAvailable =
          Number(wallet?.available_amount || 0) + (shouldReleaseDeposit ? resolvedDepositAmount : 0)
        const nextFrozen = Math.max(
          0,
          Number(wallet?.frozen_deposit_amount || 0) - resolvedDepositAmount,
        )
        const ledgerType = shouldReleaseDeposit ? 'deposit_release' : 'deposit_deduction'
        const ledgerAmount = shouldReleaseDeposit ? resolvedDepositAmount : -resolvedDepositAmount
        const ledgerNote = shouldReleaseDeposit ? '问题单处置释放押金' : '问题单处置扣除押金'

        await client.query(
          `
            UPDATE worker_wallets
            SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
            WHERE worker_id = $4
          `,
          [nextAvailable, nextFrozen, input.now, workerId],
        )
        await client.query(
          `
            INSERT INTO worker_wallet_ledgers (
              worker_id, ledger_type, amount, balance_after, frozen_after,
              related_work_order_id, note, payload_json, created_at
            ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9)
          `,
          [
            workerId,
            ledgerType,
            ledgerAmount,
            nextAvailable,
            nextFrozen,
            input.workOrderId,
            ledgerNote,
            JSON.stringify({ action: input.action, note: input.note }),
            input.now,
          ],
        )
      }
    }

    const targetStatus =
      input.action === 'return_to_worker'
        ? 'in_progress'
        : input.action === 'reopen'
          ? 'open'
          : 'cancelled'
    const shouldClearAssignment = input.action === 'reopen'
    const shouldClearSubmission = ['return_to_worker', 'reopen'].includes(input.action)

    await client.query(
      `
        UPDATE work_orders
        SET
          status = $1,
          assigned_worker_id = CASE WHEN $2 THEN NULL ELSE assigned_worker_id END,
          assigned_at = CASE WHEN $2 THEN NULL ELSE assigned_at END,
          acceptance_json = CASE WHEN $3 THEN '{}'::jsonb ELSE acceptance_json END,
          submitted_at = CASE WHEN $3 THEN NULL ELSE submitted_at END,
          problem_note = CASE WHEN $2 THEN '' ELSE problem_note END,
          deadline_at = CASE
            WHEN $1 = 'in_progress' AND timeout_minutes > 0
              THEN NOW() + (timeout_minutes * INTERVAL '1 minute')
            WHEN $1 = 'open' THEN NULL
            ELSE deadline_at
          END,
          updated_at = $4
        WHERE id = $5
      `,
      [targetStatus, shouldClearAssignment, shouldClearSubmission, input.now, input.workOrderId],
    )

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'admin',
      actorId: '',
      eventType: `problem_${input.action}`,
      fromStatus: 'problem',
      toStatus: targetStatus,
      payloadJson: JSON.stringify({
        action: input.action,
        note: input.note,
        depositAmount: resolvedDepositAmount,
      }),
      now: input.now,
    })

    return getWorkOrderByIdWithClient(client, input.workOrderId)
  })
}

export async function listOverdueWorkOrders({
  limit = 50,
  workerId = 0,
}: { limit?: number; workerId?: number } = {}): Promise<WorkOrderRow[]> {
  const params: unknown[] = []
  let workerClause = ''
  if (workerId > 0) {
    params.push(workerId)
    workerClause = `AND wo.assigned_worker_id = $${params.length}`
  }
  params.push(limit)
  const result = await query<WorkOrderRow>(
    `${WORK_ORDER_SELECT}
     WHERE wo.status = 'in_progress'
       AND wo.deadline_at IS NOT NULL
       AND wo.deadline_at < NOW()
       ${workerClause}
     ORDER BY wo.deadline_at ASC
     LIMIT $${params.length}`,
    params,
  )
  return result.rows
}

export async function settleOverdueWorkOrder(input: {
  workOrderId: number
  policy: 'reopen' | 'cancel_release' | 'cancel_deduct'
  now: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason: 'work_order_not_overdue' | 'work_order_has_active_shares' | null
}> {
  return withTransaction(async (client) => {
    const currentResult = await client.query<WorkOrderRow>(
      `
        SELECT *
        FROM work_orders
        WHERE id = $1
          AND status = 'in_progress'
          AND deadline_at IS NOT NULL
          AND deadline_at < NOW()
        FOR UPDATE
      `,
      [input.workOrderId],
    )
    const workOrder = currentResult.rows[0] || null
    if (!workOrder) {
      return { order: null, failureReason: 'work_order_not_overdue' }
    }

    const shareResult = await client.query<{ total: number }>(
      `
        SELECT COUNT(*)::int AS total
        FROM work_order_shares
        WHERE work_order_id = $1 AND status != 'cancelled'
      `,
      [input.workOrderId],
    )
    if (Number(shareResult.rows[0]?.total || 0) > 0) {
      return { order: null, failureReason: 'work_order_has_active_shares' }
    }

    const shouldReleaseDeposit = ['reopen', 'cancel_release'].includes(input.policy)
    const shouldDeductDeposit = input.policy === 'cancel_deduct'
    const workerId = Number(workOrder.assigned_worker_id || 0)
    let resolvedDepositAmount = 0

    if (workerId > 0 && (shouldReleaseDeposit || shouldDeductDeposit)) {
      await ensureWorkerWalletWithClient(client, workerId, input.now)
      const wallet = await getWorkerWalletWithClient(client, workerId)
      resolvedDepositAmount = Math.min(
        await getOutstandingDepositAmountWithClient(client, workerId, input.workOrderId),
        Number(wallet?.frozen_deposit_amount || 0),
      )

      if (resolvedDepositAmount > 0) {
        const nextAvailable =
          Number(wallet?.available_amount || 0) + (shouldReleaseDeposit ? resolvedDepositAmount : 0)
        const nextFrozen = Math.max(
          0,
          Number(wallet?.frozen_deposit_amount || 0) - resolvedDepositAmount,
        )
        const ledgerType = shouldReleaseDeposit ? 'deposit_release' : 'deposit_deduction'
        const ledgerAmount = shouldReleaseDeposit ? resolvedDepositAmount : -resolvedDepositAmount
        const ledgerNote = shouldReleaseDeposit ? '超时处置释放押金' : '超时处置扣除押金'

        await client.query(
          `
            UPDATE worker_wallets
            SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
            WHERE worker_id = $4
          `,
          [nextAvailable, nextFrozen, input.now, workerId],
        )
        await client.query(
          `
            INSERT INTO worker_wallet_ledgers (
              worker_id, ledger_type, amount, balance_after, frozen_after,
              related_work_order_id, note, payload_json, created_at
            ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9)
          `,
          [
            workerId,
            ledgerType,
            ledgerAmount,
            nextAvailable,
            nextFrozen,
            input.workOrderId,
            ledgerNote,
            JSON.stringify({ action: `timeout_${input.policy}` }),
            input.now,
          ],
        )
      }
    }

    const targetStatus = input.policy === 'reopen' ? 'open' : 'cancelled'

    await client.query(
      `
        UPDATE work_orders
        SET
          status = $1,
          assigned_worker_id = NULL,
          assigned_at = NULL,
          deadline_at = NULL,
          acceptance_json = CASE WHEN $1 = 'open' THEN '{}'::jsonb ELSE acceptance_json END,
          submitted_at = CASE WHEN $1 = 'open' THEN NULL ELSE submitted_at END,
          problem_note = CASE WHEN $1 = 'open' THEN '' ELSE problem_note END,
          published_at = CASE WHEN $1 = 'open' THEN $2 ELSE published_at END,
          updated_at = $2
        WHERE id = $3
      `,
      [targetStatus, input.now, input.workOrderId],
    )

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'system',
      actorId: '',
      eventType: `timeout_${input.policy}`,
      fromStatus: 'in_progress',
      toStatus: targetStatus,
      payloadJson: JSON.stringify({
        policy: input.policy,
        depositAmount: resolvedDepositAmount,
        workerId: workerId > 0 ? workerId : null,
      }),
      now: input.now,
    })

    return {
      order: await getWorkOrderByIdWithClient(client, input.workOrderId),
      failureReason: null,
    }
  })
}

export async function sumPendingUnfreezeByOrderIds(
  workOrderIds: number[],
): Promise<Map<number, number>> {
  const totals = new Map<number, number>()
  const uniqueIds = [...new Set(workOrderIds.map(Number).filter((id) => id > 0))]
  if (uniqueIds.length === 0) return totals
  const result = await query<{ work_order_id: number; total: number }>(
    `
      SELECT work_order_id, SUM(amount)::int AS total
      FROM worker_deposit_unfreezes
      WHERE work_order_id = ANY($1::bigint[])
        AND status = 'pending'
      GROUP BY work_order_id
    `,
    [uniqueIds],
  )
  for (const row of result.rows) {
    totals.set(Number(row.work_order_id), Number(row.total || 0))
  }
  return totals
}

export async function deductPendingDepositUnfreeze(input: {
  workOrderId: number
  amount: number
  note: string
  now: string
}): Promise<{
  deductedAmount: number
  failureReason: 'no_pending_deposit' | 'amount_exceeds_pending' | null
}> {
  return withTransaction(async (client) => {
    const pendingResult = await client.query<WorkerDepositUnfreezeRow>(
      `
        SELECT *
        FROM worker_deposit_unfreezes
        WHERE work_order_id = $1 AND status = 'pending'
        ORDER BY unfreeze_at ASC
        FOR UPDATE
      `,
      [input.workOrderId],
    )
    const pending = pendingResult.rows
    if (pending.length === 0) {
      return { deductedAmount: 0, failureReason: 'no_pending_deposit' }
    }
    const totalPending = pending.reduce((sum, row) => sum + Number(row.amount || 0), 0)
    if (input.amount <= 0 || input.amount > totalPending) {
      return { deductedAmount: 0, failureReason: 'amount_exceeds_pending' }
    }

    let remainingToDeduct = input.amount
    let deductedAmount = 0

    for (const row of pending) {
      if (remainingToDeduct <= 0) break
      const rowAmount = Number(row.amount || 0)
      if (rowAmount <= 0) continue
      const deductFromRow = Math.min(rowAmount, remainingToDeduct)
      const isFullyDeducted = deductFromRow >= rowAmount

      if (isFullyDeducted) {
        await client.query(
          `
            UPDATE worker_deposit_unfreezes
            SET status = 'deducted', updated_at = $1
            WHERE id = $2
          `,
          [input.now, row.id],
        )
      } else {
        await client.query(
          `
            UPDATE worker_deposit_unfreezes
            SET amount = amount - $1, updated_at = $2
            WHERE id = $3
          `,
          [deductFromRow, input.now, row.id],
        )
      }

      const workerWallet = await getWorkerWalletWithClient(client, row.worker_id)
      await client.query(
        `
          UPDATE worker_wallets
          SET pending_unfreeze_amount = GREATEST(0, pending_unfreeze_amount - $1), updated_at = $2
          WHERE worker_id = $3
        `,
        [deductFromRow, input.now, row.worker_id],
      )
      await client.query(
        `
          INSERT INTO worker_wallet_ledgers (
            worker_id, ledger_type, amount, balance_after, frozen_after,
            related_work_order_id, note, payload_json, created_at
          ) VALUES ($1, 'deposit_deduction', $2, $3, $4, $5, '问题单扣减待解冻押金', $6::jsonb, $7)
        `,
        [
          row.worker_id,
          -deductFromRow,
          Number(workerWallet?.available_amount || 0),
          Number(workerWallet?.frozen_deposit_amount || 0),
          input.workOrderId,
          JSON.stringify({
            workOrderId: input.workOrderId,
            unfreezeId: row.id,
            note: input.note,
          }),
          input.now,
        ],
      )
      deductedAmount += deductFromRow
      remainingToDeduct -= deductFromRow
    }

    return { deductedAmount, failureReason: null }
  })
}

export async function listDueDepositUnfreezes({
  limit = 100,
  workerId = 0,
}: { limit?: number; workerId?: number } = {}): Promise<WorkerDepositUnfreezeRow[]> {
  const params: unknown[] = []
  let workerClause = ''
  if (workerId > 0) {
    params.push(workerId)
    workerClause = `AND worker_id = $${params.length}`
  }
  params.push(limit)
  const result = await query<WorkerDepositUnfreezeRow>(
    `
      SELECT *
      FROM worker_deposit_unfreezes
      WHERE status = 'pending'
        AND unfreeze_at <= NOW()
        ${workerClause}
      ORDER BY unfreeze_at ASC
      LIMIT $${params.length}
    `,
    params,
  )
  return result.rows
}

export async function releaseDepositUnfreeze({
  unfreezeId,
  now,
}: { unfreezeId: number; now: string }): Promise<WorkerDepositUnfreezeRow | null> {
  return withTransaction(async (client) => {
    const currentResult = await client.query<WorkerDepositUnfreezeRow>(
      `
        SELECT *
        FROM worker_deposit_unfreezes
        WHERE id = $1 AND status = 'pending'
        FOR UPDATE
      `,
      [unfreezeId],
    )
    const unfreeze = currentResult.rows[0] || null
    if (!unfreeze) return null

    await ensureWorkerWalletWithClient(client, unfreeze.worker_id, now)
    const wallet = await getWorkerWalletWithClient(client, unfreeze.worker_id)
    const amount = Math.min(
      Number(unfreeze.amount || 0),
      Number(wallet?.pending_unfreeze_amount || 0),
    )

    await client.query(
      `
        UPDATE worker_deposit_unfreezes
        SET status = 'released', released_at = $1, updated_at = $1
        WHERE id = $2
      `,
      [now, unfreeze.id],
    )

    if (amount > 0) {
      const nextAvailable = Number(wallet?.available_amount || 0) + amount
      const nextPending = Math.max(
        0,
        Number(wallet?.pending_unfreeze_amount || 0) - amount,
      )
      await client.query(
        `
          UPDATE worker_wallets
          SET available_amount = $1, pending_unfreeze_amount = $2, updated_at = $3
          WHERE worker_id = $4
        `,
        [nextAvailable, nextPending, now, unfreeze.worker_id],
      )
      await client.query(
        `
          INSERT INTO worker_wallet_ledgers (
            worker_id, ledger_type, amount, balance_after, frozen_after,
            related_work_order_id, note, payload_json, created_at
          ) VALUES ($1, 'deposit_unfreeze', $2, $3, $4, $5, '押金已解冻到账', $6::jsonb, $7)
        `,
        [
          unfreeze.worker_id,
          amount,
          nextAvailable,
          Number(wallet?.frozen_deposit_amount || 0),
          unfreeze.work_order_id,
          JSON.stringify({ unfreezeId: unfreeze.id }),
          now,
        ],
      )
    }

    return {
      ...unfreeze,
      status: 'released',
      released_at: now,
      updated_at: now,
    }
  })
}

async function enqueueDepositUnfreezeWithClient(
  client: PoolClient,
  input: { workerId: number; workOrderId: number; amount: number; unfreezeDays: number; now: string },
) {
  if (input.amount <= 0) return
  const unfreezeAt = new Date(
    new Date(input.now).getTime() + input.unfreezeDays * 86_400_000,
  ).toISOString()
  await client.query(
    `
      INSERT INTO worker_deposit_unfreezes (
        worker_id, work_order_id, amount, unfreeze_at, status, created_at, updated_at
      ) VALUES ($1, $2, $3, $4, 'pending', $5, $5)
    `,
    [input.workerId, input.workOrderId, input.amount, unfreezeAt, input.now],
  )
}

export async function countWorkerActiveOrders(workerId: number | string): Promise<number> {
  const result = await query<{ total: number }>(
    `
      SELECT COUNT(*)::int AS total
      FROM work_orders
      WHERE assigned_worker_id = $1
        AND status IN ('in_progress', 'pending_acceptance', 'problem')
    `,
    [Number(workerId)],
  )
  return Number(result.rows[0]?.total || 0)
}

export async function countTimeoutEventsByWorkerIds(workerIds: number[]): Promise<Map<number, number>> {
  const counts = new Map<number, number>()
  const uniqueIds = [...new Set(workerIds.map((id) => Number(id)).filter((id) => id > 0))]
  if (uniqueIds.length === 0) return counts
  const result = await query<{ worker_id: string; total: number }>(
    `
      SELECT payload_json->>'workerId' AS worker_id, COUNT(*)::int AS total
      FROM work_order_events
      WHERE event_type LIKE 'timeout_%'
        AND payload_json->>'workerId' IS NOT NULL
        AND payload_json->>'workerId' != ''
        AND payload_json->>'workerId' = ANY($1::text[])
      GROUP BY payload_json->>'workerId'
    `,
    [uniqueIds.map(String)],
  )
  for (const row of result.rows) {
    const workerId = Number(row.worker_id)
    if (Number.isFinite(workerId) && workerId > 0) {
      counts.set(workerId, Number(row.total || 0))
    }
  }
  return counts
}

export async function countWorkerTimeoutEvents(workerId: number | string): Promise<number> {
  const counts = await countTimeoutEventsByWorkerIds([Number(workerId)])
  return counts.get(Number(workerId)) || 0
}

export async function createWorkOrderEvent(input: {
  workOrderId: number
  actorType: string
  actorId: string
  eventType: string
  fromStatus?: string
  toStatus?: string
  payloadJson?: string
  now: string
}): Promise<void> {
  await query(
    `
      INSERT INTO work_order_events (
        work_order_id, actor_type, actor_id, event_type, from_status, to_status, payload_json, created_at
      ) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
    `,
    [
      input.workOrderId,
      input.actorType,
      input.actorId,
      input.eventType,
      input.fromStatus || '',
      input.toStatus || '',
      input.payloadJson || '{}',
      input.now,
    ],
  )
}

async function createWorkOrderEventWithClient(
  client: PoolClient,
  input: Parameters<typeof createWorkOrderEvent>[0],
) {
  await client.query(
    `
      INSERT INTO work_order_events (
        work_order_id, actor_type, actor_id, event_type, from_status, to_status, payload_json, created_at
      ) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8)
    `,
    [
      input.workOrderId,
      input.actorType,
      input.actorId,
      input.eventType,
      input.fromStatus || '',
      input.toStatus || '',
      input.payloadJson || '{}',
      input.now,
    ],
  )
}

async function getOutstandingDepositAmountWithClient(
  client: PoolClient,
  workerId: number,
  workOrderId: number,
) {
  const result = await client.query<{
    frozen_amount: number
    released_amount: number
    deducted_amount: number
  }>(
    `
      SELECT
        COALESCE(SUM(CASE WHEN ledger_type = 'deposit_freeze' THEN ABS(amount) ELSE 0 END), 0)::int AS frozen_amount,
        COALESCE(SUM(CASE WHEN ledger_type = 'deposit_release' THEN ABS(amount) ELSE 0 END), 0)::int AS released_amount,
        COALESCE(SUM(CASE WHEN ledger_type = 'deposit_deduction' THEN ABS(amount) ELSE 0 END), 0)::int AS deducted_amount
      FROM worker_wallet_ledgers
      WHERE worker_id = $1
        AND related_work_order_id = $2
    `,
    [workerId, workOrderId],
  )
  return resolveOutstandingDepositAmount(result.rows[0])
}

async function countWorkerActiveOrdersWithClient(client: PoolClient, workerId: number): Promise<number> {
  const result = await client.query<{ total: number }>(
    `
      SELECT COUNT(*)::int AS total
      FROM work_orders
      WHERE assigned_worker_id = $1
        AND status IN ('in_progress', 'pending_acceptance', 'problem')
    `,
    [workerId],
  )
  return Number(result.rows[0]?.total || 0)
}

export function resolveOutstandingDepositAmount(
  totals:
    | {
        frozen_amount?: number
        released_amount?: number
        deducted_amount?: number
      }
    | null
    | undefined,
) {
  return Math.max(
    0,
    Number(totals?.frozen_amount || 0) -
      Number(totals?.released_amount || 0) -
      Number(totals?.deducted_amount || 0),
  )
}

async function getWorkOrderByIdWithClient(
  client: PoolClient,
  workOrderId: number | string,
): Promise<WorkOrderRow | null> {
  const result = await client.query<WorkOrderRow>(`${WORK_ORDER_SELECT} WHERE wo.id = $1 LIMIT 1`, [
    Number(workOrderId),
  ])
  return result.rows[0] || null
}

function buildWorkOrderWhere({
  status = '',
  keyword = '',
  workerId = 0,
  categoryId = 0,
  workerSharingId = 0,
  visibleAfterIso = '',
}: ListInput) {
  const filters: string[] = []
  const params: unknown[] = []
  if (status) {
    params.push(status)
    filters.push(`wo.status = $${params.length}`)
  }
  if (keyword) {
    params.push(`%${keyword}%`)
    filters.push(
      `(wo.work_order_no ILIKE $${params.length} OR wo.platform_order_id ILIKE $${params.length} OR wo.product_name ILIKE $${params.length})`,
    )
  }
  if (workerId) {
    params.push(workerId)
    filters.push(`wo.assigned_worker_id = $${params.length}`)
  }
  if (categoryId) {
    params.push(categoryId)
    filters.push(`wo.category_id = $${params.length}`)
  }
  if (workerSharingId) {
    params.push(workerSharingId)
    filters.push(
      `(wo.assigned_worker_id = $${params.length}
        OR wo.last_assigned_worker_id = $${params.length}
        OR wo.id IN (
          SELECT work_order_id FROM work_order_shares
          WHERE worker_id = $${params.length} AND status != 'cancelled'
        ))`,
    )
  }
  if (visibleAfterIso) {
    params.push(visibleAfterIso)
    filters.push(`(wo.published_at IS NULL OR wo.published_at <= $${params.length})`)
  }
  return {
    whereClause: filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '',
    params,
  }
}

function buildWorkProductRuleWhere({ enabled = null, keyword = '' }: ProductRuleListInput) {
  const filters: string[] = []
  const params: unknown[] = []
  if (enabled !== null && enabled !== undefined) {
    params.push(Boolean(enabled))
    filters.push(`wpr.enabled = $${params.length}`)
  }
  if (keyword) {
    params.push(`%${keyword}%`)
    filters.push(`(
      wpr.rule_key ILIKE $${params.length}
      OR wpr.sku_code ILIKE $${params.length}
      OR wpr.product_name ILIKE $${params.length}
    )`)
  }
  return {
    whereClause: filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '',
    params,
  }
}
