/**
 * Attach a file to an iDempiere record via the standard Strapi flow (the same
 * mechanism AttachmentModal.vue / mobile tasks / product galleries use):
 *   1. resolve the AD_Table_ID from the table name,
 *   2. find or create the ad-attachment record (AD_Table_ID + Record_ID),
 *   3. upload the bytes to Strapi (/upload).
 * Returns the Strapi attachment id + the uploaded file URL.
 *
 * Reused by the inbox upload (attach to CUST_IncomingInvoice) and confirm
 * (attach to the created C_Invoice).
 */
import { string } from 'alga-js'
import fetchHelper from '../fetchHelper'
import strapiHelper from '../strapiHelper'

// Frontend attachment table ids (mirror server/api/attachments/[table]/[id]/index.post.ts).
// PO_Invoice (3182) and Lead_User (1142) are NOT real iDempiere tables, so the ad_table
// lookup misses and we need this fallback. Lead_User is the target the lead page's
// AttachmentModal filters on — quote PDFs attached with it show up there.
const TABLE_ID_FALLBACK: Record<string, number> = { C_Invoice: 318, PO_Invoice: 3182, C_BPartner: 291, AD_User: 114, Lead_User: 1142 }

export const resolveTableId = async (event: any, token: string, tableName: string): Promise<number> => {
  try {
    const res: any = await fetchHelper(event, `models/ad_table?$filter=${string.urlEncode(`TableName eq '${tableName}'`)}`, 'GET', token, null)
    if (res?.records?.[0]?.id) return res.records[0].id
  } catch {}
  return TABLE_ID_FALLBACK[tableName] || 0
}

export const attachToStrapi = async (event: any, token: string, params: {
  tableName: string; recordId: number; recordUu?: string; buffer: Buffer; filename: string; mimeType?: string
}): Promise<{ strapiAttachmentId: number; fileUrl: string }> => {
  const tableId = await resolveTableId(event, token, params.tableName)

  let strapiId: number | null = null
  const existing: any = await strapiHelper(event, `ad-attachments?filters[AD_Table_ID][$eq]=${tableId}&filters[Record_ID][$eq]=${params.recordId}`, 'GET', null)
  if (existing?.data?.[0]?.id) {
    strapiId = existing.data[0].id
  } else {
    const created: any = await strapiHelper(event, 'ad-attachments', 'POST', {
      data: { AD_Table_ID: tableId, Record_ID: params.recordId, Record_UU: params.recordUu || undefined }
    })
    strapiId = created?.data?.id || null
  }
  if (!strapiId) throw new Error('Could not create attachment record')

  // NATIVE FormData + Blob (Node 18+): undici sets the multipart boundary and
  // streams the body correctly — exactly like curl/the browser. The `form-data`
  // npm package produced a body ofetch mis-sent, which Strapi rejected with a 500.
  // (Verified: curl to /api/upload returns 201, so the endpoint is fine and the
  // server-side encoding was the only bug.) field/ref/refId link the file to the
  // ad-attachment record so it shows on the iDempiere record.
  const fd: any = new FormData()
  fd.append('field', 'attachment')
  fd.append('ref', 'api::ad-attachment.ad-attachment')
  fd.append('refId', String(strapiId))
  fd.append('files', new Blob([params.buffer], { type: params.mimeType || 'application/pdf' }), params.filename)
  fd.append('fileInfo', JSON.stringify({ caption: String(params.recordId), alternativeText: params.filename }))

  // Standard Strapi endpoint on the dedicated server-side base STRAPIUPLOAD
  // (config.api.strapiupload, e.g. http://127.0.0.1:1337) — NOT /media-api/upload.
  const config: any = useRuntimeConfig()
  const uploadBase = (config.api?.strapiupload
    || (config.public?.strapi || config.api?.strapi || '').replace(/\/media-api\/?$/, '')
  ).replace(/\/$/, '')
  let up: any
  try {
    up = await $fetch(`${uploadBase}/api/upload`, {
      method: 'POST',
      headers: { Authorization: `Bearer ${config.api?.strapitoken}` }, // undici sets multipart Content-Type
      body: fd
    })
  } catch (err: any) {
    const detail = err?.data ?? err?.response?._data ?? err?.message
    console.error('[Inbox] Strapi /api/upload failed:', err?.response?.status || err?.statusCode,
      typeof detail === 'string' ? detail.slice(0, 600) : JSON.stringify(detail || {}).slice(0, 600))
    throw err
  }

  const file = Array.isArray(up) ? up[0] : (up?.[0] ?? up)
  const fileUrl = file?.url || ''
  return { strapiAttachmentId: strapiId, fileUrl }
}

/**
 * Fetch the bytes of a previously-uploaded Strapi file (by its stored url).
 *
 * The stored url is relative (`/uploads/xxx.pdf`). nginx does NOT proxy `/uploads`
 * (and the public host is the Nuxt app → 404 + Vue Router warnings), so we must
 * hit a base that actually serves the file. Topology (per nginx): files-api → :80/,
 * media-api → :1337/api, Strapi core on :1337. We try the known-good internal
 * routes in order until one returns bytes — robust without hard-coding one host.
 */
export const fetchStrapiFileBytes = async (event: any, fileUrl: string): Promise<Buffer | null> => {
  if (!fileUrl) return null
  const config: any = useRuntimeConfig()
  const auth = { Authorization: 'Bearer ' + config.api.strapitoken }

  // Already absolute → fetch directly.
  if (/^https?:\/\//i.test(fileUrl)) {
    try { return Buffer.from(await $fetch(fileUrl, { responseType: 'arrayBuffer', headers: auth })) } catch { return null }
  }

  const path = fileUrl.startsWith('/') ? fileUrl : '/' + fileUrl
  const pub = String(config.public?.strapi || '').replace(/\/$/, '')
  const candidates = [
    config.api?.strapiupload,                 // Strapi core direct (e.g. http://127.0.0.1:1337) — local provider serves /uploads
    'http://127.0.0.1:80',                    // files-api proxies to :80/ — uploads served here
    pub ? pub + '/files-api' : null           // public files-api route (last resort; goes through nginx)
  ].filter(Boolean).map((b: string) => String(b).replace(/\/$/, ''))

  for (const base of candidates) {
    try {
      const ab: any = await $fetch(base + path, { responseType: 'arrayBuffer', headers: auth })
      const buf = Buffer.from(ab)
      if (buf?.length) return buf
    } catch {}
  }
  console.warn('[Inbox] fetchStrapiFileBytes: file not reachable via', candidates.map(c => c + path).join('  |  '))
  return null
}

export default attachToStrapi
