import fetchHelper from './fetchHelper'

/**
 * Self-service access to the merchant's own company C_BPartner record
 * (used by /api/accounts/company-profile GET/PUT).
 *
 * Security model:
 *  - A limited customer role ('c') is denied/filtered on ad_user / ad_org /
 *    c_bpartner via REST, so all iDempiere reads/writes here use the service
 *    token (config.api.idempieretoken) — same pattern as
 *    materials/products/image-permission.get.ts and adUserCredentials.ts.
 *  - BECAUSE the service token bypasses role security, the routes must never
 *    forward the raw record or accept arbitrary fields. Everything that
 *    crosses the browser boundary is projected through the whitelist below.
 *  - The target partner is always resolved server-side from the CALLER's own
 *    identity (JWT payload first, cookie fallback) — the client cannot choose
 *    a partner id.
 */

/**
 * Whitelist of C_BPartner fields a merchant may see/edit through
 * /api/accounts/company-profile. Extend HERE when the merchant self-service
 * page gains fields — nothing outside this list ever crosses the network or
 * is written to iDempiere.
 *  - column:   DB column name, matched case-insensitively on reads
 *              (iDempiere OData response casing varies by model)
 *  - writeKey: lowercase-first key required by the iDempiere REST write API
 */
export const MERCHANT_PARTNER_FIELDS: Record<string, { column: string, writeKey: string }> = {
  eMail: { column: 'EMail', writeKey: 'eMail' },
  eMailInvoice: { column: 'EMail_Invoice', writeKey: 'eMail_Invoice' },
  returnEmail: { column: 'return_email', writeKey: 'return_email' },
  freightServiceSenderMail: { column: 'freight_service_sender_mail', writeKey: 'freight_service_sender_mail' }
}

// Case-insensitive column read — iDempiere response casing varies by model.
export const readPartnerFieldCI = (record: any, column: string): any => {
  if (!record || typeof record !== 'object') return ''
  const target = column.toLowerCase()
  for (const k of Object.keys(record)) {
    if (k.toLowerCase() === target) return record[k] ?? ''
  }
  return ''
}

// Projects a raw c_bpartner record down to the whitelisted fields only.
export const pickMerchantFields = (record: any): Record<string, any> => {
  const out: Record<string, any> = {}
  for (const [key, def] of Object.entries(MERCHANT_PARTNER_FIELDS)) {
    out[key] = readPartnerFieldCI(record, def.column)
  }
  return out
}

// The session JWT carries AD_User_ID — prefer it over the client-writable
// logship_user_id cookie as the identity source.
const decodeJwtPayload = (token: string): any => {
  try {
    const payload = token.split('.')[1]
    return JSON.parse(Buffer.from(payload, 'base64').toString())
  } catch {
    return null
  }
}

/** The caller's own AD_User_ID (JWT payload first, cookie fallback), or null. */
export const resolveOwnUserId = (event: any): number | null => {
  const sessionToken = getCookie(event, 'logship_it')
  const jwtUserId = sessionToken ? Number(decodeJwtPayload(sessionToken)?.AD_User_ID) : NaN
  const userId = Number.isFinite(jwtUserId) && jwtUserId > 0
    ? jwtUserId
    : Number(getCookie(event, 'logship_user_id'))
  return Number.isFinite(userId) && userId > 0 ? userId : null
}

/**
 * Resolves the caller's own company partner:
 *   ad_user.AD_Org_ID -> ad_org.C_BPartner_ID (the org's linked partner, the
 *   one fulfillment invoices are billed to), falling back to the user's own
 *   ad_user.C_BPartner_ID when the org has no linked partner.
 * Returns { id, record } or null when no partner can be resolved.
 */
export const resolveMerchantPartner = async (event: any, serviceToken: string): Promise<{ id: number, record: any } | null> => {
  const userId = resolveOwnUserId(event)
  if (!userId) return null

  const userRes: any = await fetchHelper(event, `models/ad_user/${userId}?$select=AD_Org_ID,C_BPartner_ID`, 'GET', serviceToken, null)

  const orgId = userRes?.AD_Org_ID?.id
  if (orgId && Number(orgId) > 0) {
    const orgRes: any = await fetchHelper(event, `models/ad_org/${orgId}?$expand=C_BPartner_ID`, 'GET', serviceToken, null)
    const bpartner = orgRes?.C_BPartner_ID
    if (bpartner?.id) return { id: Number(bpartner.id), record: bpartner }
  }

  const userPartnerId = userRes?.C_BPartner_ID?.id
  if (userPartnerId && Number(userPartnerId) > 0) {
    const bpartner: any = await fetchHelper(event, `models/c_bpartner/${userPartnerId}`, 'GET', serviceToken, null)
    if (bpartner?.id) return { id: Number(bpartner.id), record: bpartner }
  }

  return null
}
