import { createTask, listTasksByOrderId, updateTask } from '../../repositories/task-repo.js'
import { createTaskEvent } from '../../repositories/task-event-repo.js'
import { getFulfillmentProfileByKey } from '../../repositories/fulfillment-profile-repo.js'
import { createTaskClaimToken } from '../claim/claim-service.js'
import { notifyTaskAutoManualReview } from '../notification/domain-notifications.js'
import {
  planFulfillmentTaskForOrderItem,
  type FulfillmentBindingLike,
} from '../fulfillment/planner.js'
import { preparePaidFulfillmentTask } from '../fulfillment/executors/registry.js'
import type { FulfillmentPrepareDeps } from '../fulfillment/executors/types.js'
import { nowIso } from '../../utils/time.js'
import { randomId } from '../../utils/random.js'
import { logInfo, logWarn } from '../../utils/logger.js'
import { TASK_STATUS, resolveInitialPaidTaskStatus } from '../../domain/task-status.js'
import type { JsonObject } from '../../types/json.js'
import type { OrderItemRow, OrderRow, TaskRow } from '../../types/repository/rows.js'

type ClaimTokenLike = {
  token: string
  expired_at: string
  [key: string]: unknown
}

type DeliveryTaskRow = TaskRow & {
  skuCode?: string
  skuName?: string
}

type DeliveryTaskDeps = {
  createTask?: typeof createTask
  listTasksByOrderId?: typeof listTasksByOrderId
  updateTask?: typeof updateTask
  getFulfillmentProfileByKey?: (profileKey: string) => Promise<FulfillmentBindingLike | null>
  createTaskClaimToken?: (taskId: number | string) => Promise<ClaimTokenLike>
  notifyTaskAutoManualReview?: (payload: {
    task: unknown
    reason: string
    source: string
  }) => Promise<unknown> | unknown
  nowIso?: () => string
  randomId?: (prefix?: string) => string
  createTaskEvent?: typeof createTaskEvent
  logInfo?: typeof logInfo
  logWarn?: typeof logWarn
}

export async function syncDeliveryTasksForOrder(
  order: OrderRow,
  orderItems: OrderItemRow[],
): Promise<TaskRow[]> {
  return syncDeliveryTasksForOrderWithDeps(order, orderItems)
}

export async function syncDeliveryTasksForOrderWithDeps(
  order: OrderRow,
  orderItems: OrderItemRow[],
  deps: DeliveryTaskDeps = {},
): Promise<TaskRow[]> {
  const {
    createTask: createDeliveryTask = createTask,
    listTasksByOrderId: listTasks = listTasksByOrderId,
    updateTask: updateDeliveryTask = updateTask,
    getFulfillmentProfileByKey: getProfileByKey = getFulfillmentProfileByKey,
    createTaskClaimToken: createClaimToken = createTaskClaimToken,
    notifyTaskAutoManualReview: notifyManualReview = notifyTaskAutoManualReview,
    nowIso: getNowIso = nowIso,
    randomId: createRandomId = randomId,
    createTaskEvent: appendTaskEvent = createTaskEvent,
    logInfo: writeInfoLog = logInfo,
    logWarn: writeWarnLog = logWarn,
  } = deps

  const runtimeDeps: FulfillmentPrepareDeps = {
    updateTask: updateDeliveryTask,
    createTaskClaimToken: createClaimToken,
    notifyTaskAutoManualReview: notifyManualReview,
    nowIso: getNowIso,
  }

  const existingTasks = await listTasks(order.id)
  const tasks: DeliveryTaskRow[] = [...existingTasks]
  const existingTaskKeys = new Set(existingTasks.map(buildTaskUnitKey))

  writeInfoLog('[delivery-task-service]', '开始核对订单履约任务完整性', {
    ...buildOrderLogContext(order),
    orderItemCount: orderItems.length,
    existingTaskCount: existingTasks.length,
  })

  for (const item of orderItems) {
    const quantity = Math.max(1, Number(item.quantity || 1))
    const missingUnitIndexes = Array.from({ length: quantity }, (_, index) => index + 1).filter(
      (unitIndex) => !existingTaskKeys.has(buildOrderItemUnitKey(item.id, unitIndex)),
    )

    if (missingUnitIndexes.length === 0) {
      continue
    }

    let plan
    try {
      plan = await planFulfillmentTaskForOrderItem({
        order,
        item,
        getProfileByKey,
      })
    } catch (error) {
      writeWarnLog('[delivery-task-service]', '订单项履约任务规划执行异常', {
        ...buildOrderLogContext(order),
        ...buildOrderItemLogContext(item),
        missingUnitIndexes,
        planningDiagnostic: buildPlanningDiagnostic(item),
        error: error instanceof Error ? error.message : String(error),
      })
      throw error
    }

    if (!plan) {
      writeWarnLog('[delivery-task-service]', '订单项已配置履约路由但无法生成任务计划', {
        ...buildOrderLogContext(order),
        ...buildOrderItemLogContext(item),
        missingUnitIndexes,
        planningDiagnostic: buildPlanningDiagnostic(item),
      })
      continue
    }

    for (const unitIndex of missingUnitIndexes) {
      const createdAt = getNowIso()
      const initialStatus =
        order.pay_status === 'paid'
          ? resolvePaidTaskStatus(plan.profile)
          : TASK_STATUS.PENDING_PAYMENT

      let createdFresh = true
      let task: TaskRow | null
      try {
        task = await createDeliveryTask({
          orderId: order.id,
          orderItemId: item.id,
          unitIndex,
          provider: order.provider,
          platform: order.platform,
          shopId: order.shop_id,
          shopName: order.shop_name,
          platformOrderId: order.platform_order_id,
          taskNo: createRandomId('DT'),
          profileId: plan.profileId,
          executorKey: plan.executorKey,
          taskStatus: initialStatus,
          deliveryStatus: 'pending',
          resultCode: '',
          resultMessage: '',
          claimToken: '',
          claimExpiresAt: null,
          automationMode: plan.autoDispatch ? 'automatic' : 'manual',
          requiresClaim: plan.requiresClaim,
          userActionStatus: plan.requiresClaim ? 'pending_claim' : 'not_required',
          attemptCount: 0,
          lastError: '',
          contextJson: JSON.stringify(plan.context),
          createdAt,
          updatedAt: createdAt,
        })
      } catch (error) {
        const concurrentTasks = await listTasks(order.id)
        task =
          concurrentTasks.find(
            (candidate) =>
              buildTaskUnitKey(candidate) === buildOrderItemUnitKey(item.id, unitIndex),
          ) || null
        if (!task) {
          writeWarnLog('[delivery-task-service]', '履约任务创建执行异常', {
            ...buildOrderLogContext(order),
            ...buildOrderItemLogContext(item),
            unitIndex,
            profileId: plan.profileId,
            profileKey: plan.profileKey,
            executorKey: plan.executorKey,
            error: error instanceof Error ? error.message : String(error),
          })
          throw error
        }

        createdFresh = false
        writeInfoLog('[delivery-task-service]', '并发同步已创建相同履约任务，本次直接复用', {
          ...buildOrderLogContext(order),
          ...buildOrderItemLogContext(item),
          unitIndex,
          taskId: task.id,
          taskNo: task.task_no,
        })
      }

      if (task) {
        const deliveryTask = {
          ...task,
          skuCode: item.sku_code,
          skuName: item.sku_name,
        }
        tasks.push(deliveryTask)
        existingTaskKeys.add(buildTaskUnitKey(task))
        if (createdFresh) {
          await appendTaskEvent(
            task.id,
            'fulfillment_task_planned',
            {
              source: 'order_task_reconciliation',
              orderId: order.id,
              platformOrderId: order.platform_order_id,
              orderItemId: item.id,
              unitIndex,
              skuCode: item.sku_code,
              skuName: item.sku_name,
              profileId: plan.profileId,
              profileKey: plan.profileKey,
              executorKey: plan.executorKey,
              automationMode: plan.autoDispatch ? 'automatic' : 'manual',
              initialStatus,
            },
            createdAt,
          )
        }
      } else {
        writeWarnLog('[delivery-task-service]', '履约任务创建未返回记录', {
          ...buildOrderLogContext(order),
          ...buildOrderItemLogContext(item),
          unitIndex,
          profileId: plan.profileId,
          profileKey: plan.profileKey,
          executorKey: plan.executorKey,
        })
      }
    }
  }

  writeInfoLog('[delivery-task-service]', '订单履约任务完整性核对完成', {
    ...buildOrderLogContext(order),
    existingTaskCount: existingTasks.length,
    createdTaskCount: tasks.length - existingTasks.length,
    totalTaskCount: tasks.length,
    expectedTaskCount: orderItems.reduce(
      (total, item) => total + Math.max(1, Number(item.quantity || 1)),
      0,
    ),
  })

  if (order.pay_status !== 'paid') {
    return tasks
  }

  const preparedTasks = await Promise.all(
    tasks.map((task) => preparePaidTaskWithDiagnostics(order, task, runtimeDeps, writeWarnLog)),
  )
  return preparedTasks.filter(isTaskRow)
}

async function preparePaidTaskWithDiagnostics(
  order: OrderRow,
  task: TaskRow,
  deps: FulfillmentPrepareDeps,
  writeWarnLog: typeof logWarn,
) {
  try {
    const prepared = await preparePaidFulfillmentTask(task, deps)
    if (!prepared) {
      writeWarnLog('[delivery-task-service]', '已支付履约任务准备未返回任务记录', {
        ...buildOrderLogContext(order),
        taskId: task.id,
        taskNo: task.task_no,
        orderItemId: task.order_item_id,
        unitIndex: task.unit_index,
        executorKey: task.executor_key,
        taskStatus: task.task_status,
      })
    }
    return prepared
  } catch (error) {
    writeWarnLog('[delivery-task-service]', '已支付履约任务准备执行异常', {
      ...buildOrderLogContext(order),
      taskId: task.id,
      taskNo: task.task_no,
      orderItemId: task.order_item_id,
      unitIndex: task.unit_index,
      executorKey: task.executor_key,
      taskStatus: task.task_status,
      error: error instanceof Error ? error.message : String(error),
    })
    throw error
  }
}

function resolvePaidTaskStatus(profile: FulfillmentBindingLike | null | undefined): string {
  return resolveInitialPaidTaskStatus(profile)
}

function isTaskRow(task: TaskRow | DeliveryTaskRow | null | undefined): task is TaskRow {
  return Boolean(task && Number(task.id || 0) > 0)
}

function buildTaskUnitKey(task: Pick<TaskRow, 'order_item_id' | 'unit_index'>) {
  return buildOrderItemUnitKey(task.order_item_id, task.unit_index)
}

function buildOrderItemUnitKey(orderItemId: number | string, unitIndex: number | string) {
  return `${Number(orderItemId)}:${Math.max(1, Number(unitIndex || 1))}`
}

function buildOrderLogContext(order: OrderRow) {
  return {
    orderId: order.id,
    provider: order.provider,
    platform: order.platform,
    shopId: order.shop_id,
    platformOrderId: order.platform_order_id,
    payStatus: order.pay_status,
  }
}

function buildOrderItemLogContext(item: OrderItemRow) {
  return {
    orderItemId: item.id,
    skuCode: item.sku_code,
    skuName: item.sku_name,
    quantity: item.quantity,
  }
}

function buildPlanningDiagnostic(item: OrderItemRow) {
  const snapshot = parseJsonObject(item.item_snapshot_json)
  const route = parseJsonObject(snapshot.fulfillmentRoute)
  const cloudtentacles = parseJsonObject(snapshot.cloudtentacles)
  const kuaishouFeifei = parseJsonObject(snapshot.kuaishouFeifei)
  const affiliateDash = parseJsonObject(snapshot.affiliateDash)
  const kuaishouSendCode = parseJsonObject(snapshot.kuaishouSendCode)

  return {
    snapshotIsConfigured: Boolean(snapshot.isConfigured),
    selectedExecutorKey: String(route.selectedExecutorKey || '').trim(),
    routeReason: String(route.reason || '').trim(),
    candidateAvailability: route.candidates || route.candidateAvailability || [],
    sourceItemTitle: String(kuaishouSendCode.itemTitle || '').trim(),
    sourceSkuNick: String(kuaishouSendCode.skuNick || '').trim(),
    cloudSkuId: Number(cloudtentacles.cloudSkuId || 0) || 0,
    cloudSkuName: String(cloudtentacles.cloudSkuName || '').trim(),
    cloudSourceKeys: Array.isArray(cloudtentacles.cloudSourceKeys)
      ? cloudtentacles.cloudSourceKeys
      : [],
    feifeiProductCode: String(kuaishouFeifei.productCode || '').trim(),
    affiliateDashSku: String(affiliateDash.sku || '').trim(),
  }
}

function parseJsonObject(value: unknown): JsonObject {
  if (value && typeof value === 'object' && !Array.isArray(value)) {
    return value as JsonObject
  }

  try {
    const parsed = JSON.parse(String(value || '{}'))
    return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
      ? (parsed as JsonObject)
      : {}
  } catch {
    return {}
  }
}
