import { withTransaction } from '../../db/client.js'
import { isWorkOrderGiftDepositForfeitable } from '../../domain/work-order-acceptance-mode.js'
import { ensureWorkerWalletWithClient, getWorkerWalletWithClient } from './shared.js'
import { createWorkOrderEventWithClient } from './work-order-event-repo.js'
import { getWorkOrderByIdWithClient } from './work-order-query-repo.js'
import { getOutstandingDepositAmountWithClient } from './work-order-deposit-repo.js'
import { RESET_GIFT_FLOW_SQL } from './work-order-gift-repo.js'
import type { WorkOrderRow } from './types.js'

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,
          hall_queued_at = $1,
          ${RESET_GIFT_FLOW_SQL},
          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 unassignWorkOrder(input: {
  workOrderId: number
  now: string
  actorName?: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason: 'work_order_not_in_progress' | 'work_order_no_worker' | 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' }
    }
    const workerId = Number(workOrder.assigned_worker_id || 0)
    if (!workerId) {
      return { order: null, failureReason: 'work_order_no_worker' }
    }

    const forfeitGiftDeposit = isWorkOrderGiftDepositForfeitable(workOrder)
    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),
    )

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

    if (releaseAmount > 0) {
      const nextAvailable =
        Number(wallet?.available_amount || 0) + (forfeitGiftDeposit ? 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, 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,
          forfeitGiftDeposit ? 'deposit_deduction' : 'deposit_release',
          forfeitGiftDeposit ? -releaseAmount : releaseAmount,
          nextAvailable,
          nextFrozen,
          input.workOrderId,
          forfeitGiftDeposit ? '后台取消指派扣除押金（已确认加好友）' : '后台取消指派退还押金',
          JSON.stringify(forfeitGiftDeposit ? { giftDepositForfeited: true } : {}),
          input.now,
        ],
      )
    }

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'admin',
      actorId: input.actorName || '',
      eventType: 'unassigned_by_admin',
      fromStatus: 'in_progress',
      toStatus: 'unassigned',
      payloadJson: JSON.stringify({
        workerId,
        releaseAmount: forfeitGiftDeposit ? 0 : releaseAmount,
        forfeitedAmount: forfeitGiftDeposit ? releaseAmount : 0,
      }),
      now: input.now,
    })

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

export async function returnAssignedWorkOrderToHall(input: {
  workOrderId: number
  now: string
  reason?: string
  actorName?: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason: 'work_order_not_in_progress' | 'work_order_no_worker' | 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' }
    }
    const workerId = Number(workOrder.assigned_worker_id || 0)
    if (!workerId) {
      return { order: null, failureReason: 'work_order_no_worker' }
    }

    const forfeitGiftDeposit = isWorkOrderGiftDepositForfeitable(workOrder)
    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),
    )

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

    if (releaseAmount > 0) {
      const nextAvailable =
        Number(wallet?.available_amount || 0) + (forfeitGiftDeposit ? 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, 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,
          forfeitGiftDeposit ? 'deposit_deduction' : 'deposit_release',
          forfeitGiftDeposit ? -releaseAmount : releaseAmount,
          nextAvailable,
          nextFrozen,
          input.workOrderId,
          forfeitGiftDeposit ? '后台退回大厅扣除押金（已确认加好友）' : '后台退回大厅退还押金',
          JSON.stringify(forfeitGiftDeposit ? { giftDepositForfeited: true } : {}),
          input.now,
        ],
      )
    }

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'admin',
      actorId: input.actorName || '',
      eventType: 'returned_to_hall_by_admin',
      fromStatus: 'in_progress',
      toStatus: 'open',
      payloadJson: JSON.stringify({
        workerId,
        releaseAmount: forfeitGiftDeposit ? 0 : releaseAmount,
        forfeitedAmount: forfeitGiftDeposit ? releaseAmount : 0,
        reason: String(input.reason || '').trim(),
      }),
      now: input.now,
    })

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

export async function cancelAssignedWorkOrder(input: {
  workOrderId: number
  now: string
  reason?: string
  actorName?: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason: 'work_order_not_in_progress' | 'work_order_no_worker' | 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' }
    }
    const workerId = Number(workOrder.assigned_worker_id || 0)
    if (!workerId) {
      return { order: null, failureReason: 'work_order_no_worker' }
    }

    const forfeitGiftDeposit = isWorkOrderGiftDepositForfeitable(workOrder)
    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),
    )

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

    if (releaseAmount > 0) {
      const nextAvailable =
        Number(wallet?.available_amount || 0) + (forfeitGiftDeposit ? 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, 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,
          forfeitGiftDeposit ? 'deposit_deduction' : 'deposit_release',
          forfeitGiftDeposit ? -releaseAmount : releaseAmount,
          nextAvailable,
          nextFrozen,
          input.workOrderId,
          forfeitGiftDeposit ? '后台撤单扣除押金（已确认加好友）' : '后台撤单退还押金',
          JSON.stringify(forfeitGiftDeposit ? { giftDepositForfeited: true } : {}),
          input.now,
        ],
      )
    }

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'admin',
      actorId: input.actorName || '',
      eventType: 'cancelled_by_admin',
      fromStatus: 'in_progress',
      toStatus: 'cancelled',
      payloadJson: JSON.stringify({
        workerId,
        releaseAmount: forfeitGiftDeposit ? 0 : releaseAmount,
        forfeitedAmount: forfeitGiftDeposit ? releaseAmount : 0,
        reason: String(input.reason || '').trim(),
      }),
      now: input.now,
    })

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

/** 将已取消工单恢复为未分配状态，不变更历史押金流水。 */
export async function reopenCancelledWorkOrder(input: {
  workOrderId: number
  now: string
  actorName?: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason: 'work_order_not_cancelled' | 'work_order_has_active_shares' | 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 !== 'cancelled') {
      return { order: null, failureReason: 'work_order_not_cancelled' }
    }

    const activeShareResult = 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(activeShareResult.rows[0]?.total || 0) > 0) {
      return { order: null, failureReason: 'work_order_has_active_shares' }
    }

    await client.query(
      `
        UPDATE work_orders
        SET
          status = 'unassigned',
          assigned_worker_id = NULL,
          assigned_at = NULL,
          deadline_at = NULL,
          acceptance_json = '{}'::jsonb,
          draft_acceptance_json = '{}'::jsonb,
          submitted_at = NULL,
          accepted_at = NULL,
          problem_note = '',
          published_at = NULL,
          pinned_at = NULL,
          ${RESET_GIFT_FLOW_SQL},
          updated_at = $1
        WHERE id = $2
      `,
      [input.now, input.workOrderId],
    )

    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'admin',
      actorId: input.actorName || '',
      eventType: 'reopened_from_cancelled',
      fromStatus: 'cancelled',
      toStatus: 'unassigned',
      payloadJson: JSON.stringify({
        previousAssignedWorkerId: Number(workOrder.last_assigned_worker_id || 0) || null,
      }),
      now: input.now,
    })

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