import { string } from 'alga-js'
import refreshTokenHelper from "../../../utils/refreshTokenHelper"
import getTokenHelper from "../../../utils/getTokenHelper"
import forceLogoutHelper from "../../../utils/forceLogoutHelper"
import errorHandlingHelper from "../../../utils/errorHandlingHelper"
import fetchHelper from "../../../utils/fetchHelper"
import getReturnLocatorIds from "../../../utils/returnLocatorHelper"

// Org-scoped product search for the Qty-Fix (duplicate cleanup) tool.
// Returns matching products with their current (non-return) qty on hand so the
// operator can quickly see which duplicate holds the (wrongly added) stock.
const handleFunc = async (event: any, authToken: any = null) => {
  let data: any = { records: [] }
  const token = authToken ?? await getTokenHelper(event)
  const query = getQuery(event)
  const orgId = query?.org_id as string
  const searchTerm = ((query?.search as string) || '').trim()
  // The SOURCE (wrong/duplicate) side may legitimately be a BOM article that wrongly
  // holds stock, so it asks for BOMs to be INCLUDED. The TARGET (correct) side must
  // never be a BOM, so it omits this flag and keeps the exclusion.
  const includeBom = ['1', 'true', 'yes'].includes(String(query?.include_bom ?? '').toLowerCase())
  // "Show all" mode (source side): browse the org's products WITHOUT a search term and let
  // the client-side checkbox filters (stock / BOM) narrow them. Returns a larger bounded page.
  const loadAll = ['1', 'true', 'yes'].includes(String(query?.load_all ?? '').toLowerCase())

  if (!orgId) {
    return { status: 400, message: 'Organization ID is required', records: [] }
  }
  if (searchTerm.length < 2 && !loadAll) {
    return data
  }

  const escaped = searchTerm.replace(/'/g, "''").toLowerCase()
  // Strict org scope (no AD_Org_ID 0) — duplicate stock to be moved is org-owned.
  // BOM articles are excluded for the TARGET side only: IsBOM is the standard iDempiere
  // flag (always present → filtered server-side); isJtlBom is a custom column that
  // may be absent in some envs, so it's filtered in JS below (NOT in $filter, which
  // would 500 where the column doesn't exist). When includeBom is set (source side),
  // both filters are skipped so any BOM can be picked.
  const bomClause = includeBom ? '' : ' AND IsBOM eq false'
  // The name/SKU/EAN match is only added when a term is present; in load-all mode it is
  // omitted so the whole (bounded) org product list comes back for client-side filtering.
  const termClause = searchTerm.length >= 2
    ? `(contains(tolower(Value),'${escaped}') OR contains(tolower(Name),'${escaped}') OR contains(tolower(SKU),'${escaped}') OR contains(tolower(mpn),'${escaped}') OR contains(tolower(UPC),'${escaped}')) AND `
    : ''
  const searchFilter = `${termClause}AD_Org_ID eq ${orgId} AND IsActive eq true${bomClause}`

  // Load-all returns a larger bounded page (client then filters by stock/BOM); a normal
  // search stays tight at 30.
  const top = loadAll ? 200 : 30

  // No $select: fetch full records (matches the mobile product-lookup) so optional
  // custom columns like isJtlBom come along whether or not they exist in this env
  // (adding a missing column to $select would 500 the whole query).
  const res: any = await fetchHelper(
    event,
    `models/m_product?$filter=${string.urlEncode(searchFilter)}&$expand=M_Storage&$top=${top}&$orderby=${string.urlEncode('Name asc')}`,
    'GET',
    token,
    null
  )

  const returnLocatorIds = await getReturnLocatorIds(event, token, orgId)

  data.records = (res?.records || []).filter((p: any) => {
    // Hide JTL-BOM articles (custom column; filtered here rather than in $filter
    // because it may not exist in every env). Standard IsBOM is excluded server-side.
    // Skipped entirely for the source side (includeBom) so any BOM stays selectable.
    if (includeBom) return true
    const isJtlBom = (p.isJtlBom ?? p.IsJtlBom) === true || (p.isJtlBom ?? p.IsJtlBom) === 'Y'
    return !isJtlBom
  }).map((p: any) => {
    let qtyOnHand = 0
    let returnQtyOnHand = 0
    const storages = Array.isArray(p?.M_Storage) ? p.M_Storage : []
    for (const s of storages) {
      const qty = Number(s?.QtyOnHand ?? 0)
      if (!qty) continue
      const locId = s?.M_Locator_ID?.id ?? s?.M_Locator_ID
      if (locId !== undefined && locId !== null && returnLocatorIds.has(Number(locId))) {
        returnQtyOnHand += qty
      } else {
        qtyOnHand += qty
      }
    }
    return {
      id: p.M_Product_ID || p.id,
      Name: p.Name,
      Value: p.Value,
      SKU: p.SKU,
      UPC: p.UPC,
      MPN: p.mpn || null,
      isJtlBom: (p.isJtlBom ?? p.IsJtlBom) === true || (p.isJtlBom ?? p.IsJtlBom) === 'Y',
      isBom: (p.IsBOM ?? p.isBom) === true || (p.IsBOM ?? p.isBom) === 'Y',
      qtyOnHand,
      returnQtyOnHand
    }
  })

  // Flag when the load-all page hit its cap (more org products exist than returned) so the
  // UI can hint the operator to narrow with a search.
  data.capped = loadAll && (res?.records?.length || 0) >= top

  return data
}

export default defineEventHandler(async (event) => {
  let data: any = {}

  try {
    data = await handleFunc(event)
  } catch (err: any) {
    try {
      let authToken: any = await refreshTokenHelper(event)
      data = await handleFunc(event, authToken)
    } catch (error: any) {
      data = errorHandlingHelper(err?.data ?? err, error?.data ?? error)
      forceLogoutHelper(event, data)
    }
  }

  return data
})
