import { createTask, listTasksByOrderId, updateTask } from '../../repositories/task-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 {
  TASK_STATUS,
  resolveInitialPaidTaskStatus,
} from '../../domain/task-status.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
}

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,
  } = deps

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

  const existingTasks = await listTasks(order.id)

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

    return preparePaidTasks(existingTasks, runtimeDeps)
  }

  const tasks: DeliveryTaskRow[] = []

  for (const item of orderItems) {
    const plan = await planFulfillmentTaskForOrderItem({
      order,
      item,
      getProfileByKey,
    })

    if (!plan) {
      continue
    }

    const quantity = Math.max(1, Number(item.quantity || 1))

    for (let index = 0; index < quantity; index += 1) {
      const createdAt = getNowIso()
      const initialStatus =
        order.pay_status === 'paid'
          ? resolvePaidTaskStatus(plan.profile)
          : TASK_STATUS.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: 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,
      })

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

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

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

async function preparePaidTasks(
  tasks: TaskRow[],
  deps: FulfillmentPrepareDeps,
) {
  const preparedTasks = await Promise.all(
    tasks.map((task) => preparePaidFulfillmentTask(task, deps)),
  )
  return preparedTasks.filter(isTaskRow)
}

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)
}
