import errorHandlingHelper from "../../../../utils/errorHandlingHelper"
import fetchHelper from "../../../../utils/fetchHelper"
import getTokenHelper from "../../../../utils/getTokenHelper"
import {
  dpdGetAuth,
  dpdStoreOrders,
  extractOrderSourceDpdCreds,
  xmlEscape,
  kgTo10GramUnits,
  toCm,
  buildVolumeString,
  iso2ToCustomsOrigin,
  type DpdCredentials
} from "../../../../utils/dpdHelper"

/**
 * Manual DPD label — create a label without a M_Inout reference.
 * Mirrors the DHL manual endpoint.
 */
export default defineEventHandler(async (event) => {
  const data: any = {}
  const body: any = await readBody(event)

  const country = String(body.country ?? '').toUpperCase().trim()

  // Normalize parcels: accept either body.parcels (array) for multi-parcel mode,
  // or fall back to the flat weight/length/width/height for single-parcel mode.
  const rawParcels: any[] = Array.isArray(body.parcels) && body.parcels.length > 0
    ? body.parcels
    : [{ weight: body.weight, length: body.length, width: body.width, height: body.height }]

  const parcels = rawParcels
    .map(p => ({
      weightKg: parseFloat(p.weight ?? 0) || 0,
      length: p.length,
      width: p.width,
      height: p.height
    }))
    .filter(p => p.weightKg > 0)

  if (!body.name || !body.address || !body.postalCode || !body.city || !country || parcels.length === 0) {
    return { status: 400, message: 'Missing required fields: name, address, postalCode, city, country, and at least one parcel with weight > 0' }
  }

  // If an OrderSource was picked, fetch it and extract DPD credentials.
  // Falls back to .env defaults when no OrderSource is provided or the
  // chosen one doesn't have DPD_USE_CUSTOM enabled.
  let customCreds: Partial<DpdCredentials> | null = null
  if (body.orderSourceId) {
    try {
      const token = await getTokenHelper(event)
      const orderSource: any = await fetchHelper(event, `models/c_ordersource/${body.orderSourceId}`, 'GET', token, null)
      const extracted = extractOrderSourceDpdCreds(orderSource)
      if (!extracted) {
        return {
          status: 400,
          message: `OrderSource ${body.orderSourceId} (${orderSource?.Name || '?'}) does not have DPD_USE_CUSTOM enabled.`
        }
      }
      if (extracted.missing.length > 0) {
        // Log the raw row so we can spot e.g. encryption-masked passwords coming back null.
        console.warn('[DPD Manual] OrderSource missing fields:', extracted.missing, {
          id: orderSource?.id,
          Name: orderSource?.Name,
          DPD_USE_CUSTOM: orderSource?.DPD_USE_CUSTOM,
          DPDURL_set: !!orderSource?.DPDURL,
          DPDDELISID_set: !!orderSource?.DPDDELISID,
          DPDPASSWORD_set: !!orderSource?.DPDPASSWORD,
          DPDCUSTOMERNUMBER_set: !!orderSource?.DPDCUSTOMERNUMBER
        })
        return {
          status: 400,
          message: `OrderSource ${body.orderSourceId} (${orderSource?.Name || '?'}) is missing required DPD field(s): ${extracted.missing.join(', ')}. Open Settings → Order Sources → ${orderSource?.Name || body.orderSourceId} and fill them in. If a value IS set in iDempiere but appears missing here, the column may be encrypted and not exposed via OData — check the AD_Column IsEncrypted flag for that field.`
        }
      }
      customCreds = extracted.creds
    } catch(err: any) {
      return { status: 500, message: `Failed to load OrderSource ${body.orderSourceId}: ${err?.message || err}` }
    }
  }

  let auth, creds
  try {
    const r = await dpdGetAuth(event, customCreds)
    auth = r.auth
    creds = r.creds
  } catch(err: any) {
    return { status: 500, message: err?.message || 'DPD authentication failed' }
  }

  const refNo = `MANUAL-${Date.now()}`

  // Build one <parcels> element per parcel (DPD's "MPS" multi-parcel format).
  // Each parcel gets +0.25kg packaging weight to match DHL's behavior.
  const parcelsXml = parcels.map(p => {
    const totalWeightKg = p.weightKg + 0.25
    const lengthCm = toCm(p.length)
    const widthCm = toCm(p.width)
    const heightCm = toCm(p.height)
    const hasDims = lengthCm > 0 && widthCm > 0 && heightCm > 0
    return `   <parcels>
    <weight>${kgTo10GramUnits(totalWeightKg)}</weight>
${hasDims ? `    <volume>${buildVolumeString(lengthCm, widthCm, heightCm)}</volume>\n` : ''}   </parcels>`
  }).join('\n')

  const isMulti = parcels.length > 1

  // ----- Customs / international block -----
  // Emitted only for non-EU destinations when the user supplied per-parcel
  // customs data. paymentTerm derived from the user's chosen incoterm:
  //   DDP → B (sender pays duties + taxes — no surprise fees for recipient)
  //   DAP → A (recipient pays duties + taxes)
  //   EXW → A (recipient pays everything)
  const EU_COUNTRIES = ['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','ES','SE']
  const isNonEU = !EU_COUNTRIES.includes(country)
  const customsInput = body.customs || {}
  const customsItems = rawParcels
    .map((p: any) => ({
      description: String(p.description || '').trim(),
      hsCode: String(p.hsCode || '').trim(),
      countryOfOrigin: String(p.countryOfOrigin || '').trim().toUpperCase().substring(0, 2),
      quantity: Math.max(1, parseInt(p.quantity || 1) || 1),
      unitValue: parseFloat(p.value) || 0,
      weightKg: parseFloat(p.weight ?? 0) || 0
    }))
    .filter((it: any) => it.description && it.hsCode && it.countryOfOrigin && it.unitValue > 0)

  const totalCustomsValue = customsItems.reduce((s: number, it: any) => s + it.unitValue * it.quantity, 0)
  const customsCurrency = String(customsInput.currency || 'EUR').toUpperCase().substring(0, 3)
  const incoterm = ['EXW', 'DAP', 'DDP'].includes(customsInput.incoterm) ? customsInput.incoterm : 'DDP'
  const paymentTerm = incoterm === 'DDP' ? 'B' : 'A'
  const reasonForExport = ['01', '02', '03'].includes(customsInput.reasonForExport) ? customsInput.reasonForExport : '01'

  const internationalXml = (isNonEU && customsItems.length > 0) ? `    <international>
     <parcelType>false</parcelType>
     <customsAmount>${Math.round(totalCustomsValue * 100)}</customsAmount>
     <customsCurrency>${xmlEscape(customsCurrency)}</customsCurrency>
     <customsAmountExport>${Math.round(totalCustomsValue * 100)}</customsAmountExport>
     <customsCurrencyExport>${xmlEscape(customsCurrency)}</customsCurrencyExport>
     <customsTerms>${incoterm === 'DDP' ? '02' : '01'}</customsTerms>
     <customsPaper>A</customsPaper>
     <customsEnclosure>true</customsEnclosure>
     <numberOfArticle>${Math.min(99, customsItems.length)}</numberOfArticle>
${body.shipperEori ? `     <commercialInvoiceConsignorVatNumber>${xmlEscape(String(body.shipperEori).substring(0, 20))}</commercialInvoiceConsignorVatNumber>
     <commercialInvoiceConsignor>
      <name1>LogYou GmbH</name1>
      <street>Mühlenweg</street>
      <houseNo>4</houseNo>
      <country>DE</country>
      <zipCode>35510</zipCode>
      <city>Butzbach</city>
      <customerNumber>${xmlEscape(creds.customerNumber.substring(0, 17))}</customerNumber>
     </commercialInvoiceConsignor>\n` : ''}${customsItems.slice(0, 99).map((it: any, i: number) => {
  // V4_4 has no dedicated HS code field — embed in description text
  const desc = it.hsCode ? `${it.description} [HS: ${it.hsCode}]` : it.description
  return `     <additionalInvoiceLines>
      <customsInvoicePosition>${i + 1}</customsInvoicePosition>
      <quantityItems>${it.quantity}</quantityItems>
      <customsContent>${xmlEscape(String(desc).substring(0, 200))}</customsContent>
      <customsAmountLine>${Math.round(it.unitValue * it.quantity * 100)}</customsAmountLine>
      <customsOrigin>${iso2ToCustomsOrigin(it.countryOfOrigin)}</customsOrigin>
      <customsNetWeight>${kgTo10GramUnits(it.weightKg)}</customsNetWeight>
      <customsGrossWeight>${kgTo10GramUnits(it.weightKg)}</customsGrossWeight>
     </additionalInvoiceLines>`
}).join('\n')}
    </international>` : ''

  const orderInner = `   <generalShipmentData>
    <mpsCustomerReferenceNumber1>${xmlEscape(refNo.substring(0, 35))}</mpsCustomerReferenceNumber1>
    <sendingDepot>${xmlEscape(auth.depot)}</sendingDepot>
    <product>CL</product>
    <sender>
     <name1>LogYou GmbH</name1>
     <street>Mühlenweg</street>
     <houseNo>4</houseNo>
     <country>DE</country>
     <zipCode>35510</zipCode>
     <city>Butzbach</city>
     <customerNumber>${xmlEscape(creds.customerNumber.substring(0, 17))}</customerNumber>
     <phone>+4960339160570</phone>
     <email>info@logyou.de</email>
    </sender>
    <recipient>
     <name1>${xmlEscape(String(body.name).substring(0, 35))}</name1>
     <street>${xmlEscape(String(body.address).substring(0, 35))}</street>
     ${body.houseNumber ? `<houseNo>${xmlEscape(String(body.houseNumber).substring(0, 8))}</houseNo>` : ''}
     <country>${xmlEscape(country.substring(0, 2))}</country>
     <zipCode>${xmlEscape(String(body.postalCode).substring(0, 9))}</zipCode>
     <city>${xmlEscape(String(body.city).substring(0, 35))}</city>
     ${body.phone ? `<phone>${xmlEscape(String(body.phone).substring(0, 30))}</phone>` : ''}
     ${body.email ? `<email>${xmlEscape(body.email)}</email>` : ''}
    </recipient>
   </generalShipmentData>
${parcelsXml}
   <productAndServiceData>
    <orderType>consignment</orderType>
    ${body.email && /@/.test(body.email) ? `<predict><channel>1</channel><value>${xmlEscape(body.email)}</value><language>DE</language></predict>` : ''}
${internationalXml}
   </productAndServiceData>`

  // splitByParcel=true gives one PDF per parcel; we want a single combined PDF for the response
  const storeOrdersInner = `   <printOptions>
    <printOption>
     <outputFormat>PDF</outputFormat>
     <paperFormat>A6</paperFormat>
    </printOption>
    <splitByParcel>${isMulti ? 'true' : 'false'}</splitByParcel>
   </printOptions>
   <order>
${orderInner}
   </order>`

  try {
    const { result } = await dpdStoreOrders(event, customCreds, storeOrdersInner)

    if (result.faults && result.faults.length > 0) {
      const detail = result.faults.map(f => `${f.code}: ${f.message}`).join(' | ')
      return { status: 400, message: detail, dpd_errors: result.faults.map(f => `${f.code}: ${f.message}`) }
    }

    if (!result.parcelLabelNumbers.length || !result.labelPdfBase64) {
      // Help diagnose response-shape mismatches by surfacing what DPD actually sent.
      const rawSnippet = (result.raw || '').substring(0, 4000)
      console.error('[DPD Manual] Response missing tracking/label. Raw XML (first 4000 chars):', rawSnippet)
      return {
        status: 500,
        message: 'DPD response missing tracking number or label',
        debug: {
          parsedMpsId: result.mpsId,
          parcelCount: result.parcelLabelNumbers.length,
          hasLabel: !!result.labelPdfBase64,
          faults: result.faults,
          rawSnippet
        }
      }
    }

    data['status'] = 200
    data['message'] = ''
    data['label_b64'] = result.labelPdfBase64
    data['tracking_number'] = result.parcelLabelNumbers[0]
    data['tracking_numbers'] = result.parcelLabelNumbers   // all parcels
    data['parcel_count'] = result.parcelLabelNumbers.length
    data['shipment_code'] = result.mpsId || result.parcelLabelNumbers[0]
    // Per-parcel labels for the frontend's preview navigation
    if (result.parcelLabels && result.parcelLabels.length > 0) {
      data['parcels'] = result.parcelLabels.map(p => ({
        tracking_number: p.trackingNumber,
        label_b64: p.pdfBase64
      }))
    }
    data['mpsId'] = result.mpsId
    data['product'] = 'CL'
    return data
  } catch(err: any) {
    const detail = err?.dpdFault ? `${err.dpdFault.code}: ${err.dpdFault.message}` : (err?.message || 'DPD API Error')
    console.error('[DPD Manual] DPD API rejected shipment:', detail)
    return { status: 400, message: detail, dpd_errors: [detail] }
  }
})
