'use strict'
const cfg = require('./config')
const { log, json, parseBody, httpRequest, lookupCustomerByPhone, createCommunication } = require('./helpers')
const { broadcast, broadcastAll } = require('./sse')

let pbxToken = null, pbxTokenExpiry = 0
const processedCallIds = new Set()

async function get3cxToken () {
  if (pbxToken && Date.now() < pbxTokenExpiry - 60000) return pbxToken
  if (!cfg.PBX_USER || !cfg.PBX_PASS) return null
  try {
    const res = await httpRequest(cfg.PBX_URL, '/webclient/api/Login/GetAccessToken', {
      method: 'POST', body: { Username: cfg.PBX_USER, Password: cfg.PBX_PASS },
    })
    if (res.data.Status === 'AuthSuccess') {
      pbxToken = res.data.Token.access_token
      pbxTokenExpiry = Date.now() + (res.data.Token.expires_in * 1000)
      return pbxToken
    }
    log('3CX auth failed:', res.data.Status)
  } catch (e) { log('3CX auth error:', e.message) }
  return null
}

function formatDuration (sec) {
  return `${Math.floor(sec / 60)}m${(sec % 60).toString().padStart(2, '0')}s`
}

function parseIsoDuration (td) {
  if (typeof td === 'number') return td
  if (typeof td !== 'string' || !td.startsWith('PT')) return 0
  const hr = td.match(/(\d+)H/), mn = td.match(/(\d+)M/), sc = td.match(/(\d+)S/)
  return (hr ? parseInt(hr[1]) * 3600 : 0) + (mn ? parseInt(mn[1]) * 60 : 0) + (sc ? parseInt(sc[1]) : 0)
}

async function logCallToErp (remotePhone, isOutbound, duration, customerLabel, customerName, displayName) {
  if (!customerName || duration <= 0) return
  await createCommunication({
    communication_type: 'Communication', communication_medium: 'Phone',
    sent_or_received: isOutbound ? 'Sent' : 'Received',
    sender: 'sms@gigafibre.ca',
    sender_full_name: isOutbound ? (displayName || 'Targo Ops') : customerLabel,
    phone_no: remotePhone,
    content: `Appel ${isOutbound ? 'sortant vers' : 'entrant de'} ${remotePhone} — Duree: ${formatDuration(duration)}`,
    subject: `Appel ${isOutbound ? 'vers' : 'de'} ${remotePhone}`,
    reference_doctype: 'Customer', reference_name: customerName, status: 'Linked',
  })
}

async function callEvent (req, res) {
  const body = await parseBody(req)
  const eventType = body.event_type || body.EventType || body.type || ''
  const callId = body.call_id || body.CallId || body.id || ''
  const direction = (body.direction || body.Direction || '').toLowerCase()
  const caller = body.caller || body.Caller || body.from || ''
  const callee = body.callee || body.Callee || body.to || ''
  const ext = body.ext || body.Extension || body.extension || ''
  const duration = parseInt(body.duration || body.Duration || '0', 10)
  const status = body.status || body.Status || eventType

  const isOutbound = direction === 'outbound' || direction === 'out'
  const remotePhone = isOutbound ? callee : caller
  log(`3CX ${eventType}: ${caller} → ${callee} ext=${ext} dir=${direction} dur=${duration}s status=${status} (${callId})`)
  json(res, 200, { ok: true })

  setImmediate(async () => {
    try {
      const isCallEnd = ['ended', 'completed', 'hangup', 'Notified', 'missed'].some(
        s => eventType.toLowerCase().includes(s.toLowerCase()) || status.toLowerCase().includes(s.toLowerCase())
      )
      const customer = remotePhone ? await lookupCustomerByPhone(remotePhone) : null
      const customerName = customer?.name, customerLabel = customer?.customer_name || 'Inconnu'

      if (isCallEnd) await logCallToErp(remotePhone, isOutbound, duration, customerLabel, customerName)

      const eventData = {
        type: 'call', event: eventType, direction: isOutbound ? 'out' : 'in',
        customer: customerName, customer_name: customerLabel, phone: remotePhone,
        extension: ext, duration, status, call_id: callId, ts: new Date().toISOString(),
      }
      if (customerName) broadcast('customer:' + customerName, 'call-event', eventData)
      broadcastAll('call-event', eventData)
      log(`3CX logged: ${remotePhone} → ${customerName || 'UNKNOWN'} (${callId})`)
    } catch (e) { log('3CX call processing error:', e.message) }
  })
}

async function poll3cxCallLog () {
  const token = await get3cxToken()
  if (!token) return
  try {
    const since = new Date(Date.now() - 5 * 60 * 1000).toISOString()
    const url = `${cfg.PBX_URL}/xapi/v1/ReportCallLogData?$top=20&$orderby=StartTime%20desc&$filter=StartTime%20gt%20${encodeURIComponent(since)}`
    const res = await httpRequest(cfg.PBX_URL, url.replace(cfg.PBX_URL, ''), {
      headers: { Authorization: 'Bearer ' + token },
    })
    if (res.status !== 200 || !res.data?.value) return

    for (const call of res.data.value) {
      const callId = call.CdrId || call.CallHistoryId || ''
      if (!callId || processedCallIds.has(callId)) continue
      if (!call.TalkingDuration && !call.Answered) continue
      processedCallIds.add(callId)

      const direction = (call.Direction || '').toLowerCase().includes('inbound') ? 'in' : 'out'
      const isOutbound = direction === 'out'
      const remotePhone = isOutbound ? (call.DestinationCallerId || call.DestinationDn || '') : (call.SourceCallerId || call.SourceDn || '')
      const ext = isOutbound ? call.SourceDn : call.DestinationDn
      const durationSec = parseIsoDuration(call.TalkingDuration || '')
      if (durationSec < 3 && !call.Answered) continue

      log(`3CX POLL: ${call.SourceCallerId || call.SourceDn} → ${call.DestinationCallerId || call.DestinationDn} dir=${direction} dur=${durationSec}s (${callId})`)

      try {
        const customer = remotePhone ? await lookupCustomerByPhone(remotePhone) : null
        const customerName = customer?.name, customerLabel = customer?.customer_name || 'Inconnu'

        if (customerName) {
          const suffix = call.Answered ? '' : ' (manque)'
          await createCommunication({
            communication_type: 'Communication', communication_medium: 'Phone',
            sent_or_received: isOutbound ? 'Sent' : 'Received',
            sender: 'sms@gigafibre.ca',
            sender_full_name: isOutbound ? (call.SourceDisplayName || 'Targo Ops') : customerLabel,
            phone_no: remotePhone,
            content: `Appel ${isOutbound ? 'sortant vers' : 'entrant de'} ${remotePhone} — Duree: ${formatDuration(durationSec)}${suffix}`,
            subject: `Appel ${isOutbound ? 'vers' : 'de'} ${remotePhone}`,
            reference_doctype: 'Customer', reference_name: customerName, status: 'Linked',
          })
          log(`3CX logged to ERPNext: ${remotePhone} → ${customerName} (${formatDuration(durationSec)})`)
        }

        const eventData = {
          type: 'call', event: 'completed', direction, customer: customerName,
          customer_name: customerLabel, phone: remotePhone, extension: ext,
          duration: durationSec, answered: call.Answered, call_id: callId,
          ts: call.StartTime || new Date().toISOString(),
        }
        if (customerName) broadcast('customer:' + customerName, 'call-event', eventData)
        broadcastAll('call-event', eventData)
      } catch (e) { log('3CX poll processing error:', e.message) }
    }

    // Prune old IDs (keep last 500)
    if (processedCallIds.size > 500) {
      const arr = [...processedCallIds].slice(-500)
      processedCallIds.clear()
      arr.forEach(id => processedCallIds.add(id))
    }
  } catch (e) { log('3CX poll error:', e.message) }
}

function startPoller () {
  if (!cfg.PBX_ENABLED) { log('3CX poller: DISABLED (PBX_ENABLED != 1)'); return }
  if (!cfg.PBX_USER || !cfg.PBX_PASS) { log('3CX poller: DISABLED (no credentials)'); return }
  log(`3CX poller: ENABLED (every ${cfg.PBX_POLL_INTERVAL / 1000}s) → ${cfg.PBX_URL}`)
  setTimeout(poll3cxCallLog, 5000)
  setInterval(poll3cxCallLog, cfg.PBX_POLL_INTERVAL)
}

module.exports = { callEvent, startPoller }
