import { withTransaction } from '../../db/client.js'
import { ensureWorkerWalletWithClient, getWorkerWalletWithClient } from './shared.js'
import { createWorkOrderEventWithClient } from './work-order-event-repo.js'
import {
  countWorkerActiveOrdersWithClient,
  getWorkOrderByIdWithClient,
  isWorkOrderWithinHallCapacityWithClient,
} from './work-order-query-repo.js'
import type { GrabWorkOrderResult, WorkOrderRow } from './types.js'

export async function grabWorkOrder(input: {
  workOrderId: number
  workerId: number
  depositAmount: number
  maxActiveOrders: number
  hallCandidateLimit?: number
  hallCategoryLimits?: Record<string, number>
  visibleAfterIso?: string
  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',
      }
    }

    if (
      input.hallCandidateLimit &&
      !(await isWorkOrderWithinHallCapacityWithClient(client, {
        workOrderId: input.workOrderId,
        hallCandidateLimit: input.hallCandidateLimit,
        ...(input.hallCategoryLimits ? { hallCategoryLimits: input.hallCategoryLimits } : {}),
        visibleAfterIso: input.visibleAfterIso,
      }))
    ) {
      return {
        order: null,
        failureReason: 'work_order_not_open',
      }
    }

    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,
          gift_phase = CASE WHEN acceptance_mode = 'friend_gift' THEN 'material_required' ELSE '' END,
          booster_material_json = '{}'::jsonb,
          friend_added_at = NULL,
          gift_available_at = NULL,
          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 assignWorkOrderToWorker(input: {
  workOrderId: number
  workerId: number
  depositAmount: number
  maxActiveOrders: number
  deadlineAt: string | null
  now: string
  /** 操作人显示名（管理员用户名等） */
  actorName?: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason:
    | 'work_order_not_assignable'
    | 'worker_active_order_limit'
    | 'worker_deposit_insufficient'
    | 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 ||
      !['unassigned', 'open'].includes(workOrder.status) ||
      workOrder.assigned_worker_id
    ) {
      return { order: null, failureReason: 'work_order_not_assignable' }
    }
    const fromStatus = workOrder.status

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

    await client.query(
      `
        UPDATE work_orders
        SET
          status = 'in_progress',
          assigned_worker_id = $1,
          last_assigned_worker_id = $1,
          assigned_at = $2,
          deadline_at = $3,
          gift_phase = CASE WHEN acceptance_mode = 'friend_gift' THEN 'material_required' ELSE '' END,
          booster_material_json = '{}'::jsonb,
          friend_added_at = NULL,
          gift_available_at = NULL,
          updated_at = $2
        WHERE id = $4
      `,
      [input.workerId, input.now, input.deadlineAt, input.workOrderId],
    )

    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: 'admin',
      actorId: input.actorName || '',
      eventType: 'assigned_by_admin',
      fromStatus,
      toStatus: 'in_progress',
      payloadJson: JSON.stringify({ workerId: input.workerId, depositAmount: input.depositAmount }),
      now: input.now,
    })

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