<template>
  <q-dialog v-model="open" @hide="reset">
    <q-card class="orch-card column no-wrap">
      <q-card-section class="row items-center q-py-sm">
        <q-icon name="auto_awesome" color="deep-purple-6" size="22px" class="q-mr-sm" />
        <div class="text-subtitle1 text-weight-bold">{{ title }}</div>
        <q-space />
        <q-btn flat round dense icon="close" v-close-popup />
      </q-card-section>
      <q-separator />

      <!-- CONVERSATION (défile) — l'input est en bas, comme tout chat -->
      <q-card-section ref="scrollEl" class="col orch-body">
        <!-- État initial -->
        <div v-if="!thread.length && !diagnoses.length && !writeActions.length && !results.length" class="orch-empty">
          <q-icon name="auto_awesome" size="38px" color="deep-purple-3" />
          <div class="text-subtitle2 q-mt-sm text-grey-8">Comment puis-je aider ?</div>
          <div class="text-caption text-grey-6 q-mt-xs">« Vérifier le signal chez Louis-Paul » · « Allouer un technicien pour une panne fibre » · « Créer un ticket… »</div>
        </div>

        <!-- Fil de conversation (bulles) -->
        <div v-for="(m, i) in thread" :key="'m' + i" :class="['orch-msg', 'orch-msg-' + m.role]">
          <q-icon v-if="m.role === 'assistant'" name="auto_awesome" size="14px" color="deep-purple-6" class="q-mr-xs" />{{ m.text }}
        </div>

        <!-- Options cliquables proposées par l'assistant (désambiguïsation : quel client / entité ?) — remplace le 2e champ -->
        <q-list v-if="options.length" class="orch-options">
          <q-item v-for="(o, i) in options" :key="'o' + i" clickable v-ripple class="orch-option" @click="pickOption(o)">
            <q-item-section avatar><q-icon :name="o.icon || 'person'" size="18px" color="deep-purple-5" /></q-item-section>
            <q-item-section><q-item-label lines="1">{{ o.label }}</q-item-label><q-item-label caption lines="1">{{ o.caption }}</q-item-label></q-item-section>
            <q-item-section side><q-icon name="chevron_right" color="grey-5" /></q-item-section>
          </q-item>
        </q-list>

        <!-- Vérifications (lecture immédiate — pas de confirmation) -->
        <div v-if="diagnoses.length" class="q-mt-md">
          <div v-for="(a, i) in diagnoses" :key="'d' + i" class="orch-diag">
            <div class="row items-center q-mb-xs">
              <q-icon name="network_check" color="primary" size="18px" class="q-mr-sm" />
              <span class="text-weight-medium">Vérification</span>
              <span class="text-caption text-grey-6 q-ml-sm">« {{ a.customer_query }} »</span>
            </div>
            <!-- 1 client → diagnostic -->
            <template v-if="a.diagnostic && a.diagnostic.found">
              <div class="row items-center no-wrap">
                <span class="diag-dot" :style="{ background: dotColor(a.diagnostic.summary) }"></span>
                <div class="col">
                  <div class="text-weight-medium">{{ a.diagnostic.customer.customer_name }}</div>
                  <div class="text-caption text-grey-7">{{ a.diagnostic.location ? a.diagnostic.location.address : '—' }}</div>
                </div>
                <div class="text-right">
                  <div class="text-weight-medium" :style="{ color: dotColor(a.diagnostic.summary) }">{{ a.diagnostic.summary.label }}</div>
                  <div class="text-caption text-grey-6">{{ a.diagnostic.summary.detail }}<span v-if="a.diagnostic.summary.signal"> · {{ a.diagnostic.summary.signal }}</span></div>
                </div>
              </div>
              <div v-if="a.diagnostic.locations && a.diagnostic.locations.length > 1" class="text-caption text-warning q-mt-xs"><q-icon name="place" size="13px" /> {{ a.diagnostic.locations.length }} adresses — affichage de la principale</div>
            </template>
            <!-- plusieurs clients → préciser l'adresse -->
            <template v-else-if="a.diagnostic && a.diagnostic.ambiguous">
              <div class="text-caption text-warning q-mb-xs">Plusieurs clients — précise l'adresse :</div>
              <q-list dense bordered class="rounded-borders">
                <q-item clickable v-ripple v-for="(m, j) in a.diagnostic.matches" :key="j" @click="pickDiag(a, m)">
                  <q-item-section><q-item-label>{{ m.customer_name }}</q-item-label><q-item-label caption>{{ m.address || m.phone || m.email }}</q-item-label></q-item-section>
                  <q-item-section side><q-icon name="chevron_right" color="grey-5" /></q-item-section>
                </q-item>
              </q-list>
            </template>
            <div v-else-if="a.diagnostic && !a.diagnostic.found" class="text-caption text-negative">Client introuvable pour « {{ a.diagnostic.query || a.customer_query }} »</div>
            <div v-else class="text-caption text-grey-6"><q-spinner size="14px" /> diagnostic…</div>
          </div>
        </div>

        <!-- Plan d'actions à confirmer -->
        <div v-if="writeActions.length" class="q-mt-md">
          <div class="text-caption text-weight-medium text-grey-7 q-mb-xs">Plan ({{ writeActions.length }} action{{ writeActions.length > 1 ? 's' : '' }}) — vérifie puis confirme :</div>
          <div v-for="(a, i) in writeActions" :key="i" class="orch-act">
            <div class="row items-center no-wrap q-mb-xs">
              <q-icon :name="iconOf(a.type)" :color="colorOf(a.type)" size="18px" class="q-mr-sm" />
              <span class="text-weight-medium">{{ labelOf(a.type) }}</span>
              <q-badge v-if="a.type === 'create_ticket' && a.status === 'On Hold'" color="amber-1" text-color="amber-9" label="En attente" class="q-ml-sm" />
              <q-badge v-if="a.category" :label="a.category" color="grey-3" text-color="grey-8" class="q-ml-xs" />
              <q-space />
              <q-btn flat round dense size="xs" icon="delete" color="grey-5" @click="actions.splice(actions.indexOf(a), 1)" />
            </div>
            <!-- Aperçu générique (outils STAFF dispatch/planification : create_job, assign_tech, create_recurring_shift…) -->
            <div v-if="a.preview" class="text-body2 text-grey-8 q-mb-xs">{{ a.preview }}</div>
            <q-badge v-if="a.severity === 'high'" color="red-1" text-color="red-9" class="q-mb-xs"><q-icon name="warning" size="13px" class="q-mr-xs" />Action conséquente</q-badge>
            <!-- destinataire / client résolu -->
            <div v-if="a.candidates && a.candidates.length" class="q-mb-xs">
              <q-select dense outlined v-model="a.resolved" :options="a.candidates" option-label="customer_name" map-options emit-value
                :display-value="a.resolved ? a.resolved.customer_name : '— aucun —'" label="Client / destinataire" @update:model-value="onResolved(a)">
                <template #option="s">
                  <q-item v-bind="s.itemProps"><q-item-section>
                    <q-item-label>{{ s.opt.customer_name }}<q-badge v-if="s.opt.inactive" color="grey-4" text-color="grey-8" label="inactif" class="q-ml-xs" /></q-item-label>
                    <q-item-label caption>{{ s.opt.kind === 'technicien' ? '🔧 technicien · ' : '' }}{{ s.opt.address || s.opt.phone || s.opt.email }}{{ s.opt.can_sms ? ' · SMS ✓' : '' }}</q-item-label>
                  </q-item-section></q-item>
                </template>
              </q-select>
              <div v-if="a.type === 'send_sms' && a.resolved && !a.resolved.can_sms" class="text-caption text-warning"><q-icon name="warning" size="13px" /> pas de mobile — SMS impossible</div>
            </div>
            <div v-else-if="a.phone" class="text-caption text-grey-7 q-mb-xs"><q-icon name="call" size="13px" /> {{ a.phone }}</div>
            <div v-else-if="a.customer_query || a.to_query" class="text-caption text-warning q-mb-xs"><q-icon name="info" size="13px" /> « {{ a.customer_query || a.to_query }} » — non résolu</div>
            <!-- Adresse de service liée (compte multi-adresses → on choisit la bonne, pas la facturation) -->
            <div v-if="a.type === 'create_ticket' && a.resolved" class="q-mb-xs">
              <q-select v-if="a._locs && a._locs.length > 1" dense outlined v-model="a.service_location" :options="a._locs" option-label="address" option-value="name" emit-value map-options clearable label="Adresse de service">
                <template #prepend><q-icon name="place" color="warning" /></template>
              </q-select>
              <div v-else-if="a.resolved.address || a.service_location" class="text-caption text-grey-7"><q-icon name="place" size="13px" color="warning" /> {{ a.resolved.address || locLabel(a) }}</div>
            </div>
            <!-- contenu éditable -->
            <q-input v-if="a.type === 'create_ticket'" v-model="a.subject" dense outlined label="Sujet" />
            <q-input v-if="a.type === 'send_sms'" v-model="a.message" type="textarea" autogrow :rows="2" dense outlined label="Texto" />
            <template v-if="a.type === 'send_email'"><q-input v-model="a.subject" dense outlined label="Sujet" class="q-mb-xs" /><q-input v-model="a.body" type="textarea" autogrow :rows="2" dense outlined label="Courriel" /></template>
            <q-input v-if="a.type === 'note'" v-model="a.text" type="textarea" autogrow :rows="2" dense outlined label="Note" />
          </div>
          <!-- Confirmation INLINE dans le fil (pas de pied de page) -->
          <div v-if="!results.length" class="row justify-end q-gutter-sm q-mt-sm">
            <q-btn flat dense no-caps label="Annuler" color="grey-7" @click="actions = []" />
            <q-btn unelevated dense no-caps color="green-7" icon="send" label="Confirmer et exécuter" :loading="running" @click="run" />
          </div>
        </div>

        <!-- Résultats -->
        <div v-if="results.length" class="q-mt-md">
          <div v-for="(r, i) in results" :key="i" class="text-body2" :class="r.ok ? 'text-green-8' : 'text-negative'">
            <q-icon :name="r.ok ? 'check_circle' : 'error'" size="15px" /> {{ summaryOf(r) }}
          </div>
        </div>
      </q-card-section>

      <q-separator />
      <!-- COMPOSER (bas) : UN SEUL champ — écris ta demande / réponds à l'assistant. La désambiguïsation
           (quel client ?) se fait via les options cliquables que l'assistant affiche dans le fil, pas un 2e champ. -->
      <div class="orch-composer">
        <div class="row items-end no-wrap q-gutter-xs">
          <q-input v-model="text" type="textarea" autogrow :rows="1" outlined dense class="col orch-input" autofocus
            placeholder="Écris ta demande…  (Entrée pour envoyer)" @keyup.enter.exact.prevent="plan" />
          <q-btn round dense flat :color="listening ? 'red-6' : 'grey-7'" :icon="listening ? 'mic' : 'mic_none'" @click="toggleMic">
            <q-tooltip>{{ micSupported ? (listening ? 'Arrêter la dictée' : 'Dicter à voix') : 'Dictée non supportée ici' }}</q-tooltip>
          </q-btn>
          <q-btn round dense unelevated color="deep-purple-6" icon="send" :disable="!text.trim() || planning" :loading="planning" @click="plan">
            <q-tooltip>Envoyer</q-tooltip>
          </q-btn>
        </div>
      </div>
    </q-card>
  </q-dialog>
</template>

<script setup>
import { ref, computed, watch, nextTick } from 'vue'
import { useQuasar } from 'quasar'
import { HUB_URL } from 'src/config/hub'
import { useAuthStore } from 'src/stores/auth'

const props = defineProps({
  modelValue: Boolean,
  // Réutilisable pour DEUX surfaces NL (cf feedback_reuse_shared_components) : l'orchestrateur COMMS
  // (défauts /collab/orchestrate — tickets/SMS/courriel) ET le copilote STAFF dispatch/planif
  // (plan-endpoint="/staff-agent"). Défauts = comportement comms inchangé.
  initialText: { type: String, default: '' },       // pré-remplissage (ex. requête ⌘K) → auto-plan à l'ouverture
  title: { type: String, default: 'Commande dictée' },
  planEndpoint: { type: String, default: '/collab/orchestrate' },
  runEndpoint: { type: String, default: '/collab/orchestrate/run' },
  sendIdentity: { type: Boolean, default: false },   // joindre X-Authentik-Email (routes STAFF : agissent au nom de l'agent connecté)
})
const emit = defineEmits(['update:modelValue', 'done'])
const $q = useQuasar()

const open = ref(false)
const text = ref('')
// Options cliquables (désambiguïsation) proposées par l'assistant DANS le fil : « quel client / entité ? ». Un clic
// envoie le choix comme tour suivant → l'assistant continue avec l'id retenu. Remplace l'ancien 2e champ de recherche.
const options = ref([])
function pickOption (o) {
  options.value = []
  text.value = `${o.label} (${o.id})`
  plan()
}
const actions = ref([])
const results = ref([])
const reply = ref('')
const thread = ref([]) // fil de conversation (chat multi-tours) : [{ role:'user'|'assistant', text }]
const scrollEl = ref(null)
function scrollDown () { nextTick(() => { const el = scrollEl.value && scrollEl.value.$el; if (el) el.scrollTop = el.scrollHeight + 200 }) }

// En-têtes de requête : identité de l'agent connecté pour les routes STAFF (attribution + suivi + permissions).
function reqHeaders () {
  const h = { 'Content-Type': 'application/json' }
  if (props.sendIdentity) { const u = useAuthStore().user; if (u && u !== 'authenticated') h['X-Authentik-Email'] = u }
  return h
}
const planning = ref(false)
const running = ref(false)
const listening = ref(false)
const micSupported = !!(window.SpeechRecognition || window.webkitSpeechRecognition)
let recog = null

// Les vérifications (lecture) sont déjà exécutées dans le plan → séparées des actions à confirmer.
const diagnoses = computed(() => actions.value.filter(a => a.type === 'diagnose'))
const writeActions = computed(() => actions.value.filter(a => a.type !== 'diagnose'))
function dotColor (s) { return (s && s.online === true) ? '#16a34a' : (s && s.online === false) ? '#dc2626' : '#94a3b8' }
async function pickDiag (a, m) {
  a.diagnostic = null
  try { const r = await fetch(`${HUB_URL}/collab/service-status?customer=${encodeURIComponent(m.name)}`); if (r.ok) a.diagnostic = await r.json() } catch (e) { a.diagnostic = { ok: false, error: e.message } }
}

watch(() => props.modelValue, v => { open.value = v; if (v) { reset(); if (props.initialText) { text.value = props.initialText; nextTick(() => plan()) } } })
watch(() => thread.value.length + actions.value.length + results.value.length + options.value.length, scrollDown) // défile vers le bas à chaque nouveau contenu (comme un chat)
watch(open, v => emit('update:modelValue', v))
function reset () { text.value = ''; actions.value = []; results.value = []; reply.value = ''; thread.value = []; options.value = []; if (recog) { try { recog.stop() } catch (e) {} } listening.value = false }

const iconOf = t => ({ create_ticket: 'confirmation_number', send_sms: 'sms', send_email: 'mail', note: 'sticky_note_2', create_job: 'add_task', assign_tech: 'engineering', add_assistant: 'group_add', set_job_status: 'flag', reschedule_job: 'event_repeat', create_recurring_shift: 'event_available', follow_doc: 'notifications_active' }[t] || 'bolt')
const colorOf = t => ({ create_ticket: 'teal-7', send_sms: 'green-6', send_email: 'red-5', note: 'amber-7', create_job: 'indigo-6', assign_tech: 'indigo-6', add_assistant: 'blue-grey-6', set_job_status: 'deep-orange-6', reschedule_job: 'deep-purple-5', create_recurring_shift: 'teal-6', follow_doc: 'blue-6' }[t] || 'grey-7')
const labelOf = t => ({ create_ticket: 'Créer un ticket', send_sms: 'Envoyer un texto', send_email: 'Envoyer un courriel', note: 'Note interne', create_job: 'Créer un job', assign_tech: 'Assigner un technicien', add_assistant: 'Ajouter un assistant', set_job_status: 'Changer le statut', reschedule_job: 'Reporter le rendez-vous', create_recurring_shift: 'Horaire récurrent', follow_doc: 'Suivre' }[t] || t)
function summaryOf (r) {
  if (r.type === 'create_ticket') return r.ok ? `Ticket ${r.name} créé (${r.label || ''})` : 'Ticket : ' + (r.error || 'échec')
  if (r.type === 'send_sms') return r.ok ? `Texto envoyé à ${r.to} (${r.via})` : 'Texto : ' + (r.error || 'échec')
  if (r.type === 'send_email') return r.ok ? `Courriel envoyé à ${r.to}` : 'Courriel : ' + (r.error || 'échec')
  if (r.type === 'note') return 'Note : ' + (r.text || '')
  // Actions STAFF dispatch/planif (et repli générique) : l'exécuteur renvoie { message } / { error }.
  return (labelOf(r.type) || 'Action') + ' : ' + (r.message || r.error || (r.ok ? 'fait' : 'échec'))
}

function toggleMic () {
  const SR = window.SpeechRecognition || window.webkitSpeechRecognition
  if (!SR) { $q.notify({ type: 'warning', message: 'Dictée non supportée par ce navigateur' }); return }
  if (listening.value) { try { recog && recog.stop() } catch (e) {} return }
  recog = new SR(); recog.lang = 'fr-CA'; recog.interimResults = true; recog.continuous = false
  recog.onresult = e => { let t = ''; for (const r of e.results) t += r[0].transcript; text.value = t }
  recog.onend = () => { listening.value = false }
  recog.onerror = () => { listening.value = false }
  try { recog.start(); listening.value = true } catch (e) { listening.value = false }
}

async function plan () {
  const userText = text.value.trim()
  if (!userText || planning.value) return
  planning.value = true; results.value = []; options.value = [] // nouveau tour → efface les options du tour précédent
  thread.value.push({ role: 'user', text: userText })
  const history = thread.value.slice(0, -1).map(m => ({ role: m.role, content: m.text })) // tours précédents = contexte
  text.value = '' // vide le champ → prêt à RÉPONDRE à une question de l'assistant (chat)
  try {
    const r = await fetch(`${HUB_URL}${props.planEndpoint}`, { method: 'POST', headers: reqHeaders(), body: JSON.stringify({ text: userText, history }) })
    const d = await r.json()
    if (d.ok) {
      actions.value = d.actions || [] // plan du DERNIER tour (remplace le précédent)
      options.value = d.options || [] // candidats cliquables (désambiguïsation client/entité) proposés par l'assistant
      reply.value = d.reply || ''
      if (d.reply) thread.value.push({ role: 'assistant', text: d.reply }) // réponse/question de l'assistant → dans le fil
      for (const a of actions.value) if (a.type === 'create_ticket' && a.resolved) onResolved(a) // pré-charge les adresses de service
      if (!actions.value.length && !d.reply) $q.notify({ type: 'info', message: 'Aucune action détectée — reformule.' })
    } else { thread.value.push({ role: 'assistant', text: '⚠ ' + (d.error || 'Échec') }); $q.notify({ type: 'negative', message: d.error || 'Échec' }) }
  } catch (e) { thread.value.push({ role: 'assistant', text: '⚠ ' + e.message }); $q.notify({ type: 'negative', message: e.message }) } finally { planning.value = false }
}
// Quand le client résolu change (ou au chargement) : récupère ses adresses de service et présélectionne celle qui matche.
async function onResolved (a) {
  if (a.type !== 'create_ticket' || !a.resolved || !a.resolved.name) { a._locs = []; return }
  a.service_location = a.resolved.service_location || ''
  try {
    const r = await fetch(`${HUB_URL}/collab/service-locations?customer=${encodeURIComponent(a.resolved.name)}`)
    if (r.ok) { const d = await r.json(); a._locs = d.locations || []; if (!a.service_location && a._locs.length) a.service_location = a._locs[0].name } // adresse de service active la plus récente (triée 1re)
  } catch (e) { a._locs = [] }
}
function locLabel (a) { const l = (a._locs || []).find(x => x.name === a.service_location); return l ? l.address : a.service_location }
async function run () {
  running.value = true
  try {
    const r = await fetch(`${HUB_URL}${props.runEndpoint}`, { method: 'POST', headers: reqHeaders(), body: JSON.stringify({ actions: writeActions.value }) })
    const d = await r.json()
    results.value = d.results || []
    const ok = results.value.filter(x => x.ok).length
    $q.notify({ type: ok ? 'positive' : 'negative', message: `${ok}/${results.value.length} action(s) exécutée(s)` })
    emit('done')
  } catch (e) { $q.notify({ type: 'negative', message: e.message }) } finally { running.value = false }
}
</script>

<style scoped>
.orch-act { border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px; margin-bottom: 8px; background: #faf9ff; }
.orch-diag { border: 1px solid #e0e7ff; border-radius: 8px; padding: 10px; margin-bottom: 8px; background: #f5f7ff; }
.diag-dot { width: 11px; height: 11px; border-radius: 50%; margin-right: 10px; flex: 0 0 auto; }
.orch-reply { border-left: 3px solid #7c6cf6; background: #f6f5ff; padding: 8px 12px; border-radius: 0 8px 8px 0; color: #4a4a5a; font-size: 0.92rem; white-space: pre-wrap; }
/* Chat moderne : carte à hauteur fixe, conversation qui défile, composer en bas */
.orch-card { width: 560px; max-width: 96vw; height: 76vh; max-height: 780px; }
.orch-body { flex: 1 1 auto; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; background: #fafafe; }
.orch-empty { margin: auto; text-align: center; padding: 24px 16px; max-width: 360px; }
.orch-composer { flex: 0 0 auto; padding: 10px 14px 12px; background: #fff; }
.orch-composer :deep(textarea) { max-height: 110px; }
.orch-msg { padding: 8px 12px; border-radius: 12px; font-size: 0.92rem; white-space: pre-wrap; max-width: 86%; line-height: 1.45; box-shadow: 0 1px 1px rgba(24,32,46,.05); }
.orch-msg-user { align-self: flex-end; background: #7c6cf6; color: #fff; border-bottom-right-radius: 3px; }
.orch-msg-assistant { align-self: flex-start; background: #fff; border: 1px solid #ece9fb; color: #3a3a4a; border-bottom-left-radius: 3px; }
/* Options cliquables (désambiguïsation) — menu sous la question de l'assistant, côté gauche comme une bulle assistant */
.orch-options { align-self: flex-start; max-width: 92%; background: #fff; border: 1px solid #ece9fb; border-radius: 12px; overflow: hidden; padding: 2px 0; box-shadow: 0 1px 1px rgba(24,32,46,.05); }
.orch-option { min-height: 44px; }
.orch-option + .orch-option { border-top: 1px solid #f2f0fb; }
</style>
