// @ts-check

import { createTaskClaimToken } from '../claim/claim-service.js'
import {
  closeClaimSessionForAdminTask,
  createClaimSessionForAdminTask,
  getClaimDetailForAdminTask,
  getClaimSessionSummaryForAdminTask,
  reloadClaimSessionForAdminTask,
} from '../claim/claim-session-service.js'
import { reserveInventoryForTask } from '../order/inventory-service.js'
import { createHttpError } from '../../utils/http.js'
import { replaceOrderItems } from '../../repositories/order-item-repo.js'
import { createOrder, findOrderByPlatformOrderId } from '../../repositories/order-repo.js'
import { getFulfillmentProfileByKey, listFulfillmentProfileRequirements } from '../../repositories/fulfillment-profile-repo.js'
import { findFirstAvailableInventoryItemBySkuCode } from '../../repositories/inventory-repo.js'
import { createTask, getTaskById, updateTask } from '../../repositories/task-repo.js'
import { createTaskEvent } from '../../repositories/task-event-repo.js'
import { nowIso } from '../../utils/time.js'
import { randomId } from '../../utils/random.js'
import { closeAdminTask, confirmAdminTaskAssistedRole, redeemAdminTaskAssisted } from './admin-write-service.js'
import { createAdminViewerContext, isAssistedClaimTask, parseTaskContext } from './admin-read-shared-helpers.js'

import type {
  AdminEntityIdInput,
  AdminViewerSessionInput,
} from '../../types/admin-read-inputs.js'
import type { AdminManualRedeemCreateInput } from '../../types/admin-write-inputs.js'
import type { TaskRow } from '../../types/repository-rows.js'

const MANUAL_PROVIDER = 'manual'
const MANUAL_PLATFORM = 'manual_redeem'
const MANUAL_SOURCE_TYPE = 'admin_manual_redeem'
const ASSISTED_PROFILE_KEY = 'tencent_claim_assisted'

type JsonObject = Record<string, any>

export async function createAdminManualRedeemTask(
  payload: AdminManualRedeemCreateInput = {},
  session: AdminViewerSessionInput | null = null,
): Promise<JsonObject> {
  const viewerContext = createAdminViewerContext(session)
  const proofValue = normalizeManualProofValue(payload.proofValue)
  const skuCode = String(payload.skuCode || '').trim()
  const skuName = String(payload.skuName || '').trim() || skuCode
  const remark = String(payload.remark || '').trim()

  if (!proofValue) {
    throw createHttpError('请先输入唯一凭据', {
      statusCode: 400,
      errorCode: 'admin_manual_redeem_missing_proof',
    })
  }

  if (!skuCode) {
    throw createHttpError('请先选择内部履约 SKU', {
      statusCode: 400,
      errorCode: 'admin_manual_redeem_missing_sku',
    })
  }

  const existingOrder = await findOrderByPlatformOrderId({
    provider: MANUAL_PROVIDER,
    platform: MANUAL_PLATFORM,
    shopId: '',
    platformOrderId: proofValue,
  })

  if (existingOrder) {
    throw createHttpError('该唯一凭据已经创建过人工兑换任务，请勿重复提交', {
      statusCode: 409,
      errorCode: 'admin_manual_redeem_duplicate_proof',
    })
  }

  const profile = await getFulfillmentProfileByKey(ASSISTED_PROFILE_KEY)
  if (!profile) {
    throw createHttpError('人工兑换所需的履约档案不存在，请先检查系统初始化', {
      statusCode: 500,
      errorCode: 'admin_manual_redeem_profile_missing',
    })
  }

  const requirements = await listFulfillmentProfileRequirements(profile.id)
  const primaryRequirement = requirements.find((item) => item.is_required !== false) || requirements[0] || null
  const credentialType = String(primaryRequirement?.credential_type || primaryRequirement?.credentialType || 'tencent_code').trim() || 'tencent_code'
  const roleKey = String(primaryRequirement?.role_key || primaryRequirement?.roleKey || 'primary_code').trim() || 'primary_code'
  const availableInventory = await findFirstAvailableInventoryItemBySkuCode(
    skuCode,
    credentialType,
    viewerContext.allowedInventoryGroupCodes,
  )

  if (!availableInventory) {
    if (viewerContext.restrictInventoryGroups && viewerContext.allowedInventoryGroupCodes?.length === 0) {
      throw createHttpError('当前客服未绑定任何库存组，无法创建人工兑换任务', {
        statusCode: 403,
        errorCode: 'admin_manual_redeem_inventory_group_unbound',
      })
    }

    throw createHttpError('当前 SKU 没有可用库存，无法创建人工兑换任务', {
      statusCode: 409,
      errorCode: 'admin_manual_redeem_inventory_unavailable',
    })
  }

  const now = nowIso()
  const manualContext = {
    sourceType: MANUAL_SOURCE_TYPE,
    proofValue,
    remark,
    createdAt: now,
    createdBy: session
      ? {
        userId: Number(session.userId || 0) || 0,
        username: String(session.username || '').trim(),
        role: String(session.role || '').trim(),
      }
      : null,
  }

  let order
  try {
    order = await createOrder({
      provider: MANUAL_PROVIDER,
      platform: MANUAL_PLATFORM,
      shopId: '',
      shopName: '人工兑换',
      platformOrderId: proofValue,
      orderStatus: 'manual_created',
      payStatus: 'paid',
      buyerId: '',
      buyerName: '',
      receiverContact: '',
      totalAmount: 0,
      currency: 'CNY',
      rawPayloadJson: JSON.stringify({
        manualRedeem: manualContext,
      }),
      paidAt: now,
      createdAt: now,
      updatedAt: now,
    })
  } catch (error) {
    if (String(error?.code || '') === '23505') {
      throw createHttpError('该唯一凭据已经创建过人工兑换任务，请勿重复提交', {
        statusCode: 409,
        errorCode: 'admin_manual_redeem_duplicate_proof',
      })
    }

    throw error
  }

  if (!order) {
    throw createHttpError('人工兑换订单创建失败', {
      statusCode: 500,
      errorCode: 'admin_manual_redeem_order_create_failed',
    })
  }

  const orderItems = await replaceOrderItems(order.id, [
    {
      skuCode,
      skuName,
      quantity: 1,
      specJson: JSON.stringify({
        title: skuName,
        sourceType: MANUAL_SOURCE_TYPE,
      }),
      itemSnapshotJson: JSON.stringify({
        manualRedeem: manualContext,
        skuCode,
        skuName,
      }),
      createdAt: now,
      updatedAt: now,
    },
  ])

  const orderItem = orderItems[0]
  if (!orderItem) {
    throw createHttpError('人工兑换订单商品创建失败', {
      statusCode: 500,
      errorCode: 'admin_manual_redeem_order_item_create_failed',
    })
  }

  const createdTask = await createTask({
    orderId: order.id,
    orderItemId: orderItem.id,
    unitIndex: 1,
    provider: MANUAL_PROVIDER,
    platform: MANUAL_PLATFORM,
    shopId: '',
    shopName: '人工兑换',
    platformOrderId: proofValue,
    taskNo: randomId('MR'),
    profileId: Number(profile.id),
    executorKey: String(profile.executor_key || ASSISTED_PROFILE_KEY),
    taskStatus: 'paid',
    inventoryStatus: 'pending',
    deliveryStatus: 'pending',
    resultCode: '',
    resultMessage: '',
    claimToken: '',
    claimExpiresAt: null,
    automationMode: 'manual',
    requiresClaim: true,
    userActionStatus: 'pending_claim',
    attemptCount: 0,
    lastError: '',
    contextJson: JSON.stringify({
      profileKey: String(profile.profile_key || ASSISTED_PROFILE_KEY),
      profileName: String(profile.name || ''),
      skuCode,
      skuName,
      inventorySkuCode: skuCode,
      primaryRequirement: primaryRequirement
        ? {
          roleKey,
          credentialType,
        }
        : null,
      inventoryGroupCode: String(availableInventory.inventory_group_code || '').trim(),
      manualRedeem: manualContext,
    }),
    createdAt: now,
    updatedAt: now,
  })

  if (!createdTask) {
    throw createHttpError('人工兑换任务创建失败', {
      statusCode: 500,
      errorCode: 'admin_manual_redeem_task_create_failed',
    })
  }

  const reservedInventory = await reserveInventoryForTask({
    skuCode,
    taskId: createdTask.id,
    credentialType,
    roleKey,
    inventoryGroupCodes: viewerContext.allowedInventoryGroupCodes,
  })

  if (!reservedInventory) {
    await updateTask(createdTask.id, {
      task_status: 'waiting_inventory',
      inventory_status: 'pending',
      last_error: '库存不足，无法为人工兑换任务预占库存',
      updated_at: nowIso(),
    })

    throw createHttpError('库存刚刚被其他任务占用，请重新选择或稍后再试', {
      statusCode: 409,
      errorCode: 'admin_manual_redeem_inventory_race_lost',
    })
  }

  const claimToken = await createTaskClaimToken(createdTask.id)
  await updateTask(createdTask.id, {
    task_status: 'link_generated',
    inventory_status: 'reserved',
    claim_token: claimToken.token,
    claim_expires_at: claimToken.expired_at,
    user_action_status: 'pending_claim',
    last_error: '',
    context_json: JSON.stringify({
      ...parseTaskContext(createdTask),
      inventoryGroupCode: String(reservedInventory.inventory_group_code || '').trim(),
    }),
    updated_at: nowIso(),
  })

  await createTaskEvent(createdTask.id, 'manual_redeem_created', {
    sourceType: MANUAL_SOURCE_TYPE,
    proofValue,
    skuCode,
    skuName,
    inventoryItemId: Number(reservedInventory.id || 0) || null,
    inventoryGroupCode: String(reservedInventory.inventory_group_code || '').trim(),
    createdBy: manualContext.createdBy,
    remark,
  }, nowIso())

  return getAdminManualRedeemDetail(createdTask.id, session)
}

export async function getAdminManualRedeemDetail(
  taskId: AdminEntityIdInput,
  session: AdminViewerSessionInput | null = null,
): Promise<JsonObject> {
  const task = await getRequiredManualRedeemTask(taskId, session)
  const detail = await getClaimDetailForAdminTask(task.id)
  return decorateManualRedeemDetail(task, detail)
}

export async function createAdminManualRedeemSession(
  taskId: AdminEntityIdInput,
  payload: { loginType?: string, forceRecreate?: boolean } = {},
  session: AdminViewerSessionInput | null = null,
): Promise<JsonObject> {
  const task = await getRequiredManualRedeemTask(taskId, session)
  const detail = await createClaimSessionForAdminTask(task.id, payload)
  return decorateManualRedeemDetail(task, detail)
}

export async function getAdminManualRedeemSessionSummary(
  taskId: AdminEntityIdInput,
  session: AdminViewerSessionInput | null = null,
): Promise<JsonObject> {
  const task = await getRequiredManualRedeemTask(taskId, session)
  const detail = await getClaimSessionSummaryForAdminTask(task.id)
  return decorateManualRedeemDetail(task, detail)
}

export async function reloadAdminManualRedeemSession(
  taskId: AdminEntityIdInput,
  session: AdminViewerSessionInput | null = null,
): Promise<JsonObject> {
  const task = await getRequiredManualRedeemTask(taskId, session)
  const detail = await reloadClaimSessionForAdminTask(task.id)
  return decorateManualRedeemDetail(task, detail)
}

export async function closeAdminManualRedeemSession(
  taskId: AdminEntityIdInput,
  session: AdminViewerSessionInput | null = null,
): Promise<JsonObject> {
  const task = await getRequiredManualRedeemTask(taskId, session)
  const detail = await closeClaimSessionForAdminTask(task.id)
  return decorateManualRedeemDetail(task, detail)
}

export async function closeAdminManualRedeemTask(
  taskId: AdminEntityIdInput,
  session: AdminViewerSessionInput | null = null,
): Promise<JsonObject> {
  const task = await getRequiredManualRedeemTask(taskId, session)
  await closeAdminTask(task.id)
  return getAdminManualRedeemDetail(task.id, session)
}

export async function confirmAdminManualRedeemRole(
  taskId: AdminEntityIdInput,
  session: AdminViewerSessionInput | null = null,
): Promise<JsonObject> {
  const task = await getRequiredManualRedeemTask(taskId, session)
  await confirmAdminTaskAssistedRole(task.id, session)
  return getAdminManualRedeemDetail(task.id, session)
}

export async function redeemAdminManualRedeemTask(
  taskId: AdminEntityIdInput,
  session: AdminViewerSessionInput | null = null,
): Promise<JsonObject> {
  const task = await getRequiredManualRedeemTask(taskId, session)
  await redeemAdminTaskAssisted(task.id, session)
  return getAdminManualRedeemDetail(task.id, session)
}

async function getRequiredManualRedeemTask(
  taskId: AdminEntityIdInput,
  session: AdminViewerSessionInput | null = null,
): Promise<TaskRow> {
  const task = await getTaskById(Number(taskId))

  if (!task) {
    throw createHttpError('人工兑换任务不存在', {
      statusCode: 404,
      errorCode: 'admin_manual_redeem_task_not_found',
    })
  }

  const taskContext = parseTaskContext(task)
  const sourceType = String(taskContext?.manualRedeem?.sourceType || '').trim()

  if (!isAssistedClaimTask(task) || sourceType !== MANUAL_SOURCE_TYPE) {
    throw createHttpError('当前任务不是人工兑换任务', {
      statusCode: 409,
      errorCode: 'admin_manual_redeem_task_invalid',
    })
  }

  const viewerContext = createAdminViewerContext(session)
  if (viewerContext.restrictInventoryGroups) {
    if (!Array.isArray(viewerContext.allowedInventoryGroupCodes) || viewerContext.allowedInventoryGroupCodes.length === 0) {
      throw createHttpError('当前客服未绑定任何库存组，无法操作人工兑换任务', {
        statusCode: 403,
        errorCode: 'admin_manual_redeem_inventory_group_unbound',
      })
    }

    const inventoryGroupCode = String(taskContext?.inventoryGroupCode || '').trim()

    if (inventoryGroupCode && !viewerContext.allowedInventoryGroupCodes.includes(inventoryGroupCode)) {
      throw createHttpError('当前客服无权操作该库存组下的人工兑换任务', {
        statusCode: 403,
        errorCode: 'admin_manual_redeem_inventory_group_denied',
      })
    }
  }

  return task
}

function decorateManualRedeemDetail(task: TaskRow, detail: any): JsonObject {
  const taskContext = parseTaskContext(task)
  const manualRedeem = taskContext.manualRedeem && typeof taskContext.manualRedeem === 'object'
    ? taskContext.manualRedeem
    : {}

  return {
    ...detail,
    claimUrl: '',
    manualRequest: {
      sourceType: String(manualRedeem.sourceType || MANUAL_SOURCE_TYPE).trim() || MANUAL_SOURCE_TYPE,
      proofValue: String(manualRedeem.proofValue || detail?.order?.platformOrderId || '').trim(),
      remark: String(manualRedeem.remark || '').trim(),
    },
    result: detail.result
      ? {
        ...detail.result,
        screenshotUrl: detail.result.screenshotReady ? `/api/v1/admin/tasks/${detail.task.taskId}/screenshot` : '',
      }
      : null,
  }
}

function normalizeManualProofValue(value: unknown): string {
  return String(value || '').replace(/\s+/g, ' ').trim()
}
