import refreshTokenHelper from "../../../utils/refreshTokenHelper"
import forceLogoutHelper from "../../../utils/forceLogoutHelper"
import errorHandlingHelper from "../../../utils/errorHandlingHelper"
import fetchHelper from "../../../utils/fetchHelper"

// Creates one m_production DRAFT header per selected order line (org + warehouse
// taken from the order, default locator of that warehouse, BOM & doctype resolved
// server-side), then runs the iDempiere "Create Production" process
// (m_production_create) to generate the production lines. The document is
// intentionally NOT completed — assembly is confirmed later.
const handleFunc = async (event: any, authToken: any = null) => {
  let data: any = {}
  const token = authToken ?? await getTokenHelper(event)
  const body = await readBody(event)
  const items: any[] = body?.items || []

  if(items.length === 0) {
    return { status: 422, message: 'No items to create productions for', results: [] }
  }

  // Pre-flight BEFORE any write: resolves the doctype AND surfaces auth errors so
  // the refresh-token retry can never re-run after headers were already created.
  const doctypeRes: any = await fetchHelper(event, `models/c_doctype?$filter=${encodeURIComponent("DocBaseType eq 'MMP' and IsActive eq true")}&$top=10`, 'GET', token, null)
  const doctypes: any[] = doctypeRes?.records || []
  if(doctypes.length === 0) {
    return { status: 422, message: 'No document type with DocBaseType MMP (Material Production) found', results: [] }
  }

  const today = new Date().toISOString().slice(0, 10)
  const locatorCache: any = {}
  const results: any[] = []

  for(const item of items) {
    const result: any = { orderLineId: item.orderLineId }
    try {
      // Order line + full order → org, warehouse, partner, open qty
      const line: any = await fetchHelper(event, `models/c_orderline/${item.orderLineId}?$expand=C_Order_ID`, 'GET', token, null)
      const order = line?.C_Order_ID
      if(!order) throw new Error(`Order line ${item.orderLineId} has no order`)

      const organizationId = order.AD_Org_ID?.id
      const warehouseId = order.M_Warehouse_ID?.id
      const productId = line.M_Product_ID?.id
      const openQty = Number(line.QtyOrdered ?? 0) - Number(line.QtyDelivered ?? 0)
      const qty = Number(item.qty || openQty)

      result.orderId = order.id
      result.orderDocumentNo = order.DocumentNo
      result.productId = productId
      result.productName = line.M_Product_ID?.identifier || ''
      result.qty = qty

      if(!(qty > 0)) throw new Error('Quantity to produce must be greater than 0')

      // Default locator of the order's warehouse (mostly 0,0,0), fallback: first active
      let locator = locatorCache[warehouseId]
      if(!locator) {
        const locRes: any = await fetchHelper(event, `models/m_locator?$filter=${encodeURIComponent(`M_Warehouse_ID eq ${warehouseId} and IsDefault eq true and IsActive eq true`)}&$top=1`, 'GET', token, null)
        locator = locRes?.records?.[0]
        if(!locator) {
          const anyLocRes: any = await fetchHelper(event, `models/m_locator?$filter=${encodeURIComponent(`M_Warehouse_ID eq ${warehouseId} and IsActive eq true`)}&$top=1`, 'GET', token, null)
          locator = anyLocRes?.records?.[0]
        }
        if(!locator) throw new Error(`No active locator found for warehouse ${order.M_Warehouse_ID?.identifier || warehouseId}`)
        locatorCache[warehouseId] = locator
      }

      // Current BOM & Formula of the product (same resolution as the process default:
      // BOMType 'A', BOMUse 'A', prefer the record of the order's org over org 0)
      const bomRes: any = await fetchHelper(event, `models/pp_product_bom?$filter=${encodeURIComponent(`M_Product_ID eq ${productId} and BOMType eq 'A' and BOMUse eq 'A' and IsActive eq true`)}&$top=10`, 'GET', token, null)
      const boms: any[] = bomRes?.records || []
      const bom = boms.find((b: any) => Number(b.AD_Org_ID?.id) === Number(organizationId))
        ?? boms.find((b: any) => Number(b.AD_Org_ID?.id) === 0)
        ?? boms[0]
      if(!bom) throw new Error(`No active BOM & Formula found for product ${line.M_Product_ID?.identifier || productId}`)

      const doctype = doctypes.find((d: any) => Number(d.AD_Org_ID?.id) === Number(organizationId))
        ?? doctypes.find((d: any) => Number(d.AD_Org_ID?.id) === 0)
        ?? doctypes[0]

      // 1. Production header (draft)
      let newObjValue: any = {}
      if(order.C_BPartner_ID?.id) {
        newObjValue = {...newObjValue,
          C_BPartner_ID: {
            id: order.C_BPartner_ID.id,
            tableName: 'C_BPartner'
          }
        }
      }
      if(order.DatePromised) {
        newObjValue = {...newObjValue, datePromised: String(order.DatePromised).slice(0, 10)}
      }
      const descriptionParts = [`Order ${order.DocumentNo} / Line ${line.Line}`]
      if(item.description) descriptionParts.push(item.description)

      const production: any = await fetchHelper(event, 'models/m_production', 'POST', token, {
        AD_Org_ID: {
          id: organizationId,
          tableName: 'AD_Org'
        },
        M_Product_ID: {
          id: productId,
          tableName: 'M_Product'
        },
        M_Locator_ID: {
          id: locator.id,
          tableName: 'M_Locator'
        },
        PP_Product_BOM_ID: {
          id: bom.id,
          tableName: 'PP_Product_BOM'
        },
        C_DocType_ID: {
          id: doctype.id,
          tableName: 'C_DocType'
        },
        C_OrderLine_ID: {
          id: line.id,
          tableName: 'C_OrderLine'
        },
        movementDate: item.movementDate || today,
        productionQty: qty,
        description: descriptionParts.join(' — '),
        ...newObjValue,
        tableName: 'm_production'
      })
      if(!production?.id) throw new Error('Production header could not be created')

      result.productionId = production.id
      result.productionDocumentNo = production.DocumentNo

      // 2. "Create Production" process — generates the production lines from the BOM.
      //    The document stays in DRAFT (no completion here).
      const processRes: any = await fetchHelper(event, 'processes/m_production_create', 'POST', token, {
        'record-id': production.id,
        'model-name': 'm_production',
        Recreate: 'Y',
        ProductionQty: qty,
        PP_Product_BOM_ID: bom.id
      })
      result.processSummary = processRes?.summary || ''
      if(processRes?.isError) {
        throw new Error(processRes?.summary || 'Create Production process failed')
      }

      // 3. Created production lines (end product + components) for the response
      const linesRes: any = await fetchHelper(event, `models/m_productionline?$filter=${encodeURIComponent(`M_Production_ID eq ${production.id}`)}&$orderby=Line&$top=500`, 'GET', token, null)
      result.lines = (linesRes?.records || []).map((l: any) => ({
        id: l.id,
        line: l.Line,
        productId: l.M_Product_ID?.id || '',
        product: l.M_Product_ID?.identifier || '',
        locator: l.M_Locator_ID?.identifier || '',
        movementQty: Number(l.MovementQty ?? 0),
        isEndProduct: l.IsEndProduct === true
      }))
      result.linesCreated = result.lines.filter((l: any) => !l.isEndProduct).length
      result.status = 200
    } catch(err: any) {
      result.status = 422
      result.message = err?.data?.detail || err?.data?.message || err?.message || 'Unknown error'
    }
    results.push(result)
  }

  const failed = results.filter((r: any) => Number(r.status) !== 200).length
  data = {
    status: 200,
    message: failed > 0 ? `${failed} of ${results.length} production(s) failed` : '',
    results
  }
  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) {
      data = errorHandlingHelper(err?.data ?? err, error?.data ?? error)
      forceLogoutHelper(event, data)
    }
  }

  return data
})
