/**
 * Writes the "phone call made" C_ContactActivity (type PC) onto the called
 * AD_User once a click-to-dial call has ended.
 *
 * Runs DETACHED from any HTTP request (the call may last an hour), so it
 * never touches an h3 event: everything it needs is captured in the ctx at
 * dial time, and it authenticates with the iDempiere SERVICE token
 * (config.api.idempieretoken) rather than the user's session token, which may
 * have expired meanwhile. Payload mirrors server/utils/offers/contactActivity.ts.
 *
 * Fail-soft: logs and returns null on any error; never throws.
 */
import { string } from 'alga-js'

export interface PhoneCallContext {
  callId: string
  callerUserId: number
  callerName: string
  callerExtension: string
  orgId: number
  number: string
  targetUserId: number | null
  targetPartnerId: number | null
  targetLabel: string
  startedAt: number
}

export type TargetStatus = 'answered' | 'busy' | 'noanswer' | 'cancel' | 'congestion' | 'chanunavail' | 'unknown'

export interface PhoneCallOutcome {
  agentAnswered: boolean
  targetStatus: TargetStatus
  answeredAt: number | null
  endedAt: number
  hangupCause: string
  note: string
}

const noMs = (ts: number) => new Date(ts).toJSON().replace(/\.\d{3}Z$/, 'Z')

const fmtDuration = (ms: number) => {
  const s = Math.max(0, Math.round(ms / 1000))
  const m = Math.floor(s / 60)
  const r = s % 60
  return `${m}:${String(r).padStart(2, '0')} min`
}

const fmtTime = (ts: number | null) => ts ? new Date(ts).toLocaleString('de-DE', { timeZone: 'Europe/Berlin' }) : '–'

/** Common Q.850 cause texts (as Asterisk reports them) → German. */
const CAUSE_DE: Record<string, string> = {
  'Subscriber absent': 'Teilnehmer nicht erreichbar (Handy aus / kein Netz)',
  'User busy': 'besetzt',
  'No user responding': 'keine Antwort',
  'No answer from user': 'keine Antwort',
  'Call Rejected': 'Anruf abgewiesen',
  'Unallocated (unassigned) number': 'Rufnummer nicht vergeben',
  'Normal Clearing': 'normal beendet',
  'Normal, unspecified': 'beendet',
  'Interworking, unspecified': 'beendet',
  'Number changed': 'Rufnummer geändert',
  'Destination out of order': 'Ziel nicht erreichbar',
  'Channel unavailable': 'Leitung nicht verfügbar',
  'Switching equipment congestion': 'Netz überlastet',
  'Normal, circuit/channel congestion': 'Netz überlastet',
  'Bearer capability not authorized': 'nicht erlaubt'
}
export const causeText = (raw: string) => CAUSE_DE[String(raw || '').trim()] || String(raw || '').trim()

export const describeOutcome = (o: PhoneCallOutcome): { reached: boolean; short: string } => {
  if (!o.agentAnswered) return { reached: false, short: 'nicht erreicht (Anrufer hat nicht abgenommen)' }
  switch (o.targetStatus) {
    case 'answered': return { reached: true, short: `erreicht (${fmtDuration(o.endedAt - (o.answeredAt || o.endedAt))})` }
    case 'busy': return { reached: false, short: 'nicht erreicht (besetzt)' }
    case 'noanswer': return { reached: false, short: 'nicht erreicht (keine Antwort)' }
    case 'cancel': return { reached: false, short: 'nicht erreicht (abgebrochen)' }
    case 'congestion': return { reached: false, short: 'nicht erreicht (Netz überlastet / nicht vermittelbar)' }
    case 'chanunavail': return { reached: false, short: `nicht erreicht (${o.hangupCause && CAUSE_DE[o.hangupCause] ? CAUSE_DE[o.hangupCause] : 'Leitung nicht verfügbar'})` }
    default: return { reached: false, short: 'beendet (Ergebnis unbekannt)' }
  }
}

const serviceFetch = async (path: string, method: 'GET' | 'POST', body?: any) => {
  const config = useRuntimeConfig()
  const token = (config.api as any)?.idempieretoken
  if (!token) throw new Error('IDEMPIERETOKEN not configured')
  return await $fetch<any>(`${config.api.url}/${path}`, {
    method,
    headers: { Authorization: 'Bearer ' + token, Accept: 'application/json', 'Content-Type': 'application/json' },
    body,
    retry: 0,
    timeout: 15000
  })
}

/** Target user: explicit userId, else the partner's first active contact user. */
const resolveTargetUser = async (ctx: PhoneCallContext): Promise<{ userId: number; orgId: number } | null> => {
  if (ctx.targetUserId && ctx.targetUserId > 0) {
    const user = await serviceFetch(`models/ad_user/${ctx.targetUserId}`, 'GET')
    if (!user?.id) return null
    return { userId: Number(user.id), orgId: Number(user?.AD_Org_ID?.id || 0) }
  }
  if (ctx.targetPartnerId && ctx.targetPartnerId > 0) {
    const res = await serviceFetch(`models/ad_user?$filter=${string.urlEncode(`C_BPartner_ID eq ${ctx.targetPartnerId} AND IsActive eq true`)}&$orderby=Created asc&$top=1`, 'GET')
    const u = Array.isArray(res?.records) ? res.records[0] : null
    if (!u?.id) return null
    return { userId: Number(u.id), orgId: Number(u?.AD_Org_ID?.id || 0) }
  }
  return null
}

export const createPhoneCallActivity = async (ctx: PhoneCallContext, outcome: PhoneCallOutcome): Promise<any | null> => {
  try {
    const target = await resolveTargetUser(ctx)
    if (!target) {
      console.info(`[dial ${ctx.callId}] no target user/partner — activity skipped`)
      return null
    }
    const { short } = describeOutcome(outcome)
    const who = ctx.targetLabel ? `${ctx.targetLabel} (${ctx.number})` : ctx.number
    const description = `Anruf an ${who} – ${short}`.substring(0, 250)
    const lines = [
      `Telefonanruf über LogShip (Click-to-Dial)`,
      `Anrufer: ${ctx.callerName || ctx.callerUserId} (Nebenstelle ${ctx.callerExtension})`,
      `Gewählte Nummer: ${ctx.number}`,
      `Gestartet: ${fmtTime(ctx.startedAt)}`,
      `Anrufer abgenommen: ${outcome.agentAnswered ? 'ja' : 'nein'}`,
      `Gegenstelle: ${outcome.targetStatus}${outcome.answeredAt ? ` (angenommen ${fmtTime(outcome.answeredAt)})` : ''}`,
      `Beendet: ${fmtTime(outcome.endedAt)}${outcome.hangupCause ? ` – ${causeText(outcome.hangupCause)}` : ''}`,
      outcome.note ? `Hinweis: ${outcome.note}` : ''
    ].filter(Boolean)
    const orgId = ctx.orgId > 0 ? ctx.orgId : target.orgId
    const body: any = {
      AD_Org_ID: { id: orgId, tableName: 'AD_Org' },
      AD_User_ID: { id: target.userId, tableName: 'AD_User' },
      ContactActivityType: { id: 'PC' },
      description,
      comments: lines.join('\n').substring(0, 1990),
      StartDate: noMs(ctx.startedAt),
      EndDate: noMs(outcome.endedAt),
      isComplete: true,
      tableName: 'c_contactactivity'
    }
    if (ctx.callerUserId > 0) body.SalesRep_ID = { id: ctx.callerUserId, tableName: 'AD_User' }
    const res = await serviceFetch('models/c_contactactivity', 'POST', body)
    console.info(`[dial ${ctx.callId}] activity ${res?.id || '?'} written on user ${target.userId}: ${description}`)
    return res?.id ? res : null
  } catch (err: any) {
    console.warn(`[dial ${ctx.callId}] activity write failed:`, err?.data?.detail || err?.data?.message || err?.message || err)
    return null
  }
}
