import { findClaimTokenByToken, updateClaimToken } from '../../repositories/claim-token-repo.js'
import { getOrderItemById } from '../../repositories/order-item-repo.js'
import { getOrderById } from '../../repositories/order-repo.js'
import { findTaskByClaimTokenId, updateTask } from '../../repositories/task-repo.js'
import { createHttpError } from '../../utils/http.js'
import { formatFenToAmount, normalizeFen } from '../../utils/money.js'
import { parseTaskContext as parseTaskContextValue } from '../../utils/task-json.js'
import { nowIso } from '../../utils/time.js'
import { TASK_STATUS, isTaskFinalStatus } from '../../domain/task-status.js'
import { buildClaimUrl } from './claim-service.js'
import { buildClaimIdentityPayload, getClaimIdentityFromContext } from './claim-identity.js'
import { resolveKuaishouFeifeiH5UrlWithUid } from '../fulfillment/kuaishou-feifei/index.js'
import { normalizeAffiliateDashFlow } from '../fulfillment/affiliate-dash/index.js'
import type { ClaimTokenRow, OrderItemRow, OrderRow, TaskRow } from '../../types/repository/rows.js'
import { asJsonObject, type JsonObject } from '../../types/json.js'

export const CLAIM_TERMINAL_STATUSES = new Set([TASK_STATUS.EXPIRED, TASK_STATUS.CLOSED])
type ClaimContext = {
  claimToken: ClaimTokenRow
  task: TaskRow
  order: OrderRow
  orderItem: OrderItemRow
}

export async function getClaimContext(token: unknown) {
  const normalized = String(token || '').trim()

  if (!normalized) {
    throw createHttpError('缺少领取 token', {
      statusCode: 400,
      errorCode: 'missing_claim_token',
    })
  }

  const claimToken = await findClaimTokenByToken(normalized)

  if (!claimToken) {
    throw createHttpError('领取链接无效或不存在', {
      statusCode: 404,
      errorCode: 'claim_token_not_found',
    })
  }

  const task = await findTaskByClaimTokenId(claimToken.id)

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

  if (claimToken.status !== 'active') {
    throw createHttpError('领取链接当前不可用', {
      statusCode: 410,
      errorCode: 'claim_token_inactive',
    })
  }

  if (claimToken.expired_at && new Date(claimToken.expired_at).getTime() <= Date.now()) {
    const expiredContext = await expireClaimContext(claimToken, task)

    throw createHttpError('领取链接已过期', {
      statusCode: 410,
      errorCode: 'claim_token_expired',
      context: expiredContext,
    })
  }

  const [order, orderItem] = await Promise.all([
    getOrderById(task.order_id),
    getOrderItemById(task.order_item_id),
  ])

  if (!order || !orderItem) {
    throw createHttpError('领取任务关联订单不完整', {
      statusCode: 500,
      errorCode: 'claim_order_incomplete',
    })
  }

  return {
    claimToken,
    task,
    order,
    orderItem,
  }
}

export function buildClaimDetailPayload({ claimToken, task, order, orderItem }: ClaimContext) {
  if (String(task.executor_key || '').trim() === 'kuaishou_feifei') {
    return buildKuaishouFeifeiClaimDetailPayload({ claimToken, task, order, orderItem })
  }

  if (String(task.executor_key || '').trim() === 'affiliate_dash') {
    return buildAffiliateDashClaimDetailPayload({ claimToken, task, order, orderItem })
  }

  const taskContext = parseTaskContext(task)
  const claimIdentity = buildClaimIdentityPayload(taskContext.claimIdentity)
  const kuaishouCloudSource = resolveClaimKuaishouCloudSource(task)
  const kuaishouCloudFulfillment = mapClaimKuaishouCloudFulfillment(task, order)
  const displaySkuName = resolveClaimOrderItemDisplaySkuName(
    orderItem,
    kuaishouCloudFulfillment,
    kuaishouCloudSource,
  )
  const product = buildClaimProductPayload(displaySkuName, orderItem, kuaishouCloudSource)

  return {
    tokenStatus: claimToken.status,
    claimUrl: buildClaimUrl(claimToken.token),
    flowType: 'kuaishou_cloud',
    claimIdentity,
    task: {
      taskId: task.id,
      taskNo: task.task_no,
      status: task.task_status,
      executorKey: task.executor_key || '',
      requiresSupportReview: false,
      expiresAt: claimToken.expired_at,
      claimedAt: task.claimed_at,
      roleConfirmedAt: task.role_confirmed_at,
      redeemedAt: task.redeemed_at,
      loginType: task.login_type,
      lastError: task.last_error,
      runtimeSessionId: task.runtime_session_id,
    },
    order: {
      orderId: order.id,
      platform: order.platform,
      platformOrderId: order.platform_order_id,
      payStatus: order.pay_status,
      orderStatus: order.order_status,
      totalAmount: formatFenToAmount(order.total_amount),
      totalAmountFen: normalizeFen(order.total_amount),
      currency: order.currency,
    },
    orderItem: {
      orderItemId: orderItem.id,
      skuCode: orderItem.sku_code,
      skuName: displaySkuName,
      quantity: orderItem.quantity,
    },
    product,
    session: null as null,
    kuaishouCloudFulfillment,
    kuaishouFeifei: null as null,
    result: task.redeemed_at
      ? {
          resultCode: String(task.result_code || ''),
          resultMessage: String(task.result_message || ''),
          screenshotReady: false,
          screenshotUrl: '',
        }
      : null,
  }
}

function buildAffiliateDashClaimDetailPayload({
  claimToken,
  task,
  order,
  orderItem,
}: ClaimContext) {
  const context = parseTaskContext(task)
  const claimIdentity = buildClaimIdentityPayload(context.claimIdentity)
  const flow = normalizeAffiliateDashFlow(context.affiliateDash)
  const productTitle = String(
    flow.productName || orderItem.sku_name || orderItem.sku_code || '',
  ).trim()
  const product = {
    title: productTitle,
    skuCode: String(orderItem.sku_code || '').trim(),
    quantity: Math.max(1, Number(orderItem.quantity || 1) || 1),
    isBundle: false,
    items: [
      {
        cloudSkuId: 0,
        name: productTitle,
        quantity: Math.max(1, Number(orderItem.quantity || 1) || 1),
      },
    ],
  }

  return {
    tokenStatus: claimToken.status,
    claimUrl: buildClaimUrl(claimToken.token),
    flowType: 'affiliate_dash',
    claimIdentity,
    task: {
      taskId: task.id,
      taskNo: task.task_no,
      status: task.task_status,
      executorKey: task.executor_key || '',
      requiresSupportReview: false,
      expiresAt: claimToken.expired_at,
      claimedAt: task.claimed_at,
      roleConfirmedAt: task.role_confirmed_at,
      redeemedAt: task.redeemed_at,
      loginType: task.login_type,
      lastError: task.last_error,
      runtimeSessionId: task.runtime_session_id,
    },
    order: {
      orderId: order.id,
      platform: order.platform,
      platformOrderId: order.platform_order_id,
      payStatus: order.pay_status,
      orderStatus: order.order_status,
      totalAmount: formatFenToAmount(order.total_amount),
      totalAmountFen: normalizeFen(order.total_amount),
      currency: order.currency,
    },
    orderItem: {
      orderItemId: orderItem.id,
      skuCode: orderItem.sku_code,
      skuName: productTitle,
      quantity: orderItem.quantity,
    },
    product,
    session: null as null,
    kuaishouCloudFulfillment: null as null,
    kuaishouFeifei: null as null,
    affiliateDash: flow,
    result: task.redeemed_at
      ? {
          resultCode: String(task.result_code || ''),
          resultMessage: String(task.result_message || ''),
          screenshotReady: false,
          screenshotUrl: '',
        }
      : null,
  }
}

function buildKuaishouFeifeiClaimDetailPayload({
  claimToken,
  task,
  order,
  orderItem,
}: ClaimContext) {
  const context = parseTaskContext(task)
  const claimIdentity = buildClaimIdentityPayload(context.claimIdentity)
  const expectedUid = getClaimIdentityFromContext(context).expectedUid
  const flow = mapClaimKuaishouFeifeiFulfillment(context.kuaishouFeifei, expectedUid)
  const product = {
    title: String(flow.productName || orderItem.sku_name || orderItem.sku_code || '').trim(),
    skuCode: String(orderItem.sku_code || '').trim(),
    quantity: Math.max(1, Number(orderItem.quantity || 1) || 1),
    isBundle: false,
    items: [
      {
        cloudSkuId: 0,
        name: String(flow.productName || orderItem.sku_name || orderItem.sku_code || '').trim(),
        quantity: Math.max(1, Number(orderItem.quantity || 1) || 1),
      },
    ],
  }

  return {
    tokenStatus: claimToken.status,
    claimUrl: buildClaimUrl(claimToken.token),
    flowType: 'kuaishou_feifei',
    claimIdentity,
    task: {
      taskId: task.id,
      taskNo: task.task_no,
      status: task.task_status,
      executorKey: task.executor_key || '',
      requiresSupportReview: false,
      expiresAt: claimToken.expired_at,
      claimedAt: task.claimed_at,
      roleConfirmedAt: task.role_confirmed_at,
      redeemedAt: task.redeemed_at,
      loginType: task.login_type,
      lastError: task.last_error,
      runtimeSessionId: task.runtime_session_id,
    },
    order: {
      orderId: order.id,
      platform: order.platform,
      platformOrderId: order.platform_order_id,
      payStatus: order.pay_status,
      orderStatus: order.order_status,
      totalAmount: formatFenToAmount(order.total_amount),
      totalAmountFen: normalizeFen(order.total_amount),
      currency: order.currency,
    },
    orderItem: {
      orderItemId: orderItem.id,
      skuCode: orderItem.sku_code,
      skuName: product.title,
      quantity: orderItem.quantity,
    },
    product,
    session: null as null,
    kuaishouCloudFulfillment: null as null,
    kuaishouFeifei: flow,
    result: task.redeemed_at
      ? {
          resultCode: String(task.result_code || ''),
          resultMessage: String(task.result_message || ''),
          screenshotReady: false,
          screenshotUrl: '',
        }
      : null,
  }
}

function mapClaimKuaishouFeifeiFulfillment(value: unknown, expectedUid = '') {
  const source = isPlainObject(value) ? value : {}
  const h5 = isPlainObject(source.h5) ? source.h5 : {}
  const entryUrl = String(h5.entryUrl || '').trim()
  const rechargeUrl = String(h5.rechargeUrl || '').trim()
  const h5UrlWithUid = resolveKuaishouFeifeiH5UrlWithUid(source, expectedUid)

  return {
    flowType: 'kuaishou_feifei',
    productCode: String(source.productCode || '').trim(),
    productName: String(source.productName || '').trim(),
    platformOrderNo: String(source.platformOrderNo || '').trim(),
    orderNo: String(source.orderNo || '').trim(),
    rechargeStatus: Number(source.rechargeStatus || 0) || 0,
    rechargeStatusLabel: String(source.rechargeStatusLabel || '').trim(),
    rechargeResultMessage: String(source.rechargeResultMessage || '').trim(),
    claimUrl: String(source.claimUrl || '').trim(),
    consumeStatus: String(source.consumeStatus || 'pending').trim(),
    h5: {
      entryUrl,
      rechargeUrl,
    },
    h5UrlWithUid: expectedUid ? h5UrlWithUid : '',
    lastSyncedAt: source.lastSyncedAt || null,
  }
}

function resolveClaimOrderItemDisplaySkuName(
  orderItem: OrderItemRow,
  kuaishouCloudFulfillment: JsonObject | null,
  kuaishouCloudSource: JsonObject = {},
) {
  const fulfillment = isPlainObject(kuaishouCloudFulfillment) ? kuaishouCloudFulfillment : {}
  const binding = isPlainObject(fulfillment.binding) ? fulfillment.binding : {}
  const ticket = isPlainObject(fulfillment.ticket) ? fulfillment.ticket : {}
  const skuCode = String(orderItem.sku_code || '').trim()
  const rawSkuName = String(orderItem.sku_name || '').trim()
  const candidates = [
    kuaishouCloudSource.internalSkuName,
    rawSkuName,
    ticket.goodsTitle,
    binding.skuName,
    skuCode,
  ]

  for (const value of candidates) {
    const normalized = String(value || '').trim()
    if (normalized && !isOrderItemIdentifierName(normalized, skuCode)) {
      return normalized
    }
  }

  return rawSkuName || skuCode
}

function buildClaimProductPayload(
  displaySkuName: string,
  orderItem: OrderItemRow,
  kuaishouCloudSource: JsonObject = {},
) {
  const binding = isPlainObject(kuaishouCloudSource.binding) ? kuaishouCloudSource.binding : {}
  const deliveryItems = normalizeClaimDeliveryItems(kuaishouCloudSource.deliveryItems, binding)
    .map((item) => ({
      cloudSkuId: item.cloudSkuId,
      name: item.cloudSkuName,
      quantity: item.quantity,
    }))
    .filter((item) => item.name)
  const normalizedDeliveryItems =
    deliveryItems.length > 0
      ? mergeClaimProductItems(deliveryItems)
      : [
          {
            cloudSkuId: 0,
            name: displaySkuName,
            quantity: Math.max(1, Number(orderItem.quantity || 1) || 1),
          },
        ]

  return {
    title: displaySkuName,
    skuCode: String(orderItem.sku_code || '').trim(),
    quantity: Math.max(1, Number(orderItem.quantity || 1) || 1),
    isBundle: normalizedDeliveryItems.length > 1,
    items: normalizedDeliveryItems,
  }
}

function mergeClaimProductItems(
  items: Array<{ cloudSkuId: number; name: string; quantity: number }>,
) {
  const merged = new Map<string, { cloudSkuId: number; name: string; quantity: number }>()

  for (const item of items) {
    const key = item.cloudSkuId > 0 ? `id:${item.cloudSkuId}` : `name:${item.name}`
    const existing = merged.get(key)
    if (existing) {
      existing.quantity += item.quantity
      existing.name = existing.name || item.name
      continue
    }

    merged.set(key, { ...item })
  }

  return Array.from(merged.values())
}

function resolveClaimKuaishouCloudSource(task: TaskRow): JsonObject {
  if (String(task?.executor_key || '').trim() !== 'kuaishou_ct_assisted') {
    return {}
  }

  const source = parseTaskContext(task).kuaishouCloudFulfillment
  return isPlainObject(source) ? source : {}
}

export function mapClaimKuaishouCloudFulfillment(task: TaskRow, order: OrderRow) {
  if (String(task?.executor_key || '').trim() !== 'kuaishou_ct_assisted') {
    return null
  }

  const source = parseTaskContext(task).kuaishouCloudFulfillment
  if (!source || typeof source !== 'object') {
    return null
  }

  const binding = asJsonObject(source.binding)
  const role = asJsonObject(source.role)
  const purchase = asJsonObject(source.purchase)
  const ticket = asJsonObject(source.ticket)
  const dispatch = asJsonObject(source.dispatch)
  const returnNumber = asJsonObject(source.returnNumber)
  const consume = asJsonObject(source.consume)
  const roleName = String(role.name || binding.roleName || '').trim()
  const roleId = String(role.rid || binding.roleId || '').trim()
  const defaultRoleName = String(role.defaultName || role.defaultRoleName || '').trim()
  const defaultRoleId = String(role.defaultRid || role.defaultRoleId || '').trim()
  const isDefaultRole =
    Boolean(roleName || roleId) &&
    isSameClaimKuaishouCloudRole(
      { name: roleName, rid: roleId },
      { name: defaultRoleName, rid: defaultRoleId },
    )

  return {
    flowType: 'kuaishou_cloud',
    shopId: String(order?.shop_id || '').trim(),
    shopName: String(order?.shop_name || '').trim(),
    guideImages: [],
    ticket: {
      code: String(ticket.code || '').trim(),
      status: String(ticket.status || 'pending').trim() || 'pending',
      capturedAt: ticket.capturedAt || null,
      verifiedAt: ticket.verifiedAt || null,
      goodsTitle: String(ticket.goodsTitle || '').trim(),
      leftCount: Number(ticket.leftCount || 0) || 0,
    },
    binding: {
      prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
      cloudSourceKeys: Array.isArray(binding.cloudSourceKeys)
        ? binding.cloudSourceKeys
            .map((value: unknown) => String(value || '').trim())
            .filter(Boolean)
        : [],
      resolvedSourceKey: String(binding.resolvedSourceKey || '').trim(),
      skuId: Number(binding.skuId || 0) || 0,
      skuName: String(binding.skuName || '').trim(),
      vnKey: String(binding.vnKey || '').trim(),
      vnId: Number(binding.vnId || 0) || 0,
      bindUrl: String(binding.bindUrl || '').trim(),
      bindPreparedAt: binding.bindPreparedAt || null,
      bindExpiresAt: binding.bindExpiresAt || null,
      bindProbeAt: binding.bindProbeAt || null,
      bindProbeStatus: String(binding.bindProbeStatus || '').trim(),
      bindProbeMessage: String(binding.bindProbeMessage || '').trim(),
      vnPhone: String(binding.vnPhone || '').trim(),
      roleName: String(binding.roleName || '').trim(),
      roleId: String(binding.roleId || '').trim(),
    },
    role: {
      status: String(role.status || 'pending').trim() || 'pending',
      name: roleName,
      rid: roleId,
      refreshedAt: role.refreshedAt || null,
      errorMessage: String(role.errorMessage || '').trim(),
      defaultName: defaultRoleName,
      defaultRid: defaultRoleId,
      defaultCapturedAt: role.defaultCapturedAt || null,
      defaultCaptureStatus: String(role.defaultCaptureStatus || '').trim(),
      defaultErrorMessage: String(role.defaultErrorMessage || '').trim(),
      isDefaultRole,
    },
    purchase: {
      autoBuyEnabled: purchase.autoBuyEnabled !== false,
      minAssetReserve: Number(purchase.minAssetReserve || 0) || 0,
      usedKnapsack: purchase.usedKnapsack === true,
      purchaseTriggered: purchase.purchaseTriggered === true,
      assetBefore: Number(purchase.assetBefore || 0) || 0,
      assetAfter: Number(purchase.assetAfter || 0) || 0,
      purchaseAt: purchase.purchaseAt || null,
    },
    dispatch: {
      status: String(dispatch.status || 'pending').trim() || 'pending',
      dispatchAt: dispatch.dispatchAt || null,
      failedAt: dispatch.failedAt || null,
      failedStage: String(dispatch.failedStage || '').trim(),
      errorCode: String(dispatch.errorCode || '').trim(),
      errorMessage: String(dispatch.errorMessage || '').trim(),
      note: String(dispatch.note || '').trim(),
    },
    returnNumber: {
      status: String(returnNumber.status || 'pending').trim() || 'pending',
      returnedAt: returnNumber.returnedAt || null,
    },
    consume: {
      status: String(consume.status || 'pending').trim() || 'pending',
      shopId: String(consume.shopId || '').trim(),
      shopName: String(consume.shopName || '').trim(),
      consumedAt: consume.consumedAt || null,
      errorMessage: String(consume.errorMessage || '').trim(),
    },
  }
}

function parseTaskContext(task: TaskRow): JsonObject {
  return parseTaskContextValue(task)
}

function isOrderItemIdentifierName(value: string, skuCode: string) {
  const normalized = String(value || '').trim()
  if (!normalized) {
    return true
  }

  if (isClaimPlaceholderSkuName(normalized)) {
    return true
  }

  const normalizedSkuCode = String(skuCode || '').trim()
  if (normalizedSkuCode && normalized === normalizedSkuCode && /^\d{8,}$/.test(normalizedSkuCode)) {
    return true
  }

  return /^\d{8,}$/.test(normalized)
}

function isPlainObject(value: unknown): value is JsonObject {
  return Object.prototype.toString.call(value) === '[object Object]'
}

function isSameClaimKuaishouCloudRole(
  left: { name?: unknown; rid?: unknown },
  right: { name?: unknown; rid?: unknown },
) {
  const leftRid = String(left.rid || '').trim()
  const rightRid = String(right.rid || '').trim()
  if (leftRid && rightRid) {
    return leftRid === rightRid
  }

  const leftName = normalizeClaimRoleNameForCompare(left.name)
  const rightName = normalizeClaimRoleNameForCompare(right.name)
  return Boolean(leftName && rightName && leftName === rightName)
}

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

function normalizeClaimDeliveryItems(value: unknown, binding: JsonObject) {
  const rawItems = Array.isArray(value) ? value : []
  const items = rawItems
    .map((item) => normalizeClaimDeliveryItem(item))
    .filter((item): item is { cloudSkuId: number; cloudSkuName: string; quantity: number } =>
      Boolean(item),
    )

  if (items.length > 0) {
    return mergeClaimDeliveryItems(items)
  }

  const cloudSkuId = Number(binding.skuId || 0) || 0
  if (!cloudSkuId) {
    return []
  }

  return [
    {
      cloudSkuId,
      cloudSkuName: String(binding.skuName || '').trim(),
      quantity: 1,
    },
  ]
}

function normalizeClaimDeliveryItem(value: unknown) {
  const source = isPlainObject(value) ? value : {}
  const cloudSkuId = Number(source.cloudSkuId || source.skuId || 0) || 0

  if (!Number.isInteger(cloudSkuId) || cloudSkuId <= 0) {
    return null
  }

  const quantity = Number(source.quantity || 1) || 1
  return {
    cloudSkuId,
    cloudSkuName: String(source.cloudSkuName || source.skuName || '').trim(),
    quantity: Number.isInteger(quantity) && quantity > 0 ? quantity : 1,
  }
}

function mergeClaimDeliveryItems(
  items: Array<{ cloudSkuId: number; cloudSkuName: string; quantity: number }>,
) {
  const merged = new Map<number, { cloudSkuId: number; cloudSkuName: string; quantity: number }>()

  for (const item of items) {
    const existing = merged.get(item.cloudSkuId)
    if (existing) {
      existing.quantity += item.quantity
      existing.cloudSkuName = existing.cloudSkuName || item.cloudSkuName
      continue
    }

    merged.set(item.cloudSkuId, { ...item })
  }

  return Array.from(merged.values())
}

function isClaimPlaceholderSkuName(value: string) {
  return ['测试链接专用', '商品领取', '测试商品'].includes(String(value || '').trim())
}

async function expireClaimContext(claimToken: ClaimTokenRow, task: TaskRow) {
  const now = nowIso()
  const nextClaimToken = await updateClaimToken(claimToken.id, {
    status: TASK_STATUS.EXPIRED,
    updated_at: now,
  })

  let nextTask = task

  if (!isTaskFinalStatus(task.task_status)) {
    nextTask =
      (await updateTask(task.id, {
        task_status: TASK_STATUS.EXPIRED,
        user_action_status: TASK_STATUS.EXPIRED,
        last_error: '领取链接已过期',
        updated_at: now,
      })) || task
  }

  return {
    claimToken: nextClaimToken,
    task: nextTask,
  }
}
