import { query, withTransaction } from '../../db/client.js'
import { toJsonString, toPositiveInteger } from './shared.js'
import { getWorkOrderById, getWorkOrderByIdWithClient } from './work-order-query-repo.js'
import { createWorkOrderEventWithClient } from './work-order-event-repo.js'
import {
  normalizeWorkOrderAcceptanceMode,
  normalizeWorkOrderGiftCooldownHours,
  normalizeWorkOrderGiftCooldownMinutes,
  WORK_ORDER_ACCEPTANCE_MODE,
  WORK_ORDER_GIFT_PHASE,
} from '../../domain/work-order-acceptance-mode.js'
import type { CreateWorkOrderInput, WorkOrderRow } from './types.js'

export async function createWorkOrder(input: CreateWorkOrderInput): Promise<WorkOrderRow | null> {
  const acceptanceMode = normalizeWorkOrderAcceptanceMode(input.acceptanceMode)
  const isFriendGift = acceptanceMode === WORK_ORDER_ACCEPTANCE_MODE.FRIEND_GIFT
  const result = await query<{ id: number }>(
    `
      INSERT INTO work_orders (
        work_order_no, product_rule_id, 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,
        acceptance_mode, gift_phase, gift_cooldown_hours, gift_cooldown_minutes,
        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,
        $18, $19, $20, $21,
        $22::jsonb, $23::jsonb, $24, $25
      )
      RETURNING id
    `,
    [
      input.workOrderNo,
      input.productRuleId || null,
      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',
      acceptanceMode,
      isFriendGift ? WORK_ORDER_GIFT_PHASE.MATERIAL_REQUIRED : '',
      normalizeWorkOrderGiftCooldownHours(input.giftCooldownHours),
      normalizeWorkOrderGiftCooldownMinutes(input.giftCooldownMinutes),
      input.materialJson,
      input.requirementJson,
      input.now,
      input.now,
    ],
  )
  return getWorkOrderById(result.rows[0]?.id || 0)
}

export async function updateWorkOrder(
  workOrderId: number | string,
  patch: Partial<
    Pick<
      WorkOrderRow,
      | 'status'
      | 'assigned_worker_id'
      | 'material_json'
      | 'acceptance_json'
      | 'draft_acceptance_json'
      | 'problem_note'
      | 'reward_amount'
      | 'sharing_enabled'
      | 'sharing_total_quantity'
      | 'sharing_unit_reward'
      | 'published_at'
      | 'hall_queued_at'
      | 'pinned_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, draft_acceptance_json = $5::jsonb,
        problem_note = $6, reward_amount = $7, sharing_enabled = $8,
        sharing_total_quantity = $9, sharing_unit_reward = $10, published_at = $11,
        hall_queued_at = $12, pinned_at = $13, assigned_at = $14, submitted_at = $15,
        accepted_at = $16, updated_at = $17
      WHERE id = $18
    `,
    [
      next.status,
      next.assigned_worker_id || null,
      toJsonString(next.material_json),
      toJsonString(next.acceptance_json),
      toJsonString(next.draft_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.hall_queued_at || null,
      next.pinned_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 publishUnassignedWorkOrder(input: {
  workOrderId: number | string
  now: string
  actorName?: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason: 'work_order_not_publishable' | null
}> {
  return withTransaction(async (client) => {
    const currentResult = await client.query<WorkOrderRow>(
      `
        SELECT *
        FROM work_orders
        WHERE id = $1
        FOR UPDATE
      `,
      [Number(input.workOrderId)],
    )
    const current = currentResult.rows[0] || null
    if (!current || current.status !== 'unassigned' || current.assigned_worker_id) {
      return { order: null, failureReason: 'work_order_not_publishable' }
    }

    const updatedResult = await client.query<{ id: number }>(
      `
        UPDATE work_orders
        SET status = 'open', published_at = $1, hall_queued_at = $1, updated_at = $1
        WHERE id = $2
          AND status = 'unassigned'
          AND assigned_worker_id IS NULL
          AND reward_amount > 0
        RETURNING id
      `,
      [input.now, Number(input.workOrderId)],
    )
    if (!updatedResult.rows[0]) {
      return { order: null, failureReason: 'work_order_not_publishable' }
    }

    await createWorkOrderEventWithClient(client, {
      workOrderId: Number(input.workOrderId),
      actorType: 'admin',
      actorId: input.actorName || '',
      eventType: 'published',
      fromStatus: current.status,
      toStatus: 'open',
      payloadJson: JSON.stringify({ voucherConsumeDeferred: true }),
      now: input.now,
    })

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

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 updateWorkOrderAdminNote(
  workOrderId: number | string,
  note: string,
  updatedAt: string,
): Promise<WorkOrderRow | null> {
  const result = await query<{ id: number }>(
    `UPDATE work_orders SET admin_note = $1, updated_at = $2 WHERE id = $3 RETURNING id`,
    [String(note || '').trim(), updatedAt, Number(workOrderId)],
  )
  if (!result.rows[0]) return null
  return getWorkOrderById(result.rows[0].id)
}

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
}
