import { buildClaimUrl } from '../claim/claim-service.js'
import { buildClaimIdentityAdminSummary } from '../claim/claim-identity.js'
import { getClaimTokenById } from '../../repositories/claim-token-repo.js'
import {
  listOrderItemsByOrderId,
  listOrderItemsByOrderIds,
} from '../../repositories/order-item-repo.js'
import { getOrderById, listOrders } from '../../repositories/order-repo.js'
import {
  getTaskById,
  listTasks,
  listTasksByOrderId,
  listTasksByOrderIds,
} from '../../repositories/task-repo.js'
import { listTaskEventsByTaskId } from '../../repositories/task-event-repo.js'
import type { JsonRecord } from '../../types/json.js'
import {
  listKuaishouIndustryVouchersByOid,
  listKuaishouIndustryVouchersByTaskId,
} from '../../repositories/kuaishou-industry-voucher-repo.js'
import { listCloudtentaclesSources } from '../platforms/cloudtentacles/source-config-service.js'

import { createHttpError } from '../../utils/http.js'
import { formatFenToAmount, normalizeFen } from '../../utils/money.js'
import {
  normalizeDateQuery,
  normalizePage,
  normalizePageSize,
  safeParseJson,
} from './admin-query-utils.js'
import {
  canCompleteManualDispatchTaskStatus,
  canDispatchKuaishouCloudFulfillmentStatus,
  canMarkManualReviewTaskStatus,
  canPrepareKuaishouCloudFulfillmentStatus,
  canRebindKuaishouCloudRoleStatus,
  canReturnKuaishouCloudFulfillmentStatus,
  canRetryTaskStatus,
} from '../../domain/task-status.js'
import {
  canViewerCloseTask,
  canRegenerateClaimLinkForViewer,
  createAdminViewerContext,
  getTaskPrimaryClaimTokenId,
  isManualDispatchTask,
  mapKuaishouCloudFulfillmentContext,
  mapKuaishouCloudTaskStateProjection,
  mapManualDispatchContext,
  mapRedeemResolutionContext,
  parseTaskContext,
  parseTaskState,
  resolveAdminTaskScreenshotUrl,
  resolveDisplayShopName,
  resolveOrderItemDeliveryMode,
  resolveOrderItemTitle,
} from './admin-read-shared-helpers.js'
import { mapAdminOrderListItem, summarizeOrderItems } from './admin-order-read-helpers.js'
import {
  buildOrderFulfillmentProgress,
  getRequiredTask,
  mapAdminTaskEvent,
  mapAdminTaskListItem,
  mapAdminTaskSummary,
} from './admin-task-read-helpers.js'

import type {
  AdminOrderListResponse,
  AdminTaskListResponse,
} from '../../types/admin/read-models.js'
import type { AdminTaskDetailView } from '../../types/admin/task-detail.js'
import type {
  AdminEntityIdInput,
  AdminOrderListQueryInput,
  AdminTaskListQueryInput,
  AdminViewerSessionInput,
} from '../../types/admin/read-inputs.js'
import type { KuaishouIndustryVoucherRow, TaskRow } from '../../types/repository/rows.js'

export async function getAdminOrders(
  query: AdminOrderListQueryInput = {},
): Promise<AdminOrderListResponse> {
  const page = normalizePage(query.page)
  const pageSize = normalizePageSize(query.pageSize)
  const { items, total } = await listOrders({
    page,
    pageSize,
    keyword: String(query.keyword || '').trim(),
    provider: String(query.provider || '').trim(),
    executorKey: String(query.executorKey || '').trim(),
    platformOrderId: String(query.platformOrderId || '').trim(),
    payStatus: String(query.payStatus || '').trim(),
    taskStatus: String(query.taskStatus || '').trim(),
    skuCode: String(query.skuCode || '').trim(),
    dateFrom: normalizeDateQuery(query.dateFrom),
    dateTo: normalizeDateQuery(query.dateTo, true),
  })

  const orderIds = items.map((item) => item.id)
  const [orderItems, tasks] = await Promise.all([
    listOrderItemsByOrderIds(orderIds),
    listTasksByOrderIds(orderIds),
  ])
  const orderItemsByOrderId = groupRowsByOrderId(orderItems)
  const tasksByOrderId = groupRowsByOrderId(tasks)

  return {
    items: items.map((item) =>
      mapAdminOrderListItem(item, {
        orderItems: orderItemsByOrderId.get(item.id) || [],
        tasks: tasksByOrderId.get(item.id) || [],
      }),
    ),
    pagination: { page, pageSize, total },
  }
}

function groupRowsByOrderId<T extends { order_id: number }>(rows: T[]): Map<number, T[]> {
  const grouped = new Map<number, T[]>()
  for (const row of rows) {
    const group = grouped.get(row.order_id) || []
    group.push(row)
    grouped.set(row.order_id, group)
  }
  return grouped
}

export async function getAdminOrderDetail(
  orderId: AdminEntityIdInput,
  session: AdminViewerSessionInput | null = null,
): Promise<JsonRecord> {
  const order = await getOrderById(Number(orderId))

  if (!order) {
    throw createHttpError('订单不存在', {
      statusCode: 404,
      errorCode: 'admin_order_not_found',
    })
  }

  const [items, tasks] = await Promise.all([
    listOrderItemsByOrderId(order.id),
    listTasksByOrderId(order.id),
  ])
  const kuaishouIndustryVouchers = await listKuaishouIndustryVouchersByOid(order.platform_order_id)
  const itemSummary = summarizeOrderItems(items)
  const viewerContext = createAdminViewerContext(session)

  return {
    order: {
      orderId: order.id,
      provider: order.provider || '',
      platform: order.platform,
      shopId: order.shop_id || '',
      shopName: resolveDisplayShopName(order.provider, order.shop_id, order.shop_name),
      platformOrderId: order.platform_order_id,
      orderStatus: order.order_status,
      payStatus: order.pay_status,
      buyerId: order.buyer_id,
      buyerName: order.buyer_name,
      receiverContact: order.receiver_contact,
      totalAmount: formatFenToAmount(order.total_amount),
      totalAmountFen: normalizeFen(order.total_amount),
      currency: order.currency,
      paidAt: order.paid_at,
      createdAt: order.created_at,
      updatedAt: order.updated_at,
      itemSummary,
      rawPayload:
        order.raw_payload_json && typeof order.raw_payload_json === 'object'
          ? order.raw_payload_json
          : JSON.parse(String(order.raw_payload_json || '{}')),
      fulfillmentProgress: buildOrderFulfillmentProgress(tasks),
    },
    items: items.map((item) => ({
      orderItemId: item.id,
      skuCode: item.sku_code,
      skuName: item.sku_name,
      itemTitle: resolveOrderItemTitle(item),
      quantity: item.quantity,
      deliveryMode: resolveOrderItemDeliveryMode(tasks, item.id),
      spec:
        item.spec_json && typeof item.spec_json === 'object'
          ? item.spec_json
          : JSON.parse(String(item.spec_json || '{}')),
    })),
    tasks: tasks.map((task) => mapAdminTaskSummary(task)),
    kuaishouIndustryVouchers: kuaishouIndustryVouchers.map(mapAdminKuaishouIndustryVoucher),
    operations: {
      canResendKuaishouIndustryVoucherCode:
        viewerContext.canOperateKuaishouIndustryVoucher && kuaishouIndustryVouchers.length > 0,
    },
  }
}

function mapAdminKuaishouIndustryVoucher(voucher: {
  voucher_code?: unknown
  oid?: unknown
  unit_index?: unknown
  status?: unknown
  send_callback_status?: unknown
  send_callback_attempt_count?: unknown
  send_callback_last_error?: unknown
  send_callback_sent_at?: unknown
  consume_serial_num?: unknown
  consume_details_json?: unknown
  consumed_at?: unknown
  destroyed_at?: unknown
}) {
  return {
    oid: String(voucher.oid || '').trim(),
    voucherCode: String(voucher.voucher_code || '').trim(),
    unitIndex: Number(voucher.unit_index || 0) || 0,
    status: String(voucher.status || '').trim(),
    sendCallbackStatus: String(voucher.send_callback_status || '').trim(),
    sendCallbackAttemptCount: Number(voucher.send_callback_attempt_count || 0) || 0,
    sendCallbackLastError: String(voucher.send_callback_last_error || '').trim(),
    sendCallbackSentAt: voucher.send_callback_sent_at || null,
    consumeSerialNum: String(voucher.consume_serial_num || '').trim(),
    consumeDetails: normalizeAdminJsonArray(voucher.consume_details_json),
    consumedAt: voucher.consumed_at || null,
    destroyedAt: voucher.destroyed_at || null,
  }
}

export async function getAdminTasks(
  query: AdminTaskListQueryInput = {},
  session: AdminViewerSessionInput | null = null,
): Promise<AdminTaskListResponse> {
  const page = normalizePage(query.page)
  const pageSize = normalizePageSize(query.pageSize)
  const { items, total } = await listTasks({
    page,
    pageSize,
    keyword: String(query.keyword || '').trim(),
    executorKey: String(query.executorKey || '').trim(),
    status: String(query.status || '').trim(),
    platformOrderId: String(query.platformOrderId || '').trim(),
    taskNo: String(query.taskNo || '').trim(),
    skuCode: String(query.skuCode || '').trim(),
    roleId: String(query.roleId || '').trim(),
    dateFrom: normalizeDateQuery(query.dateFrom),
    dateTo: normalizeDateQuery(query.dateTo, true),
  })
  const viewerContext = createAdminViewerContext(session)

  return {
    items: items.map((item) => mapAdminTaskListItem(item, viewerContext)),
    pagination: { page, pageSize, total },
  }
}

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

  if (!task) {
    throw createHttpError('任务不存在', {
      statusCode: 404,
      errorCode: 'admin_task_not_found',
    })
  }

  const primaryClaimTokenId = getTaskPrimaryClaimTokenId(task)
  const [order, claimToken, taskEvents, taskKuaishouIndustryVouchers, oidKuaishouIndustryVouchers] =
    await Promise.all([
      getOrderById(task.order_id),
      primaryClaimTokenId ? getClaimTokenById(primaryClaimTokenId) : Promise.resolve(null),
      listTaskEventsByTaskId(task.id),
      listKuaishouIndustryVouchersByTaskId(task.id),
      task.platform_order_id
        ? listKuaishouIndustryVouchersByOid(task.platform_order_id)
        : Promise.resolve([]),
    ])
  const orderItems = order ? await listOrderItemsByOrderId(order.id) : []
  const orderItem = orderItems.find((item) => item.id === task.order_item_id) || null
  const taskContext = parseTaskContext(task)
  const taskState = parseTaskState(task)
  const viewerContext = createAdminViewerContext(session)
  // 统一对外领取入口为本站 claim 链接；平台 H5 仅作内部上下文
  const claimUrl = claimToken ? buildClaimUrl(claimToken.token) : ''
  const screenshotUrl = await resolveAdminTaskScreenshotUrl(task, viewerContext)
  const cloudSourceLabelMap = buildCloudSourceLabelMap()
  const kuaishouCloudFulfillment =
    mapKuaishouCloudFulfillmentContext(taskContext.kuaishouCloudFulfillment, {
      cloudSourceLabelMap,
    }) || mapKuaishouCloudTaskStateProjection(task, { cloudSourceLabelMap })
  const claimIdentity = buildClaimIdentityAdminSummary(taskContext, {
    flowLike: taskContext.kuaishouCloudFulfillment || taskContext.kuaishouFeifei || null,
    taskRoleId: task.role_id,
    taskRoleName: task.role_name,
  })
  const firstKuaishouIndustryVoucher = resolveKuaishouIndustryVoucherForTask(
    task,
    taskContext,
    taskKuaishouIndustryVouchers,
    oidKuaishouIndustryVouchers,
  )
  const kuaishouIndustryVoucher = firstKuaishouIndustryVoucher
    ? mapAdminKuaishouIndustryVoucher(firstKuaishouIndustryVoucher)
    : mapKuaishouIndustryVoucherContext(taskContext.kuaishouIndustryVoucher)
  const hasKuaishouIndustryVoucher = Boolean(
    kuaishouIndustryVoucher && String(kuaishouIndustryVoucher.voucherCode || '').trim(),
  )
  const kuaishouIndustryVoucherStatus = String(kuaishouIndustryVoucher?.status || '')
    .trim()
    .toUpperCase()
  const kuaishouIndustryVoucherSendCallbackStatus = String(
    kuaishouIndustryVoucher?.sendCallbackStatus || '',
  )
    .trim()
    .toLowerCase()

  return {
    task: mapAdminTaskListItem(
      {
        ...task,
        sku_code: orderItem?.sku_code || '',
        sku_name: orderItem?.sku_name || '',
        claim_token: claimToken?.token || '',
      },
      viewerContext,
    ),
    order: order
      ? {
          orderId: order.id,
          provider: order.provider || '',
          platform: order.platform,
          shopId: order.shop_id || '',
          shopName: order.shop_name || '',
          platformOrderId: order.platform_order_id,
          payStatus: order.pay_status,
          orderStatus: order.order_status,
        }
      : null,
    orderItem: orderItem
      ? {
          orderItemId: orderItem.id,
          skuCode: orderItem.sku_code,
          skuName: orderItem.sku_name,
          quantity: orderItem.quantity,
        }
      : null,
    claimToken: claimToken
      ? {
          primaryClaimTokenId: claimToken.id,
          token: viewerContext.canViewSensitiveTaskData ? claimToken.token || '' : '',
          status: claimToken.status || '',
          expiredAt: claimToken.expired_at || '',
          claimUrl,
        }
      : null,
    artifacts: viewerContext.canViewSensitiveTaskData ? safeParseJson(task.artifacts_json) : {},
    screenshotUrl,
    review: {
      required: false,
      screenshotCapturedAt: String(taskState.reviewCapturedAt || '').trim() || null,
      roleId: String(taskState.reviewRoleId || '').trim() || '',
      roleName: String(taskState.reviewRoleName || '').trim() || '',
    },
    redeemResolution: mapRedeemResolutionContext(taskContext.redeemResolution),
    claimIdentity,
    kuaishouCloudFulfillment,
    kuaishouIndustryVoucher,
    manualDispatch: mapManualDispatchContext(taskContext.manualDispatch, viewerContext),
    events: taskEvents.map(mapAdminTaskEvent),
    operations: {
      canRetry:
        viewerContext.canManageTaskLifecycle &&
        !isManualDispatchTask(task) &&
        canRetryTaskStatus(task.task_status),
      canRegenerateClaimLink: canRegenerateClaimLinkForViewer(task, viewerContext),
      canClose: canViewerCloseTask(task, viewerContext),
      canMarkManualReview:
        viewerContext.canManageTaskLifecycle && canMarkManualReviewTaskStatus(task.task_status),
      canCompleteManualDispatch:
        viewerContext.canManageTaskLifecycle &&
        isManualDispatchTask(task) &&
        canCompleteManualDispatchTaskStatus(task.task_status),
      canPrepareKuaishouCloudFulfillment:
        viewerContext.canManageTaskLifecycle &&
        String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' &&
        String(kuaishouCloudFulfillment?.dispatch.status || 'pending').trim() === 'pending' &&
        String(kuaishouCloudFulfillment?.returnNumber.status || 'pending').trim() === 'pending' &&
        canPrepareKuaishouCloudFulfillmentStatus(task.task_status),
      canRefreshKuaishouCloudRoleInfo:
        viewerContext.canOperateAssistedTask &&
        String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' &&
        Number(kuaishouCloudFulfillment?.binding.vnId || 0) > 0,
      canRebindKuaishouCloudRole:
        viewerContext.canOperateAssistedTask &&
        String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' &&
        canRebindKuaishouCloudRoleStatus(task.task_status) &&
        String(kuaishouCloudFulfillment?.dispatch.status || 'pending').trim() !== 'success' &&
        Number(kuaishouCloudFulfillment?.binding.vnId || 0) > 0,
      canDispatchKuaishouCloudFulfillment:
        viewerContext.canManageTaskLifecycle &&
        String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' &&
        canDispatchKuaishouCloudFulfillmentStatus(task.task_status),
      canReturnKuaishouCloudFulfillment:
        viewerContext.canManageTaskLifecycle &&
        String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' &&
        canReturnKuaishouCloudFulfillmentStatus(task.task_status),
      canResendKuaishouIndustryVoucherCode:
        viewerContext.canOperateKuaishouIndustryVoucher && hasKuaishouIndustryVoucher,
      canConsumeKuaishouIndustryVoucher:
        viewerContext.canOperateKuaishouIndustryVoucher &&
        hasKuaishouIndustryVoucher &&
        kuaishouIndustryVoucherStatus !== 'CONSUMED' &&
        kuaishouIndustryVoucherSendCallbackStatus === 'success',
      canViewSensitiveTaskData: viewerContext.canViewSensitiveTaskData,
    },
  }
}

function mapKuaishouIndustryVoucherContext(value: unknown): JsonRecord | null {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return null
  }

  const record = value as JsonRecord
  return {
    oid: String(record.oid || '').trim(),
    voucherCode: String(record.voucherCode || record.eticketId || '').trim(),
    unitIndex: Number(record.unitIndex || 0) || 0,
    status: String(record.status || '').trim(),
    sendCallbackStatus: String(record.sendCallbackStatus || '').trim(),
    sendCallbackLastError: String(record.sendCallbackLastError || '').trim(),
    sendCallbackAttemptCount: Number(record.sendCallbackAttemptCount || 0) || 0,
    sendCallbackSentAt: record.sendCallbackSentAt || null,
    consumeSerialNum: String(record.consumeSerialNum || '').trim(),
    consumeDetails: normalizeAdminJsonArray(record.consumeDetails),
    consumedAt: record.consumedAt || null,
    destroyedAt: record.destroyedAt || null,
  }
}

function normalizeAdminJsonArray(value: unknown): JsonRecord[] {
  if (Array.isArray(value)) {
    return value.filter((item): item is JsonRecord =>
      Boolean(item && typeof item === 'object' && !Array.isArray(item)),
    )
  }

  try {
    const parsed = JSON.parse(String(value || '[]'))
    return Array.isArray(parsed)
      ? parsed.filter((item): item is JsonRecord =>
          Boolean(item && typeof item === 'object' && !Array.isArray(item)),
        )
      : []
  } catch {
    return []
  }
}

function resolveKuaishouIndustryVoucherForTask(
  task: TaskRow,
  taskContext: JsonRecord,
  taskVouchers: KuaishouIndustryVoucherRow[],
  oidVouchers: KuaishouIndustryVoucherRow[],
): KuaishouIndustryVoucherRow | null {
  const linkedVoucher = taskVouchers[0] || null
  if (linkedVoucher) {
    return linkedVoucher
  }

  const voucherContext =
    taskContext.kuaishouIndustryVoucher &&
    typeof taskContext.kuaishouIndustryVoucher === 'object' &&
    !Array.isArray(taskContext.kuaishouIndustryVoucher)
      ? (taskContext.kuaishouIndustryVoucher as JsonRecord)
      : {}
  const voucherCode = String(voucherContext.voucherCode || voucherContext.eticketId || '').trim()
  if (voucherCode) {
    const matchedByCode = oidVouchers.find(
      (voucher) => String(voucher.voucher_code || '').trim() === voucherCode,
    )
    if (matchedByCode) {
      return matchedByCode
    }
  }

  const unitIndex = Number(task.unit_index || 0) || 0
  if (unitIndex > 0) {
    const matchedByUnit = oidVouchers.find(
      (voucher) => Number(voucher.unit_index || 0) === unitIndex,
    )
    if (matchedByUnit) {
      return matchedByUnit
    }
  }

  return oidVouchers.length === 1 ? oidVouchers[0] || null : null
}

function buildCloudSourceLabelMap() {
  const labelMap = new Map<string, string>()

  for (const source of listCloudtentaclesSources().sources) {
    const key = String(source.key || '').trim()
    if (!key) continue

    const label = String(source.label || source.username || key).trim() || key
    labelMap.set(key, label)
  }

  return labelMap
}

export async function getAdminTaskScreenshotPath(
  taskId: AdminEntityIdInput,
  _session: AdminViewerSessionInput | null = null,
): Promise<string> {
  const task = await getRequiredTask(taskId)

  if (task.screenshot_path) {
    return task.screenshot_path
  }

  throw createHttpError('当前任务还没有可查看截图', {
    statusCode: 404,
    errorCode: 'admin_task_screenshot_not_found',
  })
}
