/**
 * POST /api/materials/warehouse-layouts/meta/stock-by-locators — what is stored in these locators (for the
 * 3D rendering's article thumbnails, loaded only for racks near the camera).
 * Body { locatorIds: number[], warehouseId?, allWarehouses? } (≤ 200). With `allWarehouses` the stock of every
 * locator with the same Value in any warehouse is merged onto the requested id (one physical hall mirrored per org). One M_StorageOnHand query per 60 ids (OR chain, QtyOnHand > 0),
 * product thumbnails via the cached /api/mobile/product-thumb/:id resolver (service token, 10 min cache).
 * Response { byLocator: { [locatorId]: [{ productId, value, name, qty, thumb }] } }, cached 60 s per id set.
 */
import { string } from 'alga-js'
import { withIdempiere } from '../../../../utils/withIdempiere'
import fetchHelper from '../../../../utils/fetchHelper'
import pLimit from '../../../../utils/pLimit'
import { loadLocatorsByValues, loadWarehouseLocators } from '../../../../utils/warehouseLayout'

const cache = new Map<string, { at: number; data: any }>()
const TTL = 60 * 1000

export default withIdempiere(async (event, token) => {
  const body = await readBody(event)
  const ids = [...new Set((Array.isArray(body?.locatorIds) ? body.locatorIds : []).map((n: any) => Number(n)).filter((n: number) => n > 0))].sort((a, b) => a - b).slice(0, 200)
  if (!ids.length) return { status: 200, byLocator: {} }
  const allWarehouses = body?.allWarehouses === undefined || body?.allWarehouses === null ? !!(useRuntimeConfig() as any).public?.warehouseLayoutShared : !!body.allWarehouses
  const key = (allWarehouses ? 'all:' : '') + ids.join(',')
  const hit = cache.get(key)
  if (hit && Date.now() - hit.at < TTL) return { status: 200, byLocator: hit.data, cached: true }

  // mirror map: any-warehouse locator id → requested (master) id, by Value
  const alias = new Map<number, number>()
  let queryIds = ids
  if (allWarehouses && body?.warehouseId) {
    const master = await loadWarehouseLocators(event, token, Number(body.warehouseId))
    const byId = new Map(master.records.map(r => [r.id, r]))
    const valueToMaster = new Map<string, number>()
    const originals: string[] = []
    for (const id of ids) { const v = byId.get(id)?.value; if (v) { valueToMaster.set(String(v).trim().toLowerCase(), id); originals.push(String(v).trim()) } }
    // iDempiere compares Value case-sensitively → query with the original spelling
    const mirrors = await loadLocatorsByValues(event, token, originals)
    for (const m of mirrors) { const mid = valueToMaster.get(String(m.value).trim().toLowerCase()); if (mid) alias.set(m.id, mid) }
    queryIds = [...new Set([...ids, ...alias.keys()])]
  }

  const byLocator: Record<number, any[]> = {}
  const productIds = new Set<number>()
  for (let i = 0; i < queryIds.length; i += 60) {
    const chunk = queryIds.slice(i, i + 60)
    const filter = string.urlEncode('(' + chunk.map(id => `M_Locator_ID eq ${id}`).join(' OR ') + ') AND QtyOnHand gt 0')
    const res: any = await fetchHelper(event, `models/m_storageonhand?$filter=${filter}&$expand=${string.urlEncode('M_Product_ID($select=Value,Name,ImageURL)')}&$top=5000`, 'GET', token, null)
    for (const r of res?.records || []) {
      const rawLid = Number(r.M_Locator_ID?.id ?? r.M_Locator_ID)
      const lid = alias.get(rawLid) ?? rawLid
      const p = r.M_Product_ID || {}
      const qty = Number(r.QtyOnHand) || 0
      if (!lid || !p.id || qty <= 0) continue
      const list = byLocator[lid] || (byLocator[lid] = [])
      const ex = list.find(x => x.productId === Number(p.id))
      if (ex) ex.qty += qty
      else { list.push({ productId: Number(p.id), value: String(p.Value || ''), name: String(p.Name || ''), qty, imageUrl: p.ImageURL || null, thumb: null }); productIds.add(Number(p.id)) }
    }
  }
  // thumbnails (cached resolver, service token) — bounded concurrency
  const limit = pLimit(5)
  const thumbs = new Map<number, string | null>()
  await Promise.all([...productIds].map(pid => limit(async () => {
    try { const r: any = await $fetch(`/api/mobile/product-thumb/${pid}`); thumbs.set(pid, r?.url || null) } catch { thumbs.set(pid, null) }
  })))
  for (const list of Object.values(byLocator)) for (const a of list) { a.thumb = thumbs.get(a.productId) || a.imageUrl || null; list.sort((x, y) => y.qty - x.qty) }
  if (cache.size > 500) cache.delete(cache.keys().next().value as string)
  cache.set(key, { at: Date.now(), data: byLocator })
  return { status: 200, byLocator }
})
