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

async function smsIncoming (req, res) {
  const body = await parseBody(req)
  const from = body.From || '', to = body.To || '', text = body.Body || '', sid = body.MessageSid || ''
  log(`SMS IN: ${from} → ${to}: ${text.substring(0, 50)}...`)

  res.writeHead(200, { 'Content-Type': 'text/xml' })
  res.end('<Response></Response>')

  setImmediate(async () => {
    try {
      // Intercept tech absence commands (absent, absent demain, retour, etc.)
      const { handleAbsenceSms } = require('./tech-absence-sms')
      const wasAbsenceCmd = await handleAbsenceSms(from, text)
      if (wasAbsenceCmd) { log(`SMS handled as tech absence command: ${from}`); return }

      const customer = await lookupCustomerByPhone(from)
      let customerName = customer?.name, customerLabel = customer?.customer_name || 'Inconnu'

      const { findConversationByPhone, addMessage, autoLinkIncomingSms, triggerAgent } = require('./conversation')
      const existingConv = findConversationByPhone(from)
      if (existingConv) {
        addMessage(existingConv, { from: 'customer', text, via: 'sms' })
        log(`SMS routed to conversation ${existingConv.token}`)
        // Fall back to conversation's customer if ERP lookup failed
        if (!customerName && existingConv.customer) {
          customerName = existingConv.customer
          customerLabel = existingConv.customerName || 'Inconnu'
        }
        if (existingConv.customer) triggerAgent(existingConv)
      } else {
        // autoLinkIncomingSms already sends a welcome SMS — don't trigger agent on first contact
        // to avoid sending a duplicate AI reply immediately after the greeting
        await autoLinkIncomingSms(from, text)
      }

      if (customerName) {
        await createCommunication({
          communication_type: 'Communication', communication_medium: 'SMS',
          sent_or_received: 'Received', sender: from,
          sender_full_name: customerLabel, phone_no: from, content: text,
          subject: 'SMS from ' + from, reference_doctype: 'Customer',
          reference_name: customerName, message_id: sid, status: 'Open',
        })
      }

      const eventData = {
        type: 'sms', direction: 'in', customer: customerName,
        customer_name: customerLabel, phone: from, text, sid, ts: new Date().toISOString(),
      }
      let n = 0
      if (customerName) n = broadcast('customer:' + customerName, 'message', eventData)
      broadcastAll('sms-incoming', eventData)
      log(`SMS logged: ${from} → ${customerName || 'UNKNOWN'} (${sid}) broadcast=${n}`)
    } catch (e) { log('SMS processing error:', e.message) }
  })
}

async function smsStatus (req, res) {
  const body = await parseBody(req)
  const sid = body.MessageSid || body.SmsSid || ''
  const status = body.MessageStatus || body.SmsStatus || ''
  log(`SMS STATUS: ${sid} → ${status}`)
  res.writeHead(200, { 'Content-Type': 'text/xml' })
  res.end('<Response></Response>')
  setImmediate(() => broadcastAll('sms-status', { sid, status, ts: new Date().toISOString() }))
}

async function sendSmsInternal (phone, message, customer, mediaUrl) {
  // Normalize to E.164: strip non-digits, add +1 for 10-digit North American numbers
  const digits = phone.replace(/\D/g, '')
  const to = phone.startsWith('+') ? phone
    : digits.length === 10 ? '+1' + digits
    : digits.length === 11 && digits.startsWith('1') ? '+' + digits
    : '+' + digits
  const twilioData = new URLSearchParams({
    To: to, From: cfg.TWILIO_FROM, Body: message || '',
    StatusCallback: cfg.HUB_PUBLIC_URL + '/webhook/twilio/sms-status',
  })
  if (mediaUrl) twilioData.append('MediaUrl', mediaUrl) // MMS : pièce jointe image (URL publique)
  const authStr = Buffer.from(cfg.TWILIO_ACCOUNT_SID + ':' + cfg.TWILIO_AUTH_TOKEN).toString('base64')
  const https = require('https')
  const twilioRes = await new Promise((resolve, reject) => {
    const r = https.request({
      hostname: 'api.twilio.com',
      path: `/2010-04-01/Accounts/${cfg.TWILIO_ACCOUNT_SID}/Messages.json`,
      method: 'POST',
      headers: { Authorization: 'Basic ' + authStr, 'Content-Type': 'application/x-www-form-urlencoded' },
    }, (resp) => {
      let b = ''; resp.on('data', c => b += c)
      resp.on('end', () => { try { resolve({ status: resp.statusCode, data: JSON.parse(b) }) } catch { resolve({ status: resp.statusCode, data: b }) } })
    })
    r.on('error', reject); r.write(twilioData.toString()); r.end()
  })
  if (twilioRes.status >= 400) { log('Twilio send error:', twilioRes.data); return null }
  const sid = twilioRes.data.sid || ''
  log(`SMS OUT: → ${phone}: ${message.substring(0, 50)}... (${sid})`)
  return sid
}

async function sendSms (req, res) {
  const body = await parseBody(req)
  const { phone, message, customer } = body
  if (!phone || !message) return json(res, 400, { error: 'Missing phone or message' })

  const sid = await sendSmsInternal(phone, message, customer)
  if (!sid) return json(res, 502, { ok: false, error: 'Twilio error' })

  if (customer) {
    await createCommunication({
      communication_type: 'Communication', communication_medium: 'SMS',
      sent_or_received: 'Sent', sender: 'sms@gigafibre.ca', sender_full_name: 'Targo Ops',
      phone_no: phone, content: message, subject: 'SMS to ' + phone,
      reference_doctype: 'Customer', reference_name: customer, message_id: sid, status: 'Linked',
    })
    broadcast('customer:' + customer, 'message', {
      type: 'sms', direction: 'out', customer, phone, text: message, sid, ts: new Date().toISOString(),
    })
  }
  return json(res, 200, { ok: true, sid })
}

function voiceToken (req, res, url) {
  if (!cfg.TWILIO_API_KEY || !cfg.TWILIO_API_SECRET || !cfg.TWILIO_TWIML_APP_SID) {
    return json(res, 503, { error: 'Twilio Voice not configured' })
  }
  const identity = url.searchParams.get('identity') || req.headers['x-authentik-email'] || 'ops-agent'
  try {
    const twilio = require('twilio')
    const token = new twilio.jwt.AccessToken(
      cfg.TWILIO_ACCOUNT_SID, cfg.TWILIO_API_KEY, cfg.TWILIO_API_SECRET,
      { identity, ttl: 3600 }
    )
    token.addGrant(new twilio.jwt.AccessToken.VoiceGrant({
      outgoingApplicationSid: cfg.TWILIO_TWIML_APP_SID, incomingAllow: true,
    }))
    log(`Voice token generated for ${identity}`)
    return json(res, 200, { token: token.toJwt(), identity })
  } catch (e) {
    log('Voice token error:', e.message)
    return json(res, 500, { error: 'Token generation failed: ' + e.message })
  }
}

function sipConfig (req, res) {
  return json(res, 200, {
    wssUrl: cfg.SIP_WSS_URL, domain: cfg.SIP_DOMAIN,
    extension: cfg.SIP_EXTENSION, authId: cfg.SIP_AUTH_ID,
    authPassword: cfg.SIP_AUTH_PASSWORD, displayName: cfg.SIP_DISPLAY_NAME,
    identity: cfg.SIP_EXTENSION,
  })
}

async function voiceTwiml (req, res) {
  const body = await parseBody(req)
  const to = body.To || body.phone || ''
  log(`Voice TwiML: dialing ${to}`)
  const callerId = cfg.TWILIO_FROM || '+14382313838'
  const twiml = `<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial callerId="${callerId}" answerOnBridge="true" timeout="30">
    <Number statusCallbackEvent="initiated ringing answered completed"
            statusCallback="${cfg.HUB_PUBLIC_URL}/voice/status"
            statusCallbackMethod="POST">${to}</Number>
  </Dial>
</Response>`
  res.writeHead(200, { 'Content-Type': 'text/xml' })
  res.end(twiml)
}

async function voiceStatus (req, res) {
  const body = await parseBody(req)
  const { CallSid: callSid = '', CallStatus: callStatus = '', To: to = '', From: from = '' } = body
  const duration = parseInt(body.CallDuration || '0', 10)
  log(`Voice STATUS: ${callSid} ${from}→${to} status=${callStatus} dur=${duration}s`)

  res.writeHead(200, { 'Content-Type': 'text/xml' })
  res.end('<Response></Response>')

  if (callStatus === 'completed' && duration > 0) {
    setImmediate(async () => {
      try {
        const phone = to.startsWith('client:') ? from : to
        const customer = await lookupCustomerByPhone(phone)
        const customerName = customer?.name
        if (customerName) {
          const dMin = Math.floor(duration / 60), dSec = duration % 60
          const durStr = `${dMin}m${dSec.toString().padStart(2, '0')}s`
          await createCommunication({
            communication_type: 'Communication', communication_medium: 'Phone',
            sent_or_received: 'Sent', sender: 'sms@gigafibre.ca', sender_full_name: 'Targo Ops',
            phone_no: phone, content: `Appel vers ${phone} — Duree: ${durStr}`,
            subject: `Appel vers ${phone}`, reference_doctype: 'Customer',
            reference_name: customerName, status: 'Linked',
          })
          broadcast('customer:' + customerName, 'call-event', {
            type: 'call', event: 'completed', direction: 'out',
            customer: customerName, phone, duration, call_id: callSid, ts: new Date().toISOString(),
          })
          log(`Voice logged: ${phone} → ${customerName} (${durStr})`)
        }
      } catch (e) { log('Voice status processing error:', e.message) }
    })
  }
}

// Vérif de signature Twilio (X-Twilio-Signature) : HMAC-SHA1 base64 de (URL complète + concat TRIÉE des clés+valeurs
// des params POST form-encoded), clé = TWILIO_AUTH_TOKEN. true = VALIDE (ou non vérifiable → fail-open prudent, on ne
// casse pas les appels). L'URL doit être EXACTEMENT celle vue par Twilio → reconstruite via X-Forwarded-Proto/Host (Traefik).
function verifyTwilioSignature (req, params) {
  try {
    const token = cfg.TWILIO_AUTH_TOKEN; if (!token) return true // non configuré → ne pas bloquer
    const sig = req.headers['x-twilio-signature']; if (!sig) return false
    const crypto = require('crypto')
    const proto = String(req.headers['x-forwarded-proto'] || 'https').split(',')[0].trim()
    const host = String(req.headers['x-forwarded-host'] || req.headers.host || '').split(',')[0].trim()
    let data = proto + '://' + host + req.url
    for (const k of Object.keys(params || {}).sort()) data += k + String(params[k])
    const expected = crypto.createHmac('sha1', token).update(Buffer.from(data, 'utf-8')).digest('base64')
    const a = Buffer.from(sig); const b = Buffer.from(expected)
    return a.length === b.length && crypto.timingSafeEqual(a, b)
  } catch (e) { return true } // erreur inattendue → fail-open (ne pas casser appels/SMS)
}

module.exports = { smsIncoming, smsStatus, sendSms, sendSmsInternal, voiceToken, sipConfig, voiceTwiml, voiceStatus, verifyTwilioSignature }
