<script setup>
/**
 * AvailabilityByReason — module RÉUTILISABLE « raison → compétence → disponibilité ».
 *
 * Montable de PARTOUT (fiche client, détail de ticket, panneau de conversation/courriel) :
 *  1. RAISON — chips des motifs groupés par département (config/departments.js) OU « dictez la raison »
 *     (texte libre → l'IA du hub évalue la/les compétence(s) probable(s)).
 *  2. COMPÉTENCE résolue → affichée + éditable.
 *  3. DISPONIBILITÉ — bandes d'occupation (<OccupancyBands>) dont le DÉNOMINATEUR ne compte que les
 *     techs qualifiés (« 70 h installation » au lieu de « 150 h tous techs »).
 *  4. Bouton « Trouver un créneau » → émet 'book' (skills + durée + date + contexte) pour que le parent
 *     ouvre SuggestSlotsDialog / UnifiedCreateModal pré-rempli.
 *
 * Le texte libre est pré-rempli depuis le contexte (corps du courriel / sujet du ticket) via `initialText`.
 */
import { ref, computed, watch } from 'vue'
import { Notify } from 'quasar'
import { DEPARTMENTS, suggestDepartments } from 'src/config/departments'
import { resolveSkills, proposeAppointment } from 'src/api/roster'
import OccupancyBands from 'src/components/shared/OccupancyBands.vue'
import { skillSym } from 'src/composables/useSkillIcons' // icônes de compétences (SOURCE UNIQUE, partagée avec Planif)

const props = defineProps({
  modelValue: { type: Boolean, default: false },
  initialText: { type: String, default: '' }, // corps courriel / sujet ticket → pré-remplit « dictez la raison »
  // Contexte optionnel repassé tel quel au parent lors du booking (fiche client / ticket).
  customerName: { type: String, default: '' },
  address: { type: String, default: '' },
  lat: { type: Number, default: null },
  lng: { type: Number, default: null },
  connectionType: { type: String, default: '' }, // type de connexion du lieu (Fibre/Wireless/LTE…) → injecte « sans-fil » si sans-fil
  // Contexte pour « Proposer au client » (envoi du lien /book). Le Dispatch Job est résolu OU créé côté hub.
  sourceIssue: { type: String, default: '' }, // nom de l'Issue/ticket source (retrouve le job ouvert lié)
  customer: { type: String, default: '' },     // id client (retrouve son job ouvert, sinon en crée un)
  serviceLocation: { type: String, default: '' },
  phone: { type: String, default: '' },
})
const emit = defineEmits(['update:modelValue', 'book'])

const today = () => new Date().toLocaleDateString('en-CA', { timeZone: 'America/Toronto' })
const afterDate = ref(today())
const reasonText = ref('')
const dept = ref('') // département/motif choisi (chip) → confiance haute
const resolved = ref(null) // { skills, primary, confidence, source, reason }
const aiLoading = ref(false)
const occ = ref(null) // dernier chargement des bandes → résumé du dénominateur

// Le client est-il en technologie SANS-FIL (vs fibre) ? → toute intervention terrain exige aussi « sans-fil ».
const wireless = computed(() => /wireless|\blte\b|sans.?fil|\bfwa\b|radio|antenne/i.test(String(props.connectionType || '')))

// Compétence(s) effective(s) — ÉDITABLE : dérivée du résolveur/chip (+ « sans-fil » si client sans-fil),
// mais l'agent peut RETIRER une compétence ajoutée à tort (ex. « sans-fil » sur un souci non lié au radio).
const skills = ref([])
function deriveSkills () {
  const base = (resolved.value && resolved.value.skills)
    ? [...resolved.value.skills]
    : (() => { const d = DEPARTMENTS.find(x => x.category === dept.value); return d ? [...(d.skills || [])] : [] })()
  // Injecté seulement s'il y a DÉJÀ une compétence terrain (une demande traitée à distance ne devient pas « sans-fil »).
  if (wireless.value && base.length && !base.some(s => /sans.?fil|wireless/i.test(s))) base.push('sans-fil')
  skills.value = base
}
// Re-dérive quand le motif/la résolution/le type de connexion change (réinitialise donc les retraits manuels — voulu).
watch([resolved, dept, wireless], deriveSkills, { immediate: true })
function removeSkill (s) { skills.value = skills.value.filter(x => x !== s) }
const primarySkill = computed(() => skills.value[0] || '')
const isWirelessSkill = (s) => /sans.?fil|wireless/i.test(String(s || ''))
const durH = computed(() => { const d = DEPARTMENTS.find(x => x.category === dept.value); return d ? (d.dur || 1) : 1 })

// Résumé du DÉNOMINATEUR ajusté (agrégé depuis les bandes) : heures libres + nb de techs qualifiés.
const summary = computed(() => {
  const o = occ.value; if (!o || !o.techs) return null
  const free = o.techs.reduce((a, t) => a + (t.free_h || 0), 0)
  const shift = o.techs.reduce((a, t) => a + (t.shift_h || 0), 0)
  return { techs: o.techs.length, free_h: Math.round(free * 10) / 10, shift_h: Math.round(shift * 10) / 10 }
})

// Chip cliqué = intention claire → compétence(s) du département (confiance haute), pas d'IA.
function pickDept (d) {
  dept.value = d.category
  resolved.value = { skills: d.skills || [], primary: (d.skills || [])[0] || '', confidence: 'high', source: 'chip', reason: '' }
}

// « Dictez la raison » : régex instantanée (chips surlignés) puis IA à la soumission.
const suggested = computed(() => suggestDepartments(reasonText.value)) // départements probables (surlignage instantané)
async function evaluateAI () {
  const text = reasonText.value.trim()
  if (text.length < 4) return
  aiLoading.value = true
  try {
    const r = await resolveSkills({ text, department: dept.value || '', useAI: true })
    resolved.value = r
    if (!dept.value && r.primary) { const d = DEPARTMENTS.find(x => (x.skills || []).includes(r.primary)); if (d) dept.value = d.category }
    if (!r.skills.length) Notify.create({ type: 'info', message: 'Aucune compétence terrain requise (traité à distance) — dispo = tous les techs.', timeout: 3500 })
  } catch (e) {
    Notify.create({ type: 'negative', message: 'Évaluation IA indisponible : ' + (e.message || e) + ' — choisis un motif ci-dessus.', timeout: 4000 })
  } finally { aiLoading.value = false }
}

function clearReason () { dept.value = ''; resolved.value = null; reasonText.value = '' }

function book () {
  emit('book', {
    skills: skills.value, primary: primarySkill.value, duration: durH.value, date: afterDate.value,
    customerName: props.customerName, address: props.address, lat: props.lat, lng: props.lng,
  })
  emit('update:modelValue', false)
}

// « Proposer au client » : au lieu d'assigner soi-même, on envoie au client le lien /book (il choisit un créneau réservé
// 5 min, ou propose ses 3 dispos). Le hub résout/crée le Dispatch Job ouvert et envoie le SMS.
const proposing = ref(false)
async function proposeToClient () {
  proposing.value = true
  try {
    const r = await proposeAppointment({
      source_issue: props.sourceIssue || '', customer: props.customer || '', phone: props.phone || '',
      subject: props.customerName ? ('Rendez-vous — ' + props.customerName) : 'Rendez-vous à planifier',
      service_location: props.serviceLocation || '', skill: primarySkill.value || '', duration: durH.value,
    })
    if (r && r.ok) {
      Notify.create({
        type: 'positive', icon: r.sms ? 'sms' : 'link',
        message: r.sms ? 'Lien de rendez-vous envoyé au client par texto.' : ('Lien prêt — ' + (r.note || 'à transmettre au client.')),
        caption: r.url || '', timeout: 6000,
        actions: r.url ? [{ label: 'Copier le lien', color: 'white', handler: () => { try { navigator.clipboard.writeText(r.url) } catch (e) {} } }] : [],
      })
      emit('update:modelValue', false)
    } else {
      Notify.create({ type: 'negative', message: (r && r.error) || 'Envoi impossible — réessayez.', timeout: 4000 })
    }
  } catch (e) {
    Notify.create({ type: 'negative', message: 'Erreur : ' + (e.message || e), timeout: 4000 })
  } finally { proposing.value = false }
}

// Réinitialise + pré-remplit à chaque ouverture.
watch(() => props.modelValue, (o) => {
  if (!o) return
  afterDate.value = today(); dept.value = ''; resolved.value = null; occ.value = null
  reasonText.value = props.initialText || ''
})

const confidenceLabel = { high: 'motif choisi', medium: 'évalué', low: 'incertain' }
const DOW = ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam']
function onOccDay (cell) { if (cell && !cell.off) afterDate.value = cell.date }
function dayShort (iso) { const d = new Date(iso + 'T12:00:00'); return DOW[d.getDay()] + ' ' + iso.slice(8) + '/' + iso.slice(5, 7) }
</script>

<template>
  <q-dialog :model-value="modelValue" @update:model-value="v => emit('update:modelValue', v)">
    <q-card style="width:560px;max-width:96vw">
      <q-card-section class="row items-center q-pb-none">
        <q-icon name="event_available" color="primary" size="24px" class="q-mr-sm" />
        <div class="text-h6">Disponibilité par motif</div>
        <q-space /><q-btn flat round dense icon="close" @click="emit('update:modelValue', false)" />
      </q-card-section>

      <q-card-section class="q-gutter-sm">
        <!-- 1. Motifs (chips par département) -->
        <div class="text-caption text-grey-7">Motif de la demande</div>
        <div class="row items-center q-gutter-xs">
          <q-chip v-for="d in DEPARTMENTS" :key="d.category" clickable dense
                  :color="dept === d.category ? d.color : (suggested.some(s => s.category === d.category) ? 'amber-2' : 'grey-3')"
                  :text-color="dept === d.category ? 'white' : 'grey-9'"
                  :icon="d.icon" @click="pickDept(d)">
            {{ d.category }}
            <q-tooltip>{{ d.skills.length ? 'Compétence : ' + d.skills.join(', ') + ' · ' + d.dur + ' h par défaut' : 'À distance / bureau — aucune compétence terrain' }}</q-tooltip>
          </q-chip>
        </div>

        <!-- 1b. Dictez la raison → IA -->
        <q-input dense outlined v-model="reasonText" type="textarea" autogrow :rows="1"
                 label="…ou dictez la raison (l'IA évalue la compétence)" clearable @keyup.enter.exact="evaluateAI">
          <template #prepend><q-icon name="mic" /></template>
          <template #append>
            <q-btn flat dense no-caps color="primary" icon="auto_awesome" :loading="aiLoading"
                   :disable="reasonText.trim().length < 4" label="Évaluer" @click="evaluateAI" />
          </template>
        </q-input>

        <!-- 2. Compétence résolue -->
        <div v-if="resolved" class="row items-center q-gutter-xs resolved-row">
          <q-icon :name="skills.length ? 'engineering' : 'support_agent'" size="18px" :color="skills.length ? 'teal-7' : 'blue-grey-5'" />
          <span v-if="skills.length" class="text-weight-medium">
            Compétence requise :
            <q-chip v-for="s in skills" :key="s" dense square removable :icon="skillSym(s)"
                    :color="isWirelessSkill(s) ? 'indigo-1' : 'teal-1'" :text-color="isWirelessSkill(s) ? 'indigo-9' : 'teal-9'" class="q-ml-xs"
                    @remove="removeSkill(s)">
              {{ s }}
              <q-tooltip v-if="isWirelessSkill(s) && wireless">Ajoutée automatiquement (client sans-fil {{ connectionType }}) — ✕ pour retirer si non lié</q-tooltip>
              <q-tooltip v-else>✕ pour retirer cette compétence si non requise</q-tooltip>
            </q-chip>
          </span>
          <span v-else class="text-blue-grey-7">Aucune compétence terrain — traité à distance (tous les agents).</span>
          <q-badge outline color="grey-6" class="q-ml-xs">{{ confidenceLabel[resolved.confidence] || resolved.confidence }}</q-badge>
          <q-btn flat dense round size="sm" icon="close" color="grey-6" @click="clearReason"><q-tooltip>Effacer le motif</q-tooltip></q-btn>
          <div v-if="resolved.reason" class="full-width text-caption text-grey-6 q-pl-md">« {{ resolved.reason }} »</div>
        </div>

        <!-- 3. Disponibilité filtrée : dénominateur ajusté -->
        <template v-if="resolved">
          <q-separator class="q-my-xs" />
          <div class="row items-center">
            <q-input dense outlined type="date" v-model="afterDate" label="Dès le" style="width:170px" />
            <q-space />
            <div v-if="summary" class="text-right">
              <div class="text-h6 text-teal-8" style="line-height:1.1">{{ summary.free_h }} h <span class="text-caption text-grey-6">libres</span></div>
              <div class="text-caption text-grey-7">{{ summary.techs }} tech{{ summary.techs > 1 ? 's' : '' }} qualifié{{ summary.techs > 1 ? 's' : '' }}{{ skills.length ? ' « ' + skills.join(' + ') + ' »' : '' }} · {{ summary.shift_h }} h de quart</div>
            </div>
          </div>
          <OccupancyBands :skill="skills.join(',')" :after-date="afterDate" :days="7" :active="true" @day="onOccDay" @loaded="v => occ = v" />
        </template>
        <div v-else class="text-caption text-grey-6 q-pt-xs">Choisis un motif ou dicte la raison pour voir la disponibilité ajustée à la compétence.</div>
      </q-card-section>

      <q-card-actions v-if="resolved" align="right" class="q-px-md q-pb-md">
        <q-btn flat no-caps color="grey-7" label="Fermer" @click="emit('update:modelValue', false)" />
        <q-space />
        <q-btn outline no-caps color="primary" icon="sms" label="Proposer au client" :loading="proposing" @click="proposeToClient">
          <q-tooltip>Envoie au client un lien pour choisir LUI-MÊME son créneau (réservé 5 min) ou proposer ses 3 disponibilités. Idéal après un diagnostic quand on laisse le client choisir l'heure.</q-tooltip>
        </q-btn>
        <q-btn unelevated no-caps color="primary" icon="event" label="Trouver un créneau" @click="book">
          <q-tooltip>Ouvre la recherche de créneau pré-remplie ({{ primarySkill || 'sans compétence' }} · {{ durH }} h · dès {{ dayShort(afterDate) }}).</q-tooltip>
        </q-btn>
      </q-card-actions>
    </q-card>
  </q-dialog>
</template>

<style scoped>
.resolved-row { border: 1px dashed #cbd5e1; border-radius: 8px; padding: 6px 10px; }
</style>
