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

async function getWorkOrderShareById(shareId: number): Promise<WorkOrderShareRow | null> {
  const result = await query<WorkOrderShareRow>(
    `${WORK_ORDER_SHARE_SELECT} WHERE wos.id = $1 LIMIT 1`,
    [shareId],
  )
  return result.rows[0] || null
}

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,
  workOrderIds: number[],
): Promise<WorkOrderShareRow[]> {
  if (workOrderIds.length === 0) return []
  const result = await query<WorkOrderShareRow>(
    `${WORK_ORDER_SHARE_SELECT}
     WHERE wos.worker_id = $1
       AND wos.work_order_id = ANY($2::bigint[])
       AND wos.status != 'cancelled'
     ORDER BY wos.id DESC`,
    [Number(workerId), workOrderIds],
  )
  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 AND wos.status != 'cancelled' LIMIT 1`,
    [Number(workOrderId), Number(workerId)],
  )
  return result.rows[0] || null
}

/** 普通拼单使用指定份数，立即抢单则在事务中承接全部剩余份数。 */
export function resolveWorkOrderShareJoinQuantity(input: {
  quantity: number
  remaining: number
  takeRemaining?: boolean
}): number {
  return input.takeRemaining ? Math.max(0, input.remaining) : input.quantity
}

/**
 * 计算打手当前累计拼单份额实际需要冻结的押金。
 * 等级与个人叠加后的免押额度针对该打手在本工单的累计份额生效；已经冻结的金额不自动回退。
 */
export function resolveWorkOrderShareDepositAmount(input: {
  requiredDepositAmount: number
  totalQuantity: number
  nextQuantity: number
  depositFreeAmount?: number
  currentFrozenDeposit?: number
}) {
  const totalQuantity = Math.max(1, Number(input.totalQuantity || 0))
  const nextRequiredDeposit = Math.round(
    (Math.max(0, Number(input.requiredDepositAmount || 0)) *
      Math.max(0, Number(input.nextQuantity || 0))) /
      totalQuantity,
  )
  const requiredShareDeposit = Math.max(
    0,
    nextRequiredDeposit - Math.max(0, Number(input.depositFreeAmount || 0)),
  )
  return Math.max(Math.max(0, Number(input.currentFrozenDeposit || 0)), requiredShareDeposit)
}

export async function joinWorkOrderShare(input: {
  workOrderId: number
  workerId: number
  quantity: number
  /** 立即抢单时在事务锁内承接全部剩余份数。 */
  takeRemaining?: boolean
  /** 当前打手的有效免押额度（分），按其本次累计拼单份额抵扣。 */
  depositFreeAmount?: number
  maxActiveOrders: number
  hallCandidateLimit?: number
  hallCategoryLimits?: Record<string, number>
  visibleAfterIso?: string
  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 (
      input.hallCandidateLimit &&
      !(await isWorkOrderWithinHallCapacityWithClient(client, {
        workOrderId: input.workOrderId,
        hallCandidateLimit: input.hallCandidateLimit,
        ...(input.hallCategoryLimits ? { hallCategoryLimits: input.hallCategoryLimits } : {}),
        visibleAfterIso: input.visibleAfterIso,
      }))
    ) {
      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)
    const requestedQuantity = resolveWorkOrderShareJoinQuantity({
      quantity: input.quantity,
      remaining,
      takeRemaining: input.takeRemaining === true,
    })
    if (requestedQuantity <= 0 || requestedQuantity > remaining) {
      return { share: null, failureReason: 'sharing_quantity_full' }
    }

    const existingShareResult = await client.query<WorkOrderShareRow>(
      `SELECT *
       FROM work_order_shares
       WHERE work_order_id = $1 AND worker_id = $2
       FOR UPDATE`,
      [input.workOrderId, input.workerId],
    )
    const existingShare = existingShareResult.rows[0] || null
    if (existingShare && (!input.takeRemaining || existingShare.status !== 'joined')) {
      return { share: null, failureReason: 'sharing_quantity_full' }
    }
    if (!existingShare) {
      const activeOrderCount = await countWorkerActiveOrdersWithClient(client, input.workerId)
      if (activeOrderCount >= input.maxActiveOrders) {
        return { share: null, failureReason: 'worker_active_order_limit' }
      }
    }

    const unitReward = toPositiveInteger(workOrder.sharing_unit_reward, 0)
    const nextQuantity = Number(existingShare?.quantity || 0) + requestedQuantity
    const shareReward = nextQuantity * unitReward
    const nextShareDeposit = resolveWorkOrderShareDepositAmount({
      requiredDepositAmount: Number(workOrder.required_deposit_amount || 0),
      totalQuantity,
      nextQuantity,
      ...(input.depositFreeAmount === undefined
        ? {}
        : { depositFreeAmount: input.depositFreeAmount }),
      currentFrozenDeposit: Number(existingShare?.share_deposit || 0),
    })
    const depositToFreeze = Math.max(
      0,
      nextShareDeposit - Number(existingShare?.share_deposit || 0),
    )

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

    const shareResult = existingShare
      ? await client.query<{ id: number }>(
          `
            UPDATE work_order_shares
            SET
              quantity = $1,
              unit_reward = $2,
              share_reward = $3,
              share_deposit = $4,
              updated_at = $5
            WHERE id = $6
            RETURNING id
          `,
          [nextQuantity, unitReward, shareReward, nextShareDeposit, input.now, existingShare.id],
        )
      : 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,
            requestedQuantity,
            unitReward,
            shareReward,
            nextShareDeposit,
            input.now,
          ],
        )

    if (depositToFreeze > 0) {
      const nextAvailable = available - depositToFreeze
      const nextFrozen = Number(wallet?.frozen_deposit_amount || 0) + depositToFreeze
      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,
          -depositToFreeze,
          nextAvailable,
          nextFrozen,
          input.workOrderId,
          `拼单冻结押金 ${requestedQuantity} 份`,
          JSON.stringify({ workOrderId: input.workOrderId, quantity: requestedQuantity }),
          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: requestedQuantity,
        takeRemaining: input.takeRemaining === true,
      }),
      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' | 'work_order_status_invalid' | null
}> {
  return withTransaction(async (client) => {
    const orderResult = await client.query<{
      status: string
      sharing_total_quantity: number
    }>(
      `SELECT status, sharing_total_quantity
       FROM work_orders
       WHERE id = $1
       FOR UPDATE`,
      [input.workOrderId],
    )
    const workOrder = orderResult.rows[0] || null
    if (!workOrder) {
      return { share: null, failureReason: 'share_not_found' }
    }
    if (!['open', 'pending_acceptance'].includes(workOrder.status)) {
      return { share: null, failureReason: 'work_order_status_invalid' }
    }

    const lockResult = await client.query<{ id: number }>(
      `
        SELECT id
        FROM work_order_shares
        WHERE work_order_id = $1 AND worker_id = $2
        FOR UPDATE
      `,
      [input.workOrderId, input.workerId],
    )
    const shareId = lockResult.rows[0]?.id || 0
    if (!shareId) {
      return { share: null, failureReason: 'share_not_found' }
    }
    const shareResult = await client.query<WorkOrderShareRow>(
      `${WORK_ORDER_SHARE_SELECT} WHERE wos.id = $1 LIMIT 1`,
      [shareId],
    )
    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, draft_acceptance_json = '{}'::jsonb, submitted_at = $2, updated_at = $2
        WHERE id = $3
      `,
      [input.acceptanceJson, input.now, Number(share.id)],
    )

    const progressResult = await client.query<{
      joined_quantity: number
      pending_submission_count: number
    }>(
      `SELECT
         COALESCE(SUM(quantity), 0)::int AS joined_quantity,
         COUNT(*) FILTER (WHERE status = 'joined')::int AS pending_submission_count
       FROM work_order_shares
       WHERE work_order_id = $1
         AND status != 'cancelled'`,
      [input.workOrderId],
    )
    const progress = progressResult.rows[0]
    const allQuantityAllocated =
      Number(progress?.joined_quantity || 0) >=
      toPositiveInteger(workOrder.sharing_total_quantity, 1)
    if (allQuantityAllocated && Number(progress?.pending_submission_count || 0) === 0) {
      await client.query(
        `UPDATE work_orders
         SET
           status = 'pending_acceptance',
           submitted_at = COALESCE(submitted_at, $1),
           updated_at = $1
         WHERE id = $2
           AND status = 'open'`,
        [input.now, input.workOrderId],
      )
    }

    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 updateWorkOrderDraftAcceptance(input: {
  workOrderId: number
  draftJson: string
  now: string
}): Promise<WorkOrderRow | null> {
  await query(
    `
      UPDATE work_orders
      SET draft_acceptance_json = $1::jsonb, updated_at = $2
      WHERE id = $3
    `,
    [input.draftJson, input.now, Number(input.workOrderId)],
  )
  return getWorkOrderById(input.workOrderId)
}

/** 更新整单已提交的验收资料（保留原 submitted_at，用于提交验收后补充/修改图片） */
export async function updateWorkOrderAcceptanceRecord(input: {
  workOrderId: number
  acceptanceJson: string
  now: string
}): Promise<WorkOrderRow | null> {
  await query(
    `
      UPDATE work_orders
      SET acceptance_json = $1::jsonb, updated_at = $2
      WHERE id = $3
    `,
    [input.acceptanceJson, input.now, Number(input.workOrderId)],
  )
  return getWorkOrderById(input.workOrderId)
}

/** 保存拼单份额的验收草稿（暂存图片/说明），不改变份额状态 */
export async function updateWorkOrderShareDraftAcceptance(input: {
  workOrderId: number
  workerId: number
  draftJson: string
  now: string
}): Promise<WorkOrderShareRow | null> {
  const result = await query<WorkOrderShareRow>(
    `${WORK_ORDER_SHARE_SELECT}
     WHERE wos.work_order_id = $1 AND wos.worker_id = $2`,
    [Number(input.workOrderId), Number(input.workerId)],
  )
  const share = result.rows[0] || null
  if (!share) return null
  await query(
    `
      UPDATE work_order_shares
      SET draft_acceptance_json = $1::jsonb, updated_at = $2
      WHERE id = $3
    `,
    [input.draftJson, input.now, Number(share.id)],
  )
  return getWorkOrderShareById(share.id)
}

/** 更新拼单份额已提交的验收资料（保留原 submitted_at，用于提交验收后补充/修改图片） */
export async function updateWorkOrderShareAcceptanceRecord(input: {
  workOrderId: number
  workerId: number
  acceptanceJson: string
  now: string
}): Promise<WorkOrderShareRow | null> {
  const result = await query<WorkOrderShareRow>(
    `${WORK_ORDER_SHARE_SELECT}
     WHERE wos.work_order_id = $1 AND wos.worker_id = $2`,
    [Number(input.workOrderId), Number(input.workerId)],
  )
  const share = result.rows[0] || null
  if (!share) return null
  await query(
    `
      UPDATE work_order_shares
      SET acceptance_json = $1::jsonb, updated_at = $2
      WHERE id = $3
    `,
    [input.acceptanceJson, input.now, Number(share.id)],
  )
  return getWorkOrderShareById(share.id)
}
