import {
  createOrder,
  findOrderByPlatformOrderId,
  findOrderByPlatformOrderIdCandidates,
  updateOrder,
} from '../../repositories/order-repo.js'
import { replaceOrderItems } from '../../repositories/order-item-repo.js'
import { getClaimTokenById } from '../../repositories/claim-token-repo.js'
import { buildClaimUrl } from '../claim/claim-service.js'
import { syncDeliveryTasksForOrder } from './delivery-task-service.js'
import { ensureAgisoXianyuClaimMessageDeliveredForTask } from '../platforms/agiso/xianyu/message-service.js'
import { resolveOrderItemForFulfillment } from './product-match-service.js'
import { nowIso } from '../../utils/time.js'
import { logWebhook } from '../../utils/logger.js'
import type { OrderItemRow, OrderRow, TaskRow } from '../../types/repository-rows.js'

type SourceOrderItem = {
  quantity?: number
  spec?: Record<string, unknown>
  snapshot?: Record<string, unknown>
  [key: string]: unknown
}

type FulfillmentOrderItem = SourceOrderItem & {
  isConfigured: boolean
  skuCode: string
  skuName: string
  quantity: number
  spec?: Record<string, unknown>
  snapshot?: Record<string, unknown>
}

type SourceOrderEvent = {
  provider: string
  platform: string
  shopId: string
  shopIdAliases?: string[]
  shopName: string
  platformOrderId: string
  orderStatus: string
  payStatus: string
  buyerId: string
  buyerName: string
  receiverContact: string
  totalAmount: number | string
  currency: string
  rawPayload: unknown
  paidAt?: string | null
  items: SourceOrderItem[]
}

type UpsertOrderSourceOptions = {
  sourceLabel?: string
}

type MessageDeliveryResult = {
  taskId: number
  [key: string]: unknown
}

type UpsertOrderIgnoredResult = {
  ignored: true
  ignoreReason: string
  order: null
  orderItems: []
  tasks: []
  messageDeliveries: []
}

type UpsertOrderResult = {
  ignored?: false
  ignoreReason?: string
  order: OrderRow
  orderItems: OrderItemRow[]
  tasks: TaskRow[]
  messageDeliveries: MessageDeliveryResult[]
} | UpsertOrderIgnoredResult

type OrderStateInput = {
  order_status?: string
  orderStatus?: string
  pay_status?: string
  payStatus?: string
  paid_at?: string | null
  paidAt?: string | null
}

type OrderStateMergeResult = {
  orderStatus: string
  payStatus: string
  paidAt: string | null
}

type StatusPriorityMap = Record<string, number>

export async function upsertOrderFromWebhook(event: SourceOrderEvent): Promise<UpsertOrderResult> {
  return upsertOrderFromSource(event, { sourceLabel: 'webhook' })
}

export async function upsertOrderFromSource(
  event: SourceOrderEvent,
  { sourceLabel = 'source' }: UpsertOrderSourceOptions = {},
): Promise<UpsertOrderResult> {
  const now = nowIso()
  const exactExisting = await findOrderByPlatformOrderId({
    provider: event.provider,
    platform: event.platform,
    shopId: event.shopId,
    platformOrderId: event.platformOrderId,
  })
  const existing = exactExisting || await findOrderByPlatformOrderIdCandidates({
    provider: event.provider,
    platform: event.platform,
    shopIds: resolveEventShopIdCandidates(event),
    platformOrderId: event.platformOrderId,
  })

  logWebhook('[order-service]', `开始处理 ${sourceLabel} 订单 upsert`, {
    provider: event.provider,
    platform: event.platform,
    shopId: event.shopId,
    shopName: event.shopName,
    platformOrderId: event.platformOrderId,
    existingOrderId: existing?.id || null,
  })

  const resolvedItems = await Promise.all(
    event.items.map((item) => resolveOrderItemForFulfillment({
      provider: event.provider,
      platform: event.platform,
      shopId: event.shopId,
      shopIdAliases: event.shopIdAliases,
      item,
    })),
  )
  const configuredItems = (resolvedItems as FulfillmentOrderItem[]).filter((item) => item.isConfigured)

  if (configuredItems.length === 0) {
    logWebhook('[order-service]', `${sourceLabel} 订单已忽略：未命中任何已配置履约商品`, {
      provider: event.provider,
      platform: event.platform,
      shopId: event.shopId,
      shopName: event.shopName,
      platformOrderId: event.platformOrderId,
      existingOrderId: existing?.id || null,
      rawItemCount: event.items.length,
    })

    return {
      ignored: true,
      ignoreReason: 'unconfigured_items',
      order: null,
      orderItems: [],
      tasks: [],
      messageDeliveries: [],
    }
  }

  const basePayload = {
    provider: event.provider,
    platform: event.platform,
    shopId: event.shopId,
    shopName: event.shopName,
    platformOrderId: event.platformOrderId,
    orderStatus: event.orderStatus,
    payStatus: event.payStatus,
    buyerId: event.buyerId,
    buyerName: event.buyerName,
    receiverContact: event.receiverContact,
    totalAmount: event.totalAmount,
    currency: event.currency,
    rawPayloadJson: JSON.stringify(event.rawPayload),
    paidAt: event.paidAt,
  }
  const mergedPayload = mergeWebhookOrderState(existing, basePayload)

  const order = existing
    ? await updateOrder(existing.id, {
      ...basePayload,
      ...mergedPayload,
      updatedAt: now,
    })
    : await createOrder({
      ...basePayload,
      ...mergedPayload,
      createdAt: now,
      updatedAt: now,
    })

  if (!order) {
    throw new Error('订单写入失败')
  }

  const orderItems = await replaceOrderItems(
    order.id,
    configuredItems.map((item) => ({
      skuCode: item.skuCode,
      skuName: item.skuName,
      quantity: item.quantity,
      specJson: JSON.stringify(item.spec || {}),
      itemSnapshotJson: JSON.stringify(item.snapshot || item.spec || {}),
      createdAt: now,
      updatedAt: now,
    })),
  )

  const tasks = await syncDeliveryTasksForOrder(order, orderItems)
  const messageDeliveries: MessageDeliveryResult[] = []

  logWebhook('[order-service]', `${sourceLabel} 订单 upsert 完成`, {
    orderId: order.id,
    provider: order.provider,
    platform: order.platform,
    shopId: order.shop_id,
    shopName: order.shop_name,
    platformOrderId: order.platform_order_id,
    orderItemCount: orderItems.length,
    taskCount: tasks.length,
    resolvedSkuCodes: configuredItems.map((item) => item.skuCode),
  })

  for (const task of tasks) {
    const isClaimLinkTask = String(task.task_status || '') === 'link_generated'
      || (
        String(task.executor_key || '').trim() === 'kuaishou_ct_assisted'
        && ['pending_binding_prepare', 'waiting_binding', 'role_confirmed', 'dispatched_pending_return'].includes(String(task.task_status || '').trim())
      )

    if (
      event.provider !== 'agiso'
      || event.platform !== 'xianyu'
      || !isClaimLinkTask
      || !task.primary_claim_token_id
    ) {
      continue
    }

    const claimToken = await getClaimTokenById(task.primary_claim_token_id)
    if (!claimToken) {
      continue
    }

    const result = await ensureAgisoXianyuClaimMessageDeliveredForTask({
      order,
      task,
      claimUrl: buildClaimUrl(claimToken.token),
      expiredAt: claimToken.expired_at,
    })
    messageDeliveries.push({
      taskId: task.id,
      ...result,
    })
  }

  return {
    order,
    orderItems,
    tasks,
    messageDeliveries,
  }
}

function resolveEventShopIdCandidates(event: Pick<SourceOrderEvent, 'shopId' | 'shopIdAliases'>): string[] {
  return [...new Set([
    String(event?.shopId || '').trim(),
    ...(Array.isArray(event?.shopIdAliases) ? event.shopIdAliases : []).map((item) => String(item || '').trim()),
  ].filter(Boolean))]
}

const ORDER_STATUS_PRIORITY = {
  created: 0,
  paid: 1,
  closed: 2,
  refunded: 3,
}

const PAY_STATUS_PRIORITY = {
  unpaid: 0,
  failed: 0,
  paid: 1,
  refunded: 2,
}

export function mergeWebhookOrderState(
  existing: OrderStateInput | null | undefined,
  incoming: OrderStateInput | null | undefined,
): OrderStateMergeResult {
  const orderStatus = chooseHigherPriorityStatus(
    normalizeStateValue(existing?.order_status ?? existing?.orderStatus),
    normalizeStateValue(incoming?.orderStatus ?? incoming?.order_status),
    ORDER_STATUS_PRIORITY,
  ) || normalizeStateValue(incoming?.orderStatus ?? incoming?.order_status) || 'created'

  const payStatus = chooseHigherPriorityStatus(
    normalizeStateValue(existing?.pay_status ?? existing?.payStatus),
    normalizeStateValue(incoming?.payStatus ?? incoming?.pay_status),
    PAY_STATUS_PRIORITY,
  ) || normalizeStateValue(incoming?.payStatus ?? incoming?.pay_status) || 'unpaid'

  return {
    orderStatus,
    payStatus,
    paidAt: resolveMergedPaidAt({
      existingPaidAt: existing?.paid_at ?? existing?.paidAt ?? null,
      incomingPaidAt: incoming?.paidAt ?? incoming?.paid_at ?? null,
      payStatus,
    }),
  }
}

function chooseHigherPriorityStatus(
  existingValue: string,
  incomingValue: string,
  priorityMap: StatusPriorityMap,
): string {
  const existingPriority = resolveStatusPriority(existingValue, priorityMap)
  const incomingPriority = resolveStatusPriority(incomingValue, priorityMap)

  if (incomingPriority > existingPriority) {
    return incomingValue
  }

  if (existingPriority >= incomingPriority) {
    return existingValue
  }

  return incomingValue || existingValue
}

function resolveStatusPriority(value: string, priorityMap: StatusPriorityMap): number {
  return Number(priorityMap[normalizeStateValue(value)] ?? -1)
}

function normalizeStateValue(value: unknown): string {
  return String(value || '').trim().toLowerCase()
}

function resolveMergedPaidAt({
  existingPaidAt,
  incomingPaidAt,
  payStatus,
}: {
  existingPaidAt: string | null
  incomingPaidAt: string | null
  payStatus: string
}): string | null {
  if (!['paid', 'refunded'].includes(normalizeStateValue(payStatus))) {
    return null
  }

  const existingTime = parseDateValue(existingPaidAt)
  const incomingTime = parseDateValue(incomingPaidAt)

  if (existingTime && incomingTime) {
    return existingTime <= incomingTime ? existingPaidAt : incomingPaidAt
  }

  return existingPaidAt || incomingPaidAt || null
}

function parseDateValue(value: unknown): number | null {
  const text = String(value || '').trim()

  if (!text) {
    return null
  }

  const timestamp = Date.parse(text)
  return Number.isFinite(timestamp) ? timestamp : null
}
