import { string } from 'alga-js'
import fetchHelper from './fetchHelper'
import getTokenHelper from './getTokenHelper'

// Server-side BACKSTOP for m_inout creation: iDempiere refuses an m_inout
// without C_DocType_ID ("FillMandatory - Document Type"), and some callers
// legitimately end up with an empty docTypeId (e.g. a failed client-side
// doc-type lookup). Resolving a default here only ever runs when the body
// carried no doc type — a case that today is a guaranteed error — and is
// fail-soft: on lookup failure nothing is merged and the POST behaves exactly
// as before.
//
// Discriminators verified against production c_doctype (DocBaseType + IsSOTrx):
//   MMS: customer shipment → DocBaseType MMS, IsSOTrx Y  ("MM Shipment")
//   MCR: customer return   → DocBaseType MMR, IsSOTrx Y  ("MM Customer Return")
//   MMR: vendor receipt    → DocBaseType MMR, IsSOTrx N  ("MM Receipt")
//   MVR: vendor return     → DocBaseType MMS, IsSOTrx N  ("MM Vendor Return")

export type InOutDocKind = 'MMS' | 'MCR' | 'MMR' | 'MVR'

const TTL_MS = 10 * 60 * 1000
const cache = new Map<string, { id: number | null; at: number }>()

const FILTERS: Record<InOutDocKind, string> = {
  MMS: "DocBaseType eq 'MMS' AND IsSOTrx eq true",
  MCR: "DocBaseType eq 'MMR' AND IsSOTrx eq true",
  MMR: "DocBaseType eq 'MMR' AND IsSOTrx eq false",
  MVR: "DocBaseType eq 'MMS' AND IsSOTrx eq false",
}

// Name tie-breakers matching /api/types/[id]'s conventions.
const PREFERRED_NAME: Partial<Record<InOutDocKind, string>> = {
  MMS: 'MM Shipment',
  MCR: 'MM Customer Return',
  MMR: 'MM Receipt',
  MVR: 'MM Vendor Return',
}

// Map an inout create body (isSOTrx + MovementType) to the doc kind.
export function inOutDocKind(isSOTrx: any, movementType?: string | null): InOutDocKind {
  const so = isSOTrx === true || isSOTrx === 'true' || isSOTrx === 'Y'
  const mt = String(movementType ?? '')
  if (so) return mt === 'C+' ? 'MCR' : 'MMS'
  return mt === 'V-' ? 'MVR' : 'MMR'
}

export async function resolveDefaultInOutDocTypeId(event: any, kind: InOutDocKind, authToken: any = null): Promise<number | null> {
  const clientCookie = Number(getCookie(event, 'logship_client_id'))
  const clientId = Number.isFinite(clientCookie) && clientCookie > 0 ? clientCookie : null
  const key = `${clientId ?? 'tok'}:${kind}`
  const hit = cache.get(key)
  if (hit && Date.now() - hit.at < TTL_MS) return hit.id

  try {
    const token = authToken ?? await getTokenHelper(event)
    // The token is already client-scoped; the explicit AD_Client_ID clause is a
    // safety net against system-level doc types leaking in.
    const clientClause = clientId ? `AD_Client_ID eq ${clientId} AND ` : ''
    const res: any = await fetchHelper(
      event,
      `models/c_doctype?$filter=${string.urlEncode(clientClause + 'IsActive eq true AND ' + FILTERS[kind])}&$orderby=${string.urlEncode('C_DocType_ID')}`,
      'GET', token, null
    )
    const records: any[] = Array.isArray(res?.records) ? res.records : []
    const pick = records.find(r => r?.IsDefault === true)
      ?? records.find(r => r?.Name === PREFERRED_NAME[kind])
      ?? records[0]
    const id = pick?.id ?? null
    cache.set(key, { id, at: Date.now() })
    if (!id) console.warn('[inoutDocType] no doc type found for', kind, 'client', clientId)
    return id
  } catch (e: any) {
    console.warn('[inoutDocType] lookup failed for', kind, '—', e?.message)
    // stale-if-error; null → caller merges nothing (same error as before)
    return hit?.id ?? null
  }
}

// FK patch in the app's write shape; empty object when unresolved.
export function docTypeFkPatch(id: number | null): Record<string, any> {
  return id ? { C_DocType_ID: { id: id, tableName: 'C_DocType' } } : {}
}
