<template>
  <teleport to="body">
    <transition name="slide-up">
      <div v-if="show" class="phone-modal" :class="{ 'phone-minimized': minimized }">
        <div v-if="minimized" class="phone-mini-bar" @click="minimized = false">
          <q-icon name="phone_in_talk" size="18px" color="green" />
          <span class="text-weight-medium q-ml-xs">{{ dialNumber || 'Telephone' }}</span>
          <q-space />
          <span class="text-caption text-green-7">{{ formattedDuration }}</span>
          <q-icon name="open_in_full" size="14px" color="grey-6" class="q-ml-sm" />
        </div>

        <div v-else class="phone-panel">
          <div class="phone-header">
            <q-icon name="phone" size="20px" color="primary" class="q-mr-xs" />
            <span class="text-weight-bold" style="font-size:1rem">Telephone</span>
            <q-space />
            <q-badge v-if="deviceReady" color="green" class="q-mr-sm">Twilio</q-badge>
            <q-badge v-else-if="deviceError" color="red" class="q-mr-sm">Erreur</q-badge>
            <q-badge v-else color="orange" class="q-mr-sm"><q-spinner-dots size="10px" class="q-mr-xs" />Init</q-badge>
            <q-btn flat dense round size="xs" icon="minimize" color="grey-6" @click="minimized = true" />
            <q-btn flat dense round size="xs" icon="close" color="grey-6" @click="close" />
          </div>

          <div v-if="customerName" class="phone-contact">
            <q-icon name="person" size="18px" color="green-5" />
            <span class="q-ml-xs text-weight-medium">{{ customerName }}</span>
          </div>

          <div v-if="deviceError" class="phone-error">
            <q-icon name="error_outline" size="16px" color="red" class="q-mr-xs" />
            <span class="text-caption text-red">{{ deviceError }}</span>
          </div>

          <div v-if="!inCall" class="phone-dialer">
            <q-input v-model="dialNumber" dense outlined placeholder="+1 514 555 1234"
              :input-style="{ fontSize: '1.1rem', textAlign: 'center', letterSpacing: '1px' }"
              class="q-mb-sm" @keydown.enter="startCall">
              <template #prepend><q-icon name="dialpad" color="grey-5" /></template>
              <template #append>
                <q-icon v-if="dialNumber" name="backspace" color="grey-5" class="cursor-pointer"
                  @click="dialNumber = dialNumber.slice(0, -1)" size="18px" />
              </template>
            </q-input>
            <div class="dialpad q-mb-sm">
              <q-btn v-for="key in dialpadKeys" :key="key" flat dense class="dialpad-key" :label="key" @click="pressKey(key)" />
            </div>
            <div class="flex flex-center q-gutter-sm">
              <q-btn round color="green" icon="call" size="lg"
                :disable="!dialNumber.trim() || !deviceReady"
                :loading="connecting" @click="startCall">
                <q-tooltip>Appeler via WebRTC</q-tooltip>
              </q-btn>
            </div>
          </div>

          <div v-if="inCall" class="phone-call-view">
            <div class="call-status-bar">
              <div class="call-info">
                <div class="text-weight-bold" style="font-size:1.1rem">{{ dialNumber }}</div>
                <div class="text-caption" :class="connected ? 'text-green' : 'text-orange'">
                  <q-spinner-dots v-if="!connected" size="12px" class="q-mr-xs" />
                  {{ connected ? formattedDuration : callStatusText }}
                </div>
              </div>
            </div>
            <div class="call-controls">
              <div class="control-row">
                <div v-for="ctrl in callControls" :key="ctrl.label" class="control-btn" @click="ctrl.action">
                  <q-btn round flat :icon="ctrl.icon" size="md" :color="ctrl.color" />
                  <span class="text-caption">{{ ctrl.label }}</span>
                </div>
              </div>
              <div v-if="showDtmf" class="dialpad q-my-sm">
                <q-btn v-for="key in dialpadKeys" :key="'dtmf-'+key" flat dense class="dialpad-key" :label="key" @click="sendDtmf(key)" />
              </div>
              <div class="flex flex-center q-mt-sm">
                <q-btn round color="red" icon="call_end" size="lg" @click="endCall" />
              </div>
            </div>
          </div>

          <div v-if="callEnded" class="phone-log-bar">
            <q-icon name="check_circle" size="18px" color="green" class="q-mr-xs" />
            <span class="text-caption">Appel termine — {{ formattedDuration }}</span>
            <q-space />
            <q-btn dense unelevated size="sm" color="primary" icon="save" label="Logger" @click="logCall" :loading="loggingCall" />
          </div>

          <div class="phone-footer">
            <q-icon name="circle" size="8px" :color="deviceReady ? 'green' : 'grey-4'" class="q-mr-xs" />
            <span class="text-caption text-grey-6">Twilio WebRTC — {{ identity }}</span>
          </div>
        </div>
      </div>
    </transition>
  </teleport>
</template>

<script setup>
import { ref, watch, computed, onMounted, onUnmounted } from 'vue'
import { Notify } from 'quasar'
import { Device } from '@twilio/voice-sdk'

const HUB_URL = window.location.hostname === 'localhost' ? 'http://localhost:3300' : (import.meta.env.BASE_URL||'/').replace(/\/$/,'')+'/hub'

const props = defineProps({
  modelValue: { type: Boolean, default: false },
  initialNumber: { type: String, default: '' },
  customerName: { type: String, default: '' },
  customerErpName: { type: String, default: '' },
  provider: { type: String, default: 'twilio' },
})

const emit = defineEmits(['update:modelValue', 'call-ended'])

const show = ref(props.modelValue)
const minimized = ref(false)
const dialNumber = ref(props.initialNumber || '')
const inCall = ref(false)
const connected = ref(false)
const connecting = ref(false)
const callEnded = ref(false)
const muted = ref(false)
const speakerOn = ref(true)
const showDtmf = ref(false)
const loggingCall = ref(false)
const callStatusText = ref('Connexion...')
const deviceReady = ref(false)
const deviceError = ref('')
const identity = ref('')
const callStartTime = ref(null)
const callDurationSec = ref(0)
let durationTimer = null
let device = null
let activeCall = null
let audioElement = null
let heartbeatTimer = null

const dialpadKeys = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#']

const callControls = computed(() => [
  { label: muted.value ? 'Unmute' : 'Mute', icon: muted.value ? 'mic_off' : 'mic', color: muted.value ? 'red' : 'grey-7', action: toggleMute },
  { label: 'Clavier', icon: 'dialpad', color: 'grey-7', action: toggleDialpad },
  { label: 'HP', icon: speakerOn.value ? 'volume_up' : 'volume_off', color: speakerOn.value ? 'primary' : 'grey-7', action: toggleSpeaker },
])

watch(() => props.modelValue, v => { show.value = v })
watch(show, v => { emit('update:modelValue', v); if (v) initDevice() })
watch(() => props.provider, () => cleanupDevices())
watch(() => props.initialNumber, v => { if (v) dialNumber.value = v })

const formattedDuration = computed(() => {
  const m = Math.floor(callDurationSec.value / 60)
  const s = callDurationSec.value % 60
  return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`
})

async function initDevice () {
  if (!device) await initTwilioDevice()
}

async function initTwilioDevice () {
  deviceError.value = ''
  try {
    const res = await fetch(HUB_URL + '/voice/token')
    if (!res.ok) throw new Error('Token fetch failed: ' + res.status)
    const data = await res.json()
    identity.value = data.identity

    device = new Device(data.token, { edge: 'ashburn', logLevel: 'warn', codecPreferences: ['opus', 'pcmu'] })
    device.on('registered', () => { deviceReady.value = true; deviceError.value = ''; startHeartbeat() })
    device.on('error', err => { deviceError.value = err.message || 'Device error' })
    device.on('tokenWillExpire', async () => {
      try { const r = await fetch(HUB_URL + '/voice/token'); const d = await r.json(); device.updateToken(d.token) } catch {}
    })

    await device.register()
    deviceReady.value = true; startHeartbeat()

    device.on('incoming', call => {
      // Screen-pop softphone : le hub a passé le nom du compte + le CallSid parent en Parameters → Répondre / Transférer à l'IA / Refuser.
      const p = call.customParameters
      const get = k => { try { return p && p.get ? (p.get(k) || '') : '' } catch { return '' } }
      const account = get('account')
      const from = call.parameters.From || get('from') || 'Inconnu'
      const parentSid = get('parentCallSid')
      show.value = true; minimized.value = false
      dialNumber.value = from
      Notify.create({
        type: 'info', position: 'top-right', timeout: 25000, icon: 'phone_in_talk', color: account ? 'green-8' : 'blue-grey-8', textColor: 'white',
        message: account ? `Appel entrant — ${account}` : `Appel entrant — ${from}`,
        caption: account ? from : 'numéro non reconnu',
        actions: [
          { label: 'Répondre', color: 'white', handler: () => answerIncoming(call) },
          { label: 'Transférer à l\'IA', color: 'white', handler: () => { transferToAI(parentSid, from); try { call.reject() } catch {} } },
          { label: 'Refuser', color: 'white', handler: () => { try { call.reject() } catch {} } },
        ],
      })
    })
  } catch (e) {
    deviceError.value = e.message
  }
}

function cleanupDevices () {
  deviceReady.value = false; deviceError.value = ''
  if (device) { try { device.unregister(); device.destroy() } catch {} device = null }
  if (audioElement) audioElement.srcObject = null
}

// Battement « je suis en ligne » → le hub sait quels softphones faire sonner à l'appel entrant.
function startHeartbeat () {
  if (heartbeatTimer) return
  const ping = () => { if (identity.value) fetch(HUB_URL + '/voice/heartbeat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identity: identity.value }) }).catch(() => {}) }
  ping(); heartbeatTimer = setInterval(ping, 30000)
}
function stopHeartbeat () { if (heartbeatTimer) { clearInterval(heartbeatTimer); heartbeatTimer = null } }
// Transférer l'appel (entrant/en cours) vers l'agent IA : le hub redirige la jambe PSTN (REST Twilio).
async function transferToAI (parentSid, from) {
  if (!parentSid) return
  try { await fetch(HUB_URL + '/voice/to-ai', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ callSid: parentSid, from }) }) } catch {}
}

function getAudioElement () {
  if (!audioElement) {
    audioElement = document.createElement('audio')
    audioElement.autoplay = true
    document.body.appendChild(audioElement)
  }
  return audioElement
}

function answerIncoming (call) {
  activeCall = call; setupCallListeners(call); call.accept()
  inCall.value = true; connected.value = true; callEnded.value = false; startDurationTimer()
}

async function startCall () {
  if (!dialNumber.value.trim() || !deviceReady.value) return
  connecting.value = true; callEnded.value = false; callStatusText.value = 'Connexion...'

  let number = dialNumber.value.trim().replace(/[\s\-\(\)]/g, '')
  if (!number.startsWith('+')) {
    number = number.length === 10 ? '+1' + number : number.length === 11 && number.startsWith('1') ? '+' + number : '+' + number
  }

  await startTwilioCall(number)
}

async function startTwilioCall (number) {
  try {
    activeCall = await device.connect({ params: { To: number } })
    setupCallListeners(activeCall); inCall.value = true
  } catch (e) {
    deviceError.value = 'Appel echoue: ' + e.message; connecting.value = false
  }
}

function setupCallListeners (call) {
  call.on('ringing', () => { callStatusText.value = 'Sonnerie...'; connecting.value = false })
  call.on('accept', () => { connected.value = true; connecting.value = false; callStatusText.value = ''; startDurationTimer() })
  call.on('disconnect', () => {
    stopDurationTimer(); inCall.value = false; connected.value = false; connecting.value = false
    callEnded.value = true; muted.value = false; showDtmf.value = false; activeCall = null
  })
  call.on('cancel', () => { inCall.value = false; connected.value = false; connecting.value = false; callEnded.value = false; activeCall = null })
  call.on('error', err => { deviceError.value = err.message || 'Call error'; inCall.value = false; connected.value = false; connecting.value = false; activeCall = null })
}

function endCall () {
  if (!activeCall) return
  activeCall.disconnect()
}

function toggleMute () {
  if (!activeCall) return
  muted.value = !muted.value
  activeCall.mute(muted.value)
}

function toggleSpeaker () { speakerOn.value = !speakerOn.value }
function toggleDialpad () { showDtmf.value = !showDtmf.value }

function sendDtmf (digit) {
  if (!activeCall) return
  activeCall.sendDigits(digit)
}

function pressKey (key) { dialNumber.value += key }

function startDurationTimer () {
  callStartTime.value = Date.now(); callDurationSec.value = 0
  durationTimer = setInterval(() => { if (callStartTime.value) callDurationSec.value = Math.floor((Date.now() - callStartTime.value) / 1000) }, 1000)
}

function stopDurationTimer () { if (durationTimer) { clearInterval(durationTimer); durationTimer = null } }

async function logCall () {
  loggingCall.value = true
  const phone = dialNumber.value.trim()
  const duration = callDurationSec.value
  try {
    // Le doc « Communication » est créé par le PARENT (ChatterPanel.onCallEnded) = source UNIQUE (gère direction + rafraîchit le fil) → évite le DOUBLE enregistrement de chaque appel.
    emit('call-ended', { direction: 'out', remote: phone, duration })
    Notify.create({ type: 'positive', message: 'Appel enregistré', timeout: 2000 })
    callEnded.value = false
  } finally { loggingCall.value = false }
}

function close () {
  if (inCall.value) { minimized.value = true; return }
  stopDurationTimer(); show.value = false; inCall.value = false; connected.value = false
  callEnded.value = false; callStartTime.value = null; callDurationSec.value = 0
}

// Enregistrement EN ARRIÈRE-PLAN dès le montage (panneau caché) → l'agent reçoit les appels entrants sans ouvrir le clavier.
onMounted(() => { initDevice() })
onUnmounted(() => { stopDurationTimer(); stopHeartbeat(); cleanupDevices() })
</script>

<style scoped>
.phone-modal { position: fixed; bottom: 16px; right: 16px; z-index: 9998; width: 340px; background: white; border-radius: 16px; box-shadow: 0 8px 32px rgba(0,0,0,0.18), 0 2px 8px rgba(0,0,0,0.1); overflow: hidden; transition: all 0.3s ease; }
.phone-minimized { width: 260px; border-radius: 24px; }
.phone-mini-bar { display: flex; align-items: center; padding: 10px 16px; cursor: pointer; }
.phone-mini-bar:hover { background: #f5f5f5; }
.phone-panel { display: flex; flex-direction: column; }
.phone-header { display: flex; align-items: center; padding: 10px 12px 8px; border-bottom: 1px solid #f0f0f0; }
.phone-contact { display: flex; align-items: center; padding: 6px 16px; background: #f1f5f9; font-size: 0.88rem; }
.phone-error { display: flex; align-items: center; padding: 6px 12px; background: #fef2f2; }
.phone-dialer { padding: 12px 16px; }
.dialpad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; max-width: 200px; margin: 0 auto; }
.dialpad-key { font-size: 1.1rem !important; font-weight: 600; min-height: 38px; border-radius: 50%; }
.phone-call-view { display: flex; flex-direction: column; }
.call-status-bar { display: flex; align-items: center; padding: 16px; background: linear-gradient(135deg, #f0fdf0, #e8f5e9); }
.call-info { flex: 1; text-align: center; }
.call-controls { padding: 12px 16px; }
.control-row { display: flex; justify-content: space-around; align-items: center; }
.control-btn { display: flex; flex-direction: column; align-items: center; cursor: pointer; gap: 2px; }
.control-btn .text-caption { font-size: 0.7rem; color: #666; }
.phone-log-bar { display: flex; align-items: center; padding: 8px 12px; background: #f0fdf0; border-top: 1px solid #e0e0e0; }
.phone-footer { display: flex; align-items: center; padding: 5px 12px; border-top: 1px solid #f0f0f0; background: #fafafa; }
.slide-up-enter-active, .slide-up-leave-active { transition: all 0.3s ease; }
.slide-up-enter-from, .slide-up-leave-to { transform: translateY(20px); opacity: 0; }
</style>
