import { createTask, listTasksByOrderId, updateTask } from '../../repositories/task-repo.js'
import {
  listFulfillmentProfileRequirements,
  resolveFulfillmentBinding,
} from '../../repositories/fulfillment-profile-repo.js'
import { reserveInventoryForTask } from './inventory-service.js'
import { createTaskClaimToken } from '../claim/claim-service.js'
import { notifyTaskAutoManualReview } from '../notification/domain-notifications.js'
import { nowIso } from '../../utils/time.js'
import { randomId } from '../../utils/random.js'
import type { InventoryItemRow, OrderItemRow, OrderRow, TaskRow } from '../../types/repository-rows.js'

type JsonObject = Record<string, unknown>

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

type FulfillmentBindingLike = {
  id: number
  profile_id?: number
  profile_key?: string
  profile_name?: string
  name?: string
  executor_key?: string
  requires_claim?: boolean
  auto_dispatch?: boolean
  config_json?: string | JsonObject
  [key: string]: unknown
}

type FulfillmentRequirementLike = {
  role_key?: string
  roleKey?: string
  credential_type?: string
  credentialType?: string
  is_required?: boolean
  [key: string]: unknown
}

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

type DeliveryTaskDeps = {
  createTask?: typeof createTask
  listTasksByOrderId?: typeof listTasksByOrderId
  updateTask?: typeof updateTask
  listFulfillmentProfileRequirements?: (
    profileId: number | string,
  ) => Promise<FulfillmentRequirementLike[]>
  resolveFulfillmentBinding?: (input: {
    skuCode: string
    provider?: string
    platform?: string
    shopId?: string
  }) => Promise<FulfillmentBindingLike | null>
  reserveInventoryForTask?: (input: {
    skuCode: string
    taskId: number | string
    credentialType?: string
    roleKey?: string
  }) => Promise<InventoryItemRow | null>
  createTaskClaimToken?: (taskId: number | string) => Promise<ClaimTokenLike>
  notifyTaskAutoManualReview?: (payload: {
    task: unknown
    reason: string
    source: string
  }) => Promise<unknown> | unknown
  nowIso?: () => string
  randomId?: (prefix?: string) => string
}

type RuntimeDeliveryTaskDeps = Required<Pick<
  DeliveryTaskDeps,
  'updateTask'
  | 'reserveInventoryForTask'
  | 'createTaskClaimToken'
  | 'notifyTaskAutoManualReview'
  | 'nowIso'
>>

type TaskContext = {
  inventorySkuCode?: string
  primaryRequirement?: {
    roleKey?: string
    credentialType?: string
  } | null
  [key: string]: unknown
}

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

export async function syncDeliveryTasksForOrderWithDeps(
  order: OrderRow,
  orderItems: OrderItemRow[],
  deps: DeliveryTaskDeps = {},
): Promise<Array<TaskRow | null>> {
  const {
    createTask: createDeliveryTask = createTask,
    listTasksByOrderId: listTasks = listTasksByOrderId,
    updateTask: updateDeliveryTask = updateTask,
    listFulfillmentProfileRequirements: listProfileRequirements = listFulfillmentProfileRequirements,
    resolveFulfillmentBinding: resolveBinding = resolveFulfillmentBinding,
    reserveInventoryForTask: reserveInventory = reserveInventoryForTask,
    createTaskClaimToken: createClaimToken = createTaskClaimToken,
    notifyTaskAutoManualReview: notifyManualReview = notifyTaskAutoManualReview,
    nowIso: getNowIso = nowIso,
    randomId: createRandomId = randomId,
  } = deps

  const runtimeDeps: RuntimeDeliveryTaskDeps = {
    updateTask: updateDeliveryTask,
    reserveInventoryForTask: reserveInventory,
    createTaskClaimToken: createClaimToken,
    notifyTaskAutoManualReview: notifyManualReview,
    nowIso: getNowIso,
  }

  const existingTasks = await listTasks(order.id)

  if (existingTasks.length > 0) {
    if (order.pay_status !== 'paid') {
      return existingTasks
    }

    const itemMap = new Map(orderItems.map((item) => [item.id, item]))
    return Promise.all(existingTasks.map((task) => preparePaidTask({
      ...task,
      skuCode: itemMap.get(task.order_item_id)?.sku_code || '',
      skuName: itemMap.get(task.order_item_id)?.sku_name || '',
    }, runtimeDeps)))
  }

  const tasks: DeliveryTaskRow[] = []

  for (const item of orderItems) {
    const binding = await resolveBinding({
      skuCode: item.sku_code,
      provider: order.provider,
      platform: order.platform,
      shopId: order.shop_id,
    })

    if (!binding) {
      continue
    }
    const profile = binding
    const requirements = await listProfileRequirements(profile.profile_id || profile.id)
    const primaryRequirement = requirements.find((requirement) => requirement.is_required !== false) || requirements[0] || null
    const quantity = Math.max(1, Number(item.quantity || 1))
    const fulfillmentConfig = parseJsonObject(profile.config_json)
    const cloudtentaclesConfig = parseJsonObject(fulfillmentConfig.cloudtentacles)
    const kuaishouConsumeConfig = parseJsonObject(fulfillmentConfig.kuaishouConsume)
    const kuaishouShopConfig = parseJsonObject(fulfillmentConfig.kuaishouShop)

    for (let index = 0; index < quantity; index += 1) {
      const createdAt = getNowIso()
      const initialStatus = order.pay_status === 'paid'
        ? resolvePaidTaskStatus(profile)
        : 'pending_payment'

      const task = await createDeliveryTask({
        orderId: order.id,
        orderItemId: item.id,
        unitIndex: index + 1,
        provider: order.provider,
        platform: order.platform,
        shopId: order.shop_id,
        shopName: order.shop_name,
        platformOrderId: order.platform_order_id,
        taskNo: createRandomId('DT'),
        profileId: Number(profile.profile_id || profile.id),
        executorKey: String(profile.executor_key || 'manual_dispatch'),
        taskStatus: initialStatus,
        inventoryStatus: profile.requires_claim ? 'pending' : 'not_required',
        deliveryStatus: 'pending',
        resultCode: '',
        resultMessage: '',
        claimToken: '',
        claimExpiresAt: null,
        automationMode: profile.auto_dispatch ? 'automatic' : 'manual',
        requiresClaim: Boolean(profile.requires_claim),
        userActionStatus: profile.requires_claim ? 'pending_claim' : 'not_required',
        attemptCount: 0,
        lastError: '',
        contextJson: JSON.stringify({
          profileKey: String(profile.profile_key || ''),
          profileName: String(profile.profile_name || profile.name || ''),
          skuCode: item.sku_code,
          skuName: item.sku_name,
          inventorySkuCode: item.sku_code,
          primaryRequirement: primaryRequirement
            ? {
              roleKey: String(primaryRequirement.role_key || primaryRequirement.roleKey || 'primary_code'),
              credentialType: String(primaryRequirement.credential_type || primaryRequirement.credentialType || 'tencent_code'),
            }
            : null,
          kuaishouCloudFulfillment: isKuaishouCloudExecutor(profile.executor_key)
            ? {
              flowType: 'kuaishou_cloud_fulfillment',
              configId: String(fulfillmentConfig.configId || '').trim(),
              internalSkuCode: item.sku_code,
              internalSkuName: item.sku_name,
              ticket: {
                code: '',
                status: 'pending',
                capturedAt: null,
                capturedBy: null,
                verifiedAt: null,
                oid: '',
                formToken: '',
                leftCount: 0,
                goodsTitle: '',
              },
              binding: {
                prepareStatus: 'pending',
                cloudSourceKey: String(cloudtentaclesConfig.cloudSourceKey || 'default').trim() || 'default',
                skuId: Number(cloudtentaclesConfig.skuId || 0) || 0,
                skuName: String(cloudtentaclesConfig.skuName || '').trim(),
                vnKey: '1',
                vnId: 0,
                vnPhone: '',
                bindUrl: '',
                bindPreparedAt: null,
                bindExpiresAt: null,
                bindProbeAt: null,
                bindProbeStatus: '',
                bindProbeMessage: '',
              },
              role: {
                status: 'pending',
                name: '',
                rid: '',
                refreshedAt: null,
                errorMessage: '',
                rawInfo: null,
              },
              purchase: {
                autoBuyEnabled: cloudtentaclesConfig.autoBuyEnabled !== false,
                minAssetReserve: Number(cloudtentaclesConfig.minAssetReserve || 0) || 0,
                usedKnapsack: false,
                purchaseTriggered: false,
                assetBefore: 0,
                assetAfter: 0,
                purchaseAt: null,
              },
              dispatch: {
                status: 'pending',
                dispatchAt: null,
                dispatchBy: null,
                sendType: 0,
                note: '',
              },
              returnNumber: {
                status: 'pending',
                returnedAt: null,
                returnedBy: null,
                autoReturnEnabled: cloudtentaclesConfig.autoReturnNumberAfterDispatch === true,
              },
              consume: {
                status: 'pending',
                shopId: String(kuaishouConsumeConfig.shopId || kuaishouShopConfig.shopId || '').trim(),
                shopName: String(kuaishouConsumeConfig.shopName || kuaishouShopConfig.kshopName || order.shop_name || '').trim(),
                autoConsumeEnabled: kuaishouConsumeConfig.autoConsumeAfterDispatch === true,
                consumedAt: null,
                errorMessage: '',
              },
              notes: String(fulfillmentConfig.notes || '').trim(),
            }
            : null,
        }),
        createdAt,
        updatedAt: createdAt,
      })

      tasks.push({
        ...task,
        skuCode: item.sku_code,
        skuName: item.sku_name,
      })
    }
  }

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

  return Promise.all(tasks.map((task) => preparePaidTask(task, runtimeDeps)))
}

async function preparePaidTask(
  task: DeliveryTaskRow,
  deps: Partial<RuntimeDeliveryTaskDeps> = {},
): Promise<TaskRow | DeliveryTaskRow | null> {
  const {
    updateTask: updateDeliveryTask = updateTask,
    reserveInventoryForTask: reserveInventory = reserveInventoryForTask,
    createTaskClaimToken: createClaimToken = createTaskClaimToken,
    notifyTaskAutoManualReview: notifyManualReview = notifyTaskAutoManualReview,
    nowIso: getNowIso = nowIso,
  } = deps

  const now = getNowIso()
  const taskContext = parseTaskContext(task)
  const primaryRequirement = taskContext.primaryRequirement || null
  const inventorySkuCode = String(taskContext.inventorySkuCode || task.skuCode || '').trim()

  if (['link_generated', 'claimed', 'role_confirmed', 'redeeming', 'redeemed', 'closed'].includes(task.task_status)) {
    return task
  }

  if (isKuaishouCloudExecutor(task.executor_key)) {
    if ([
      'pending_binding_prepare',
      'waiting_binding',
      'dispatched_pending_return',
      'completed',
      'manual_review',
      'failed',
    ].includes(String(task.task_status || '').trim())) {
      if (!task.primary_claim_token_id && !String(task.claim_token || '').trim()) {
        const claimToken = await createClaimToken(task.id)
        return updateDeliveryTask(task.id, {
          claim_token: claimToken.token,
          claim_expires_at: claimToken.expired_at,
          user_action_status: 'pending_claim',
          updated_at: now,
        })
      }

      return task
    }

    const claimToken = await createClaimToken(task.id)

    return updateDeliveryTask(task.id, {
      task_status: 'pending_binding_prepare',
      inventory_status: 'not_required',
      claim_token: claimToken.token,
      claim_expires_at: claimToken.expired_at,
      user_action_status: 'pending_claim',
      last_error: task.last_error || '领取链接已生成，等待客户提交核销码',
      updated_at: now,
    })
  }

  if (!task.requires_claim || String(task.executor_key || '').trim() === 'manual_dispatch') {
    const lastError = task.last_error || '当前任务需要人工履约处理'
    const updatedTask = await updateDeliveryTask(task.id, {
      task_status: 'manual_review',
      inventory_status: 'not_required',
      user_action_status: 'not_required',
      last_error: lastError,
      updated_at: now,
    })
    await notifyManualReview({
      task: updatedTask || task,
      reason: lastError,
      source: 'manual_dispatch_profile',
    })
    return updatedTask
  }

  if (!inventorySkuCode) {
    const lastError = '未匹配到 SKU，无法为任务分配库存凭据'
    const updatedTask = await updateDeliveryTask(task.id, {
      task_status: 'manual_review',
      last_error: lastError,
      updated_at: now,
    })
    await notifyManualReview({
      task: updatedTask || task,
      reason: lastError,
      source: 'missing_sku',
    })
    return updatedTask
  }

  if (task.primary_inventory_item_id && task.primary_claim_token_id) {
    return updateDeliveryTask(task.id, {
      task_status: 'link_generated',
      last_error: '',
      updated_at: now,
    })
  }

  if (!primaryRequirement?.credentialType) {
    const lastError = '履约档案未配置库存要求，无法自动分配库存'
    const updatedTask = await updateDeliveryTask(task.id, {
      task_status: 'manual_review',
      last_error: lastError,
      updated_at: now,
    })
    await notifyManualReview({
      task: updatedTask || task,
      reason: lastError,
      source: 'missing_inventory_requirement',
    })
    return updatedTask
  }

  const reserved = await reserveInventory({
    skuCode: inventorySkuCode,
    taskId: task.id,
    credentialType: primaryRequirement.credentialType,
    roleKey: primaryRequirement.roleKey || 'primary_code',
  })

  if (!reserved) {
    return updateDeliveryTask(task.id, {
      task_status: 'waiting_inventory',
      inventory_status: 'pending',
      last_error: '库存不足，等待可用库存凭据',
      updated_at: now,
    })
  }

  const claimToken = await createClaimToken(task.id)

  return updateDeliveryTask(task.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: '',
    updated_at: now,
  })
}

function resolvePaidTaskStatus(profile: FulfillmentBindingLike | null | undefined): string {
  if (Boolean(profile?.requires_claim)) {
    return 'paid'
  }

  if (isKuaishouCloudExecutor(profile?.executor_key)) {
    return 'pending_binding_prepare'
  }

  if (String(profile?.executor_key || '').trim() === 'manual_dispatch') {
    return 'manual_review'
  }

  return 'paid'
}

function parseTaskContext(task: { context_json?: unknown } | null | undefined): TaskContext {
  const value = task?.context_json

  if (!value) {
    return {}
  }

  if (typeof value === 'object') {
    return value as TaskContext
  }

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

function parseJsonObject(value: unknown): JsonObject {
  if (!value) {
    return {}
  }

  if (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 {}
  }
}

function isKuaishouCloudExecutor(value: unknown): boolean {
  return String(value || '').trim() === 'kuaishou_ct_assisted'
}
