// Shared enrichment for M_Transaction rows (product history page + org transactions page).
// Resolves locators (+ warehouse), products, shipment lines → shipment/receipt → partner + order,
// inventory lines → physical inventory, movement lines → stock transfer. All lookups are batched
// `$filter=X eq a or X eq b …` reads with `$select` — no child `$expand` (see rest-load-rules.md).
import { string } from 'alga-js'
import fetchHelper from './fetchHelper'

export interface EnrichedTransaction {
  id: number | string
  movementDate: string
  movementQty: number
  movementType: string
  movementTypeId: string
  locator: string
  locatorId: number | null
  warehouse: string
  warehouseId: number | null
  productId: number | null
  productName: string
  productSku: string
  productValue: string
  orgId: number | null
  orgName: string
  inOutLineId: number | null
  inOutId: number | null
  inventoryId: number | null
  inventoryDocumentNo: string
  movementId: number | null
  movementDocumentNo: string
  bpartnerName: string
  bpartnerId: number | null
  externalOrderId: string
  orderId: number | null
  shipmentDocumentNo: string
  isSOTrx: boolean
  shipmentMovementType: string
}

export const extractId = (value: any): number | null => {
  if (typeof value === 'number') return value
  if (value?.id !== undefined && value?.id !== null) return Number(value.id)
  return null
}

const BATCH = 50

const batchedFetch = async (
  event: any,
  token: string,
  model: string,
  idColumn: string,
  ids: Iterable<number>,
  select: string,
  onRecord: (rec: any) => void
) => {
  const arr = Array.from(ids).filter(id => id > 0)
  for (let i = 0; i < arr.length; i += BATCH) {
    const batch = arr.slice(i, i + BATCH)
    const filter = batch.map(id => `${idColumn} eq ${id}`).join(' or ')
    try {
      const res: any = await fetchHelper(
        event,
        `models/${model}?$filter=${string.urlEncode(filter)}&$select=${string.urlEncode(select)}&$top=${batch.length}`,
        'GET',
        token,
        null
      )
      for (const rec of res?.records || []) onRecord(rec)
    } catch (err) {
      console.error(`Error fetching ${model} details:`, err)
    }
  }
}

/**
 * @param productInfo optional fixed product (product-history page: every row is the same product,
 *                    so the product lookup is skipped)
 */
export const enrichTransactions = async (
  event: any,
  token: string,
  transactions: any[],
  productInfo: { name: string; sku: string; value?: string } | null = null
): Promise<EnrichedTransaction[]> => {
  if (!transactions?.length) return []

  const locatorIds = new Set<number>()
  const productIds = new Set<number>()
  const inOutLineIds = new Set<number>()
  const inventoryLineIds = new Set<number>()
  const movementLineIds = new Set<number>()

  for (const t of transactions) {
    const l = extractId(t.M_Locator_ID); if (l) locatorIds.add(l)
    const p = extractId(t.M_Product_ID); if (p) productIds.add(p)
    const io = extractId(t.M_InOutLine_ID); if (io) inOutLineIds.add(io)
    const inv = extractId(t.M_InventoryLine_ID); if (inv) inventoryLineIds.add(inv)
    const mv = extractId(t.M_MovementLine_ID); if (mv) movementLineIds.add(mv)
  }

  // Locators (+ warehouse)
  const locators: Record<number, { value: string; warehouseId: number | null; warehouse: string }> = {}
  await batchedFetch(event, token, 'M_Locator', 'M_Locator_ID', locatorIds, 'M_Locator_ID,Value,M_Warehouse_ID', (rec) => {
    locators[rec.id] = {
      value: rec.Value || rec.identifier || '',
      warehouseId: extractId(rec.M_Warehouse_ID),
      warehouse: rec.M_Warehouse_ID?.identifier || ''
    }
  })

  // Products
  const products: Record<number, { name: string; sku: string; value: string }> = {}
  if (!productInfo) {
    await batchedFetch(event, token, 'M_Product', 'M_Product_ID', productIds, 'M_Product_ID,Name,SKU,Value', (rec) => {
      products[rec.id] = { name: rec.Name || '', sku: rec.SKU || '', value: rec.Value || '' }
    })
  }

  // Shipment lines → shipment ids
  const inOutLineToInOut: Record<number, number | null> = {}
  const inOutIds = new Set<number>()
  await batchedFetch(event, token, 'M_InOutLine', 'M_InOutLine_ID', inOutLineIds, 'M_InOutLine_ID,M_InOut_ID', (rec) => {
    const inOutId = extractId(rec.M_InOut_ID)
    inOutLineToInOut[rec.id] = inOutId
    if (inOutId) inOutIds.add(inOutId)
  })

  // Shipments → partner / order
  const shipments: Record<number, any> = {}
  const bpartnerIds = new Set<number>()
  const orderIds = new Set<number>()
  await batchedFetch(event, token, 'M_InOut', 'M_InOut_ID', inOutIds, 'M_InOut_ID,DocumentNo,IsSOTrx,MovementType,C_BPartner_ID,C_Order_ID', (rec) => {
    const bpartnerId = extractId(rec.C_BPartner_ID)
    const orderId = extractId(rec.C_Order_ID)
    if (bpartnerId) bpartnerIds.add(bpartnerId)
    if (orderId) orderIds.add(orderId)
    shipments[rec.id] = {
      documentNo: rec.DocumentNo || '',
      isSOTrx: rec.IsSOTrx === true || rec.IsSOTrx === 'Y',
      movementType: rec.MovementType?.id || rec.MovementType || '',
      bpartnerId,
      orderId,
      bpartnerName: '',
      externalOrderId: ''
    }
  })

  const bpartnerNames: Record<number, string> = {}
  await batchedFetch(event, token, 'C_BPartner', 'C_BPartner_ID', bpartnerIds, 'C_BPartner_ID,Name', (rec) => {
    bpartnerNames[rec.id] = rec.Name || ''
  })
  const externalOrderIds: Record<number, string> = {}
  await batchedFetch(event, token, 'C_Order', 'C_Order_ID', orderIds, 'C_Order_ID,ExternalOrderId', (rec) => {
    externalOrderIds[rec.id] = rec.ExternalOrderId || ''
  })
  for (const s of Object.values(shipments)) {
    if (s.bpartnerId) s.bpartnerName = bpartnerNames[s.bpartnerId] || ''
    if (s.orderId) s.externalOrderId = externalOrderIds[s.orderId] || ''
  }

  // Inventory lines → physical inventory document
  const inventoryLineToInventory: Record<number, { id: number | null; documentNo: string }> = {}
  await batchedFetch(event, token, 'M_InventoryLine', 'M_InventoryLine_ID', inventoryLineIds, 'M_InventoryLine_ID,M_Inventory_ID', (rec) => {
    inventoryLineToInventory[rec.id] = { id: extractId(rec.M_Inventory_ID), documentNo: '' }
  })
  const inventoryIds = new Set<number>()
  for (const v of Object.values(inventoryLineToInventory)) if (v.id) inventoryIds.add(v.id)
  const inventoryDocNos: Record<number, string> = {}
  await batchedFetch(event, token, 'M_Inventory', 'M_Inventory_ID', inventoryIds, 'M_Inventory_ID,DocumentNo', (rec) => {
    inventoryDocNos[rec.id] = rec.DocumentNo || ''
  })

  // Movement lines → stock transfer document
  const movementLineToMovement: Record<number, { id: number | null; documentNo: string }> = {}
  await batchedFetch(event, token, 'M_MovementLine', 'M_MovementLine_ID', movementLineIds, 'M_MovementLine_ID,M_Movement_ID', (rec) => {
    movementLineToMovement[rec.id] = { id: extractId(rec.M_Movement_ID), documentNo: '' }
  })
  const movementIds = new Set<number>()
  for (const v of Object.values(movementLineToMovement)) if (v.id) movementIds.add(v.id)
  const movementDocNos: Record<number, string> = {}
  await batchedFetch(event, token, 'M_Movement', 'M_Movement_ID', movementIds, 'M_Movement_ID,DocumentNo', (rec) => {
    movementDocNos[rec.id] = rec.DocumentNo || ''
  })

  return transactions.map((t: any): EnrichedTransaction => {
    const locatorId = extractId(t.M_Locator_ID)
    const productId = extractId(t.M_Product_ID)
    const inOutLineId = extractId(t.M_InOutLine_ID)
    const inventoryLineId = extractId(t.M_InventoryLine_ID)
    const movementLineId = extractId(t.M_MovementLine_ID)
    const inOutId = inOutLineId ? inOutLineToInOut[inOutLineId] ?? null : null
    const shipment = inOutId ? shipments[inOutId] || {} : {}
    const loc = locatorId ? locators[locatorId] : undefined
    const prod = productInfo
      ? { name: productInfo.name, sku: productInfo.sku, value: productInfo.value || '' }
      : (productId ? products[productId] : undefined)
    const inv = inventoryLineId ? inventoryLineToInventory[inventoryLineId] : undefined
    const mv = movementLineId ? movementLineToMovement[movementLineId] : undefined

    return {
      id: t.id || t.uid,
      movementDate: t.MovementDate || '',
      movementQty: Number(t.MovementQty) || 0,
      movementType: t.MovementType?.identifier || t.MovementType || '',
      movementTypeId: t.MovementType?.id || '',
      locator: loc?.value || '',
      locatorId,
      warehouse: loc?.warehouse || '',
      warehouseId: loc?.warehouseId ?? null,
      productId,
      productName: prod?.name || t.M_Product_ID?.identifier || '',
      productSku: prod?.sku || '',
      productValue: prod?.value || '',
      orgId: extractId(t.AD_Org_ID),
      orgName: t.AD_Org_ID?.identifier || '',
      inOutLineId,
      inOutId,
      inventoryId: inv?.id ?? null,
      inventoryDocumentNo: inv?.id ? inventoryDocNos[inv.id] || '' : '',
      movementId: mv?.id ?? null,
      movementDocumentNo: mv?.id ? movementDocNos[mv.id] || '' : '',
      bpartnerName: shipment.bpartnerName || '',
      bpartnerId: shipment.bpartnerId || null,
      externalOrderId: shipment.externalOrderId || '',
      orderId: shipment.orderId || null,
      shipmentDocumentNo: shipment.documentNo || '',
      isSOTrx: shipment.isSOTrx || false,
      shipmentMovementType: shipment.movementType || ''
    }
  })
}
