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

/**
 * 工单脱离打手（回大厅、取消指派、恢复未分配等）时重置好友赠送进度，
 * 下一个打手重新走“提交资料 → 确认好友 → 冷却”流程。
 * 标准模式工单这些字段本就为空，重置无副作用。
 */
export const RESET_GIFT_FLOW_SQL = `
  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
`

/** 打手提交本人资料（昵称/ID/区服/系统 + 主页截图），进入待确认好友阶段。 */
export async function submitWorkOrderGiftMaterial(input: {
  workOrderId: number
  workerId: number
  boosterMaterialJson: string
  now: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason:
    | 'work_order_not_found'
    | 'work_order_mode_invalid'
    | 'work_order_owner_required'
    | 'work_order_status_invalid'
    | 'work_order_gift_phase_invalid'
    | 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) return { order: null, failureReason: 'work_order_not_found' }
    if (
      normalizeWorkOrderAcceptanceMode(workOrder.acceptance_mode) !==
      WORK_ORDER_ACCEPTANCE_MODE.FRIEND_GIFT
    ) {
      return { order: null, failureReason: 'work_order_mode_invalid' }
    }
    if (Number(workOrder.assigned_worker_id || 0) !== input.workerId) {
      return { order: null, failureReason: 'work_order_owner_required' }
    }
    if (!['in_progress', 'problem'].includes(workOrder.status)) {
      return { order: null, failureReason: 'work_order_status_invalid' }
    }
    if (workOrder.gift_phase !== WORK_ORDER_GIFT_PHASE.MATERIAL_REQUIRED) {
      return { order: null, failureReason: 'work_order_gift_phase_invalid' }
    }

    await client.query(
      `
        UPDATE work_orders
        SET
          gift_phase = 'material_submitted',
          booster_material_json = $1::jsonb,
          friend_added_at = NULL,
          gift_available_at = NULL,
          updated_at = $2
        WHERE id = $3
      `,
      [input.boosterMaterialJson, input.now, input.workOrderId],
    )
    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'worker',
      actorId: String(input.workerId),
      eventType: 'gift_material_submitted',
      fromStatus: workOrder.status,
      toStatus: workOrder.status,
      payloadJson: input.boosterMaterialJson,
      now: input.now,
    })
    return {
      order: await getWorkOrderByIdWithClient(client, input.workOrderId),
      failureReason: null,
    }
  })
}

/** 后台确认已加好友：记录冷却起点并顺延任务时限到冷却结束后再起算。 */
export async function confirmWorkOrderFriendAdded(input: {
  workOrderId: number
  actorName?: string
  now: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason:
    | 'work_order_not_found'
    | 'work_order_mode_invalid'
    | 'work_order_gift_phase_invalid'
    | 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) return { order: null, failureReason: 'work_order_not_found' }
    if (
      normalizeWorkOrderAcceptanceMode(workOrder.acceptance_mode) !==
      WORK_ORDER_ACCEPTANCE_MODE.FRIEND_GIFT
    ) {
      return { order: null, failureReason: 'work_order_mode_invalid' }
    }
    if (workOrder.gift_phase !== WORK_ORDER_GIFT_PHASE.MATERIAL_SUBMITTED) {
      return { order: null, failureReason: 'work_order_gift_phase_invalid' }
    }

    const cooldownHours = normalizeWorkOrderGiftCooldownHours(workOrder.gift_cooldown_hours)
    const cooldownMinutes = normalizeWorkOrderGiftCooldownMinutes(workOrder.gift_cooldown_minutes)
    // 参数必须显式转型：未知类型参数与 interval 做运算时 PostgreSQL 无法唯一解析操作符。
    await client.query(
      `
        UPDATE work_orders
        SET
          gift_phase = 'friend_countdown',
          friend_added_at = $1::timestamptz,
          gift_available_at = $1::timestamptz + ($2::int * INTERVAL '1 hour') + ($3::int * INTERVAL '1 minute'),
          deadline_at = CASE
            WHEN timeout_minutes > 0
              THEN $1::timestamptz + ($2::int * INTERVAL '1 hour') + ($3::int * INTERVAL '1 minute') + (timeout_minutes * INTERVAL '1 minute')
            ELSE NULL
          END,
          updated_at = $1::timestamptz
        WHERE id = $4
      `,
      [input.now, cooldownHours, cooldownMinutes, input.workOrderId],
    )
    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'admin',
      actorId: input.actorName || '',
      eventType: 'friend_confirmed',
      fromStatus: workOrder.status,
      toStatus: workOrder.status,
      payloadJson: JSON.stringify({
        cooldownHours,
        cooldownMinutes,
        friendAddedAt: input.now,
      }),
      now: input.now,
    })
    return {
      order: await getWorkOrderByIdWithClient(client, input.workOrderId),
      failureReason: null,
    }
  })
}

/** 后台人工确认赠送冷却已完成：立即开放打手提交赠送凭证，并从当前重新计算任务时限。 */
export async function completeWorkOrderGiftCooldown(input: {
  workOrderId: number
  note?: string
  actorName?: string
  now: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason:
    | 'work_order_not_found'
    | 'work_order_mode_invalid'
    | 'work_order_status_invalid'
    | 'work_order_gift_phase_invalid'
    | 'work_order_gift_cooldown_completed'
    | 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) return { order: null, failureReason: 'work_order_not_found' }
    if (
      normalizeWorkOrderAcceptanceMode(workOrder.acceptance_mode) !==
      WORK_ORDER_ACCEPTANCE_MODE.FRIEND_GIFT
    ) {
      return { order: null, failureReason: 'work_order_mode_invalid' }
    }
    if (!['in_progress', 'problem'].includes(workOrder.status)) {
      return { order: null, failureReason: 'work_order_status_invalid' }
    }
    if (workOrder.gift_phase !== WORK_ORDER_GIFT_PHASE.FRIEND_COUNTDOWN) {
      return { order: null, failureReason: 'work_order_gift_phase_invalid' }
    }
    const availableAt = Date.parse(String(workOrder.gift_available_at || ''))
    if (Number.isFinite(availableAt) && availableAt <= Date.parse(input.now)) {
      return { order: null, failureReason: 'work_order_gift_cooldown_completed' }
    }

    await client.query(
      `
        UPDATE work_orders
        SET
          gift_available_at = $1::timestamptz,
          deadline_at = CASE
            WHEN timeout_minutes > 0
              THEN $1::timestamptz + (timeout_minutes * INTERVAL '1 minute')
            ELSE NULL
          END,
          updated_at = $1::timestamptz
        WHERE id = $2
      `,
      [input.now, input.workOrderId],
    )
    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'admin',
      actorId: input.actorName || '',
      eventType: 'gift_cooldown_completed_by_admin',
      fromStatus: workOrder.status,
      toStatus: workOrder.status,
      payloadJson: JSON.stringify({
        previousGiftAvailableAt: workOrder.gift_available_at,
        completedAt: input.now,
        note: String(input.note || '').trim(),
      }),
      now: input.now,
    })
    return {
      order: await getWorkOrderByIdWithClient(client, input.workOrderId),
      failureReason: null,
    }
  })
}

/** 后台重置打手资料（资料有误或误确认好友）：清空好友状态并让打手重新提交。 */
export async function resetWorkOrderGiftMaterial(input: {
  workOrderId: number
  note?: string
  actorName?: string
  now: string
}): Promise<{
  order: WorkOrderRow | null
  failureReason:
    | 'work_order_not_found'
    | 'work_order_mode_invalid'
    | 'work_order_status_invalid'
    | 'work_order_gift_phase_invalid'
    | 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) return { order: null, failureReason: 'work_order_not_found' }
    if (
      normalizeWorkOrderAcceptanceMode(workOrder.acceptance_mode) !==
      WORK_ORDER_ACCEPTANCE_MODE.FRIEND_GIFT
    ) {
      return { order: null, failureReason: 'work_order_mode_invalid' }
    }
    if (!['in_progress', 'problem'].includes(workOrder.status)) {
      return { order: null, failureReason: 'work_order_status_invalid' }
    }
    if (
      workOrder.gift_phase !== WORK_ORDER_GIFT_PHASE.MATERIAL_SUBMITTED &&
      workOrder.gift_phase !== WORK_ORDER_GIFT_PHASE.FRIEND_COUNTDOWN
    ) {
      return { order: null, failureReason: 'work_order_gift_phase_invalid' }
    }

    await client.query(
      `
        UPDATE work_orders
        SET
          gift_phase = 'material_required',
          booster_material_json = '{}'::jsonb,
          friend_added_at = NULL,
          gift_available_at = NULL,
          deadline_at = CASE
            WHEN timeout_minutes > 0
              THEN $1::timestamptz + (timeout_minutes * INTERVAL '1 minute')
            ELSE NULL
          END,
          updated_at = $1::timestamptz
        WHERE id = $2
      `,
      [input.now, input.workOrderId],
    )
    await createWorkOrderEventWithClient(client, {
      workOrderId: input.workOrderId,
      actorType: 'admin',
      actorId: input.actorName || '',
      eventType: 'gift_material_reset',
      fromStatus: workOrder.status,
      toStatus: workOrder.status,
      payloadJson: JSON.stringify({
        previousPhase: workOrder.gift_phase,
        note: String(input.note || '').trim(),
      }),
      now: input.now,
    })
    return {
      order: await getWorkOrderByIdWithClient(client, input.workOrderId),
      failureReason: null,
    }
  })
}
