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

const idempiereFilterName = (key: string) => {
  const map: Record<string, string> = {
    name: 'Name',
    value: 'Value',
    sku: 'SKU',
    mpn: 'mpn',
    upc: 'UPC',
    weight: 'Weight',
    width: 'Width',
    length: 'Length',
    height: 'Height',
    volume: 'Volume',
    isBOM: 'IsBOM',
    organization: 'AD_Org_ID',
    qtyonhand: 'QtyOnHand',
    taricCode: 'taricCode'
  }
  return map[key] || ''
}

const filterHandler = (filterModel: any) => {
  let newFilter = ''
  for (const key of Object.keys(filterModel)) {
    const filterName = idempiereFilterName(key)
    if (!filterName) continue
    if (newFilter !== '') {
      newFilter += ' AND '
    }
    const item = filterModel[key]
    if (item.filterType == 'text') {
      newFilter += textFilterHandler(filterName, item)
    } else if (item.filterType == 'number') {
      newFilter += numberFilterHandler(filterName, item)
    }
  }
  if (newFilter !== '') {
    newFilter = ` AND (${newFilter})`
  }
  return newFilter
}

const numberFilterHandler = (key: string, item: any) => {
  let newNumberFilter = ''
  if (item.type == 'equals') {
    newNumberFilter = `${key} eq ${item.filter}`
  } else if (item.type == 'notEqual') {
    newNumberFilter = `${key} neq ${item.filter}`
  } else if (item.type == 'greaterThan') {
    newNumberFilter = `${key} gt ${item.filter}`
  } else if (item.type == 'greaterThanOrEqual') {
    newNumberFilter = `${key} ge ${item.filter}`
  } else if (item.type == 'lessThan') {
    newNumberFilter = `${key} lt ${item.filter}`
  } else if (item.type == 'lessThanOrEqual') {
    newNumberFilter = `${key} le ${item.filter}`
  } else if (item.type == 'inRange') {
    newNumberFilter = `${key} ge ${item.filter} AND ${key} le ${item.filterTo}`
  }
  return newNumberFilter
}

const textFilterHandler = (key: string, item: any) => {
  let newTextFilter = ''
  if (item.type == 'equals') {
    newTextFilter = `${key} eq ${item.filter}`
  } else if (item.type == 'notEqual') {
    newTextFilter = `${key} neq ${item.filter}`
  } else if (item.type == 'contains') {
    newTextFilter = `contains(${key},${item.filter.replaceAll('%', '')})`
  } else if (item.type == 'notContains') {
    newTextFilter = `not contains(${key},${item.filter.replaceAll('%', '')})`
  } else if (item.type == 'startsWith') {
    newTextFilter = `startswith(${key},'${item.filter}')`
  } else if (item.type == 'endsWith') {
    newTextFilter = `endswith(${key},'${item.filter}')`
  }
  return newTextFilter
}

const sortHandler = (sortModel: any[]) => {
  if (!sortModel || sortModel.length === 0) {
    return 'M_Product_ID desc'
  }
  const parts = sortModel.map((s: any) => {
    const col = idempiereFilterName(s.colId)
    if (!col) return ''
    return `${col} ${s.sort}`
  }).filter(Boolean)
  return parts.length > 0 ? parts.join(',') : 'M_Product_ID desc'
}

const storageCount = (storages: any[], key: string) => {
  return storages.map(i => i[key]).reduce((acc: number, prev: any) => Number(acc) + Number(prev), 0)
}

const splitStorageByLocator = (storages: any[], returnLocatorIds: Set<number>) => {
  let onHand = 0
  let returnOnHand = 0
  for (const s of storages) {
    const qty = Number(s?.QtyOnHand ?? 0)
    const locId = s?.M_Locator_ID?.id ?? s?.M_Locator_ID
    if (locId !== undefined && locId !== null && returnLocatorIds.has(Number(locId))) {
      returnOnHand += qty
    } else {
      onHand += qty
    }
  }
  return { onHand, returnOnHand }
}

const handleFunc = async (event: any, authToken: any = null) => {
  let data: any = {
    rows: [],
    lastRow: 0
  }
  const config = useRuntimeConfig()
  const token = authToken ?? await getTokenHelper(event)
  const body = await readBody(event)

  const startRow = body.startRow
  const endRow = body.endRow
  const pageSize = (endRow - startRow) + 1

  const orderBy = sortHandler(body.sortModel)
  // NB: isJtlBom/taricCode are custom columns with lowercase-first ColumnName — PascalCase
  // ('IsJtlBom'/'TaricCode') is rejected by iDempiere's $select and would break the whole grid.
  const select = string.urlEncode('M_Product_ID,M_Product_UU,Name,Value,SKU,mpn,UPC,Weight,Width,Length,Height,Volume,IsBOM,isJtlBom,AD_Org_ID,Strapi_Product_documentId,CompletedAllMarketplaceData,CustomFulfillmentPrice,isCustomFulfillmentPrice,CUST_FulfillmentProductPricingRules_ID,labelprinter,taricCode,C_Country_ID')

  // Build org filter from toolbar switch.
  // Include org 0 ('*') so shared/client-level product masters aren't filtered out
  // when a concrete org is selected (mirrors materials/products/search.get.ts).
  let orgFilter = ''
  if (body.orgId) {
    orgFilter = ` AND (AD_Org_ID eq ${body.orgId} OR AD_Org_ID eq 0)`
  }

  // By default hide bill-of-materials articles (standard BOM + JTL-BOM). The
  // products-list toolbar can opt back in via `includeBom`. NB: iDempiere's OData
  // `ne`/`not(...)` are unsupported here, so we use `eq false` (which also drops
  // the handful of rows whose flag is null — acceptable, the toggle reveals them).
  let bomFilter = ''
  if (!body.includeBom) {
    bomFilter = ' AND IsBOM eq false AND isJtlBom eq false'
  }

  const [res, returnLocatorIds]: any = await Promise.all([
    // C_Country_ID is expanded because the flag needs the country's CountryCode —
    // the plain FK object only carries id + identifier.
    event.context.fetch(`models/m_product?$select=${select}&$expand=M_Storage,C_Country_ID&$filter=${string.urlEncode('(isActive eq true OR isActive eq false)' + orgFilter + bomFilter + filterHandler(body.filterModel))}&$orderby=${string.urlEncode(orderBy)}&$top=${pageSize}&$skip=${startRow}`, 'GET', token, null),
    getReturnLocatorIds(event, token, body?.orgId || null)
  ])

  if (res) {
    const allRecords = res['records']?.map((item: any) => {
      const { onHand, returnOnHand } = splitStorageByLocator(item?.M_Storage ?? [], returnLocatorIds)
      return {
        id: item.id,
        uid: item.uid,
        name: item.Name,
        value: item.Value,
        sku: item.SKU,
        mpn: item.mpn,
        upc: item.UPC,
        weight: item.Weight,
        width: item.width || item.Width || 0,
        length: item.length || item.Length || 0,
        height: item.height || item.Height || 0,
        volume: item.volume || item.Volume || 0,
        isBOM: item.IsBOM,
        isJtlBom: item.IsJtlBom ?? item.isJtlBom ?? false,
        organization: item.AD_Org_ID?.identifier || '',
        organizationId: item.AD_Org_ID?.id || '',
        strapiProductId: item.Strapi_Product_documentId,
        completedAllMarketplaceData: item.CompletedAllMarketplaceData,
        qtyonhand: onHand,
        returnQtyOnHand: returnOnHand,
        customFulfillmentPrice: item.CustomFulfillmentPrice || 0,
        isCustomFulfillmentPrice: item.isCustomFulfillmentPrice ?? false,
        fulfillmentProductPricingRules: item.CUST_FulfillmentProductPricingRules_ID?.identifier || '',
        fulfillmentProductPricingRulesId: item.CUST_FulfillmentProductPricingRules_ID?.id || '',
        labelPrinter: item.labelprinter?.identifier || '',
        labelPrinterId: item.labelprinter?.id || '',
        taricCode: item.taricCode ?? '',
        // expanded FK: full c_country record (identifier is null when expanded)
        originCountry: item.C_Country_ID?.Name ?? item.C_Country_ID?.identifier ?? '',
        originCountryCode: item.C_Country_ID?.CountryCode || '',
        originCountryId: item.C_Country_ID?.id || ''
      }
    }) || []

    data.rows = allRecords
    data.lastRow = res['row-count'] || 0
  }

  return data
}

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

  try {
    console.log('[Products AG Grid] handleFunc attempt 1')
    data = await handleFunc(event)
    console.log('[Products AG Grid] Success - rows:', data?.rows?.length, 'lastRow:', data?.lastRow)
  } catch (err: any) {
    console.log('[Products AG Grid] First attempt failed:', err?.statusCode || err?.message || err)
    try {
      let authToken: any = await refreshTokenHelper(event)
      console.log('[Products AG Grid] Got refresh token, retrying handleFunc')
      data = await handleFunc(event, authToken)
      console.log('[Products AG Grid] Retry success - rows:', data?.rows?.length, 'lastRow:', data?.lastRow)
    } catch (error: any) {
      console.log('[Products AG Grid] Retry also failed:', error?.statusCode || error?.message || error)
      data = errorHandlingHelper(err?.data ?? err, error?.data ?? error)
      forceLogoutHelper(event, data)
    }
  }

  return data
})
