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

const handleFunc = async (event: any, authToken: any = null) => {
  const token = authToken ?? await getTokenHelper(event)
  const query = getQuery(event)
  const role = getCookie(event, 'logship_role')
  const cookieOrgId = getCookie(event, 'logship_organization_id')

  const newUserRole = JSON.parse(role ?? '{}')
  const queryOrgId = query?.org_id as string
  const orgId = !newUserRole?.IsClientAdministrator
    ? (queryOrgId || cookieOrgId)
    : queryOrgId

  if (!orgId) {
    return { status: 400, message: 'Organization ID is required', locators: [], products: [], totalLocators: 0, totalProducts: 0 }
  }

  const now = new Date()
  const queryMonth = query?.month ? Number(query.month) : null
  const queryYear = query?.year ? Number(query.year) : null
  const selectedYear = queryYear || now.getFullYear()
  const selectedMonth = queryMonth || (now.getMonth() + 1)

  const startDate = `${selectedYear}-${String(selectedMonth).padStart(2, '0')}-01`
  const nextMonth = selectedMonth === 12 ? 1 : selectedMonth + 1
  const nextYear = selectedMonth === 12 ? selectedYear + 1 : selectedYear
  const endDate = `${nextYear}-${String(nextMonth).padStart(2, '0')}-01`

  // Fetch ALL transactions up to end of selected month (signed MovementQty so we can
  // compute the running balance per (locator, product) at the start of the month).
  const filter = string.urlEncode(
    `MovementDate lt '${endDate}' and AD_Org_ID eq ${orgId}`
  )

  const select = string.urlEncode('M_Locator_ID,MovementDate,MovementQty,M_Product_ID,AD_Org_ID')
  const expand = string.urlEncode('M_Product_ID($select=Name,Value,SKU)')
  const orderby = string.urlEncode('MovementDate')

  const pageSize = 5000
  let skip = 0
  let allRecords: any[] = []
  let orgName = ''

  while (true) {
    const res: any = await fetchHelper(
      event,
      `models/m_transaction?$select=${select}&$expand=${expand}&$filter=${filter}&$orderby=${orderby}&$top=${pageSize}&$skip=${skip}`,
      'GET',
      token,
      null
    )

    const records = res?.records || []
    if (records.length > 0 && !orgName) {
      orgName = records[0]?.AD_Org_ID?.identifier || ''
    }
    allRecords = allRecords.concat(records)

    if (records.length < pageSize) break
    skip += pageSize
    if (skip > 500000) break
  }

  // Group per (locator, product): cumulative qty up to end of M, and any movement in M.
  // A pair (L, P) is "stored during M" iff end-of-M qty >= 1 OR there was any movement in M
  // (any movement in M proves the item was physically there at some point — incoming brings
  // it in, outgoing/sale means it had to have been there to leave).
  type Group = {
    locatorId: number
    locatorName: string
    productId: number
    productName: string
    productValue: string
    productSku: string
    endQty: number
    firstDateInM: string | null
    lastDateInM: string | null
    hadMovementInM: boolean
  }

  const groups = new Map<string, Group>()

  for (const rec of allRecords) {
    const locator = rec?.M_Locator_ID
    const product = rec?.M_Product_ID
    if (!locator?.id || !product?.id) continue
    const date = rec?.MovementDate ? String(rec.MovementDate).slice(0, 10) : null
    if (!date) continue
    const qty = Number(rec?.MovementQty || 0)
    const inM = date >= startDate && date < endDate
    const key = `${locator.id}-${product.id}`

    let g = groups.get(key)
    if (!g) {
      g = {
        locatorId: locator.id,
        locatorName: locator.identifier || `Locator-${locator.id}`,
        productId: product.id,
        productName: product.Name || product.identifier || `Product-${product.id}`,
        productValue: product.Value || '',
        productSku: product.SKU || '',
        endQty: 0,
        firstDateInM: null,
        lastDateInM: null,
        hadMovementInM: false
      }
      groups.set(key, g)
    }

    g.endQty += qty
    if (inM) {
      g.hadMovementInM = true
      if (!g.firstDateInM || date < g.firstDateInM) g.firstDateInM = date
      if (!g.lastDateInM || date > g.lastDateInM) g.lastDateInM = date
    }
  }

  // Aggregate to unique locators and products
  const locatorMap = new Map<number, any>()
  const productMap = new Map<number, any>()

  const upsert = (map: Map<number, any>, id: number, base: Record<string, any>, g: Group, hasStock: boolean) => {
    let entry = map.get(id)
    if (!entry) {
      entry = {
        id,
        ...base,
        firstDate: null,
        hasStock: false,
        hadMovementInM: false
      }
      map.set(id, entry)
    }
    entry.hasStock = entry.hasStock || hasStock
    entry.hadMovementInM = entry.hadMovementInM || g.hadMovementInM
    if (g.firstDateInM) {
      if (!entry.firstDate || g.firstDateInM < entry.firstDate) entry.firstDate = g.firstDateInM
    }
  }

  for (const g of groups.values()) {
    const hasStock = g.endQty >= 1
    const used = hasStock || g.hadMovementInM
    if (!used) continue

    upsert(locatorMap, g.locatorId, { name: g.locatorName }, g, hasStock)
    upsert(productMap, g.productId, { name: g.productName, value: g.productValue, sku: g.productSku }, g, hasStock)
  }

  const withSource = (entry: any) => ({
    ...entry,
    source: entry.hasStock && entry.hadMovementInM
      ? 'both'
      : entry.hasStock
        ? 'on-hand'
        : 'movement-only'
  })

  const sortByName = (a: any, b: any) =>
    a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })

  const locators = Array.from(locatorMap.values()).map(withSource).sort(sortByName)
  const products = Array.from(productMap.values()).map(withSource).sort(sortByName)

  return {
    status: 200,
    locators,
    products,
    totalLocators: locators.length,
    totalProducts: products.length,
    selectedMonth,
    selectedYear,
    startDate,
    endDate,
    orgId: Number(orgId),
    orgName
  }
}

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

  try {
    data = await handleFunc(event)
  } catch (err: any) {
    try {
      const 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
})
