import {
  assignReservedInventoryItem,
  findFirstAvailableInventoryItemBySkuCode,
  releaseReservedInventoryItem,
} from '../../repositories/inventory-repo.js'
import type { InventoryItemRow } from '../../types/repository-rows.js'
import { nowIso } from '../../utils/time.js'

type InventoryItemId = number | string | null | undefined
type InventoryGroupCodesInput = string | string[] | null | undefined

type ReserveInventoryForTaskInput = {
  skuCode?: string
  taskId: number | string
  credentialType?: string
  roleKey?: string
  inventoryGroupCodes?: InventoryGroupCodesInput
}

type ReserveInventoryForTaskDeps = {
  assignReservedInventoryItem?: (
    inventoryItemId: number | string,
    taskId: number | string,
    updatedAt: string,
    roleKey?: string,
  ) => Promise<InventoryItemRow | null>
  findFirstAvailableInventoryItemBySkuCode?: (
    skuCode: string,
    credentialType?: string,
    inventoryGroupCodes?: InventoryGroupCodesInput,
  ) => Promise<InventoryItemRow | null>
  nowIso?: () => string
}

type ReleaseReservedInventoryItemsDeps = {
  releaseReservedInventoryItem?: (
    inventoryItemId: number | string,
    updatedAt: string,
  ) => Promise<InventoryItemRow | null>
}

export async function reserveInventoryForTask({
  skuCode,
  taskId,
  credentialType = 'tencent_code',
  roleKey = 'primary_code',
  inventoryGroupCodes = null,
}: ReserveInventoryForTaskInput): Promise<InventoryItemRow | null> {
  return reserveInventoryForTaskWithDeps(
    { skuCode, taskId, credentialType, roleKey, inventoryGroupCodes },
    {
      assignReservedInventoryItem,
      findFirstAvailableInventoryItemBySkuCode,
      nowIso,
    },
  )
}

export async function reserveInventoryForTaskWithDeps(
  {
    skuCode,
    taskId,
    credentialType = 'tencent_code',
    roleKey = 'primary_code',
    inventoryGroupCodes = null,
  }: ReserveInventoryForTaskInput,
  {
    assignReservedInventoryItem: assignReserved = assignReservedInventoryItem,
    findFirstAvailableInventoryItemBySkuCode: findAvailable = findFirstAvailableInventoryItemBySkuCode,
    nowIso: getNowIso = nowIso,
  }: ReserveInventoryForTaskDeps = {},
): Promise<InventoryItemRow | null> {
  if (!skuCode) {
    return null
  }

  if (Array.isArray(inventoryGroupCodes) && inventoryGroupCodes.length === 0) {
    return null
  }

  const available = await findAvailable(skuCode, credentialType, inventoryGroupCodes)

  if (!available) {
    return null
  }

  return assignReserved(available.id, taskId, getNowIso(), roleKey)
}

export async function releaseReservedInventoryItems(
  inventoryItemIds: InventoryItemId[] = [],
  updatedAt = nowIso(),
): Promise<InventoryItemRow[]> {
  return releaseReservedInventoryItemsWithDeps(
    inventoryItemIds,
    updatedAt,
    { releaseReservedInventoryItem },
  )
}

export async function releaseReservedInventoryItemsWithDeps(
  inventoryItemIds: InventoryItemId[] = [],
  updatedAt = nowIso(),
  {
    releaseReservedInventoryItem: releaseReserved = releaseReservedInventoryItem,
  }: ReleaseReservedInventoryItemsDeps = {},
): Promise<InventoryItemRow[]> {
  const released: InventoryItemRow[] = []

  for (const inventoryItemId of inventoryItemIds) {
    if (!inventoryItemId) {
      continue
    }

    const item = await releaseReserved(inventoryItemId, updatedAt)
    if (item) {
      released.push(item)
    }
  }

  return released
}
