<template>
  <q-page class="hist-page">
    <div class="hist-head">
      <div class="text-h6">Historique &amp; classements</div>
      <div class="hist-sub">Retracer la journée de chaque technicien (tous statuts) et reconnaître l'effort de l'équipe.</div>
    </div>

    <q-tabs v-model="tab" no-caps dense align="left" class="hist-tabs" active-color="primary" indicator-color="primary">
      <q-tab name="tournees" icon="route" label="Tournées par technicien" />
      <q-tab name="techs" icon="emoji_events" label="Classement techniciens" />
      <q-tab name="inbox" icon="forum" label="Classement Inbox" />
    </q-tabs>
    <q-separator />

    <q-tab-panels v-model="tab" animated class="hist-panels">
      <!-- ─────────── Tournées par technicien (tous statuts) ─────────── -->
      <q-tab-panel name="tournees" class="q-pa-none">
        <div class="hist-toolbar">
          <q-btn flat dense round icon="chevron_left" @click="shiftDay(-1)" />
          <q-btn flat dense no-caps class="hist-datebtn" :label="prettyDate(histDate)" icon-right="event">
            <q-popup-proxy cover transition-show="scale" transition-hide="scale">
              <q-date v-model="histDate" mask="YYYY-MM-DD" today-btn minimal @update:model-value="() => { loadHistory() }" />
            </q-popup-proxy>
          </q-btn>
          <q-btn flat dense round icon="chevron_right" :disable="histDate >= today" @click="shiftDay(1)" />
          <q-btn flat dense no-caps label="Aujourd'hui" class="q-ml-sm" :disable="histDate === today" @click="histDate = today; loadHistory()" />
          <q-space />
          <div v-if="!histLoading && hist" class="hist-summary">
            <b>{{ hist.count }}</b> tâche{{ hist.count > 1 ? 's' : '' }} · <b>{{ (hist.techs || []).length }}</b> technicien{{ (hist.techs || []).length > 1 ? 's' : '' }}
            <span v-for="s in histStatusSummary" :key="s.key" class="hist-sumchip" :style="{ color: s.color }">● {{ s.n }} {{ s.label }}</span>
          </div>
        </div>

        <div v-if="histLoading" class="hist-loading"><q-spinner-dots size="32px" color="primary" /></div>
        <div v-else-if="!hist || !(hist.techs || []).length" class="hist-empty">
          <q-icon name="event_busy" size="32px" class="q-mb-sm" />
          <div>Aucune tâche planifiée le {{ prettyDate(histDate) }}.</div>
        </div>
        <div v-else class="hist-techs">
          <q-card v-for="t in hist.techs" :key="t.tech" flat bordered class="hist-techcard">
            <div class="hist-techhead">
              <q-avatar size="28px" color="blue-grey-1" text-color="blue-grey-8" class="q-mr-sm">{{ initials(t.tech_name) }}</q-avatar>
              <div class="hist-techname">{{ t.tech_name }}</div>
              <q-space />
              <span class="hist-techcount">{{ t.jobs.length }} tâche{{ t.jobs.length > 1 ? 's' : '' }}</span>
            </div>
            <q-separator />
            <div v-for="j in t.jobs" :key="j.name" class="hist-job" :class="{ 'hist-job--cancel': j.status === 'Cancelled' }">
              <q-chip dense square :style="{ background: statusMeta(j.status).bg, color: statusMeta(j.status).fg }" class="hist-statuschip">{{ statusMeta(j.status).label }}</q-chip>
              <q-icon v-if="j.actual_start" name="my_location" size="15px" color="green-6" class="q-mr-xs">
                <q-tooltip>Présence confirmée sur place (geofence){{ geofenceMin(j) ? ' · ' + geofenceMin(j) + ' min sur site' : '' }}</q-tooltip>
              </q-icon>
              <span class="hist-jobtime">{{ jobTime(j) }}</span>
              <span class="hist-jobsubj">{{ j.subject || j.job_type || j.name }}</span>
              <q-space />
              <span v-if="j.duration_h" class="hist-jobdur">{{ j.duration_h }} h</span>
              <q-btn flat round dense size="xs" icon="history" color="grey-6" class="q-ml-xs" @click="openAudit(j)">
                <q-tooltip>Historique d'assignation</q-tooltip>
              </q-btn>
            </div>
          </q-card>
        </div>
      </q-tab-panel>

      <!-- ─────────── Classement techniciens (jobs complétés) ─────────── -->
      <q-tab-panel name="techs" class="q-pa-none">
        <div class="hist-toolbar">
          <q-btn-toggle v-model="rangeDays" no-caps dense unelevated toggle-color="primary" color="grey-2" text-color="grey-8"
            :options="[{ label: '7 jours', value: 7 }, { label: '30 jours', value: 30 }, { label: '90 jours', value: 90 }]"
            @update:model-value="loadTechBoard" />
          <q-space />
          <div class="hist-summary">Jobs <b>complétés</b> · {{ prettyDate(rangeFrom) }} → {{ prettyDate(today) }}</div>
        </div>
        <div v-if="techLoading" class="hist-loading"><q-spinner-dots size="32px" color="primary" /></div>
        <div v-else-if="!techBoard.length" class="hist-empty"><q-icon name="emoji_events" size="32px" class="q-mb-sm" /><div>Aucun job complété sur la période.</div></div>
        <div v-else class="hist-board">
          <div v-for="(b, i) in techBoard" :key="b.tech" class="hist-rank">
            <div class="hist-medal" :class="'hist-medal--' + (i < 3 ? i + 1 : 0)">{{ i < 3 ? ['🥇', '🥈', '🥉'][i] : (i + 1) }}</div>
            <q-avatar size="30px" color="blue-grey-1" text-color="blue-grey-8" class="q-mr-sm">{{ initials(b.tech_name) }}</q-avatar>
            <div class="hist-rankname">{{ b.tech_name }}</div>
            <div class="hist-bar"><div class="hist-bar-fill" :style="{ width: barPct(b.completed, techMax) + '%' }"></div></div>
            <div class="hist-rankval">{{ b.completed }}</div>
          </div>
        </div>
      </q-tab-panel>

      <!-- ─────────── Classement Inbox (réponses par agent) ─────────── -->
      <q-tab-panel name="inbox" class="q-pa-none">
        <div class="hist-toolbar">
          <q-btn-toggle v-model="rangeDays" no-caps dense unelevated toggle-color="primary" color="grey-2" text-color="grey-8"
            :options="[{ label: '7 jours', value: 7 }, { label: '30 jours', value: 30 }, { label: '90 jours', value: 90 }]"
            @update:model-value="loadInboxBoard" />
          <q-space />
          <div class="hist-summary">Réponses &amp; conversations prises · {{ prettyDate(rangeFrom) }} → {{ prettyDate(today) }}</div>
        </div>
        <div v-if="inboxLoading" class="hist-loading"><q-spinner-dots size="32px" color="primary" /></div>
        <template v-else>
          <div v-if="!inboxBoard.length" class="hist-empty">
            <q-icon name="forum" size="32px" class="q-mb-sm" />
            <div>Les statistiques par agent s'accumulent à partir de maintenant.</div>
            <div class="hist-empty-sub">Chaque réponse envoyée est désormais attribuée à son auteur·rice.<br>{{ inboxUnattributed }} réponse{{ inboxUnattributed > 1 ? 's' : '' }} antérieure{{ inboxUnattributed > 1 ? 's' : '' }} (avant suivi) ne sont pas attribuées.</div>
          </div>
          <div v-else class="hist-board">
            <div v-for="(b, i) in inboxBoard" :key="b.agent" class="hist-rank">
              <div class="hist-medal" :class="'hist-medal--' + (i < 3 ? i + 1 : 0)">{{ i < 3 ? ['🥇', '🥈', '🥉'][i] : (i + 1) }}</div>
              <q-avatar size="30px" color="green-1" text-color="green-9" class="q-mr-sm">{{ initials(agentName(b.agent)) }}</q-avatar>
              <div class="hist-rankname">{{ agentName(b.agent) }}</div>
              <div class="hist-bar"><div class="hist-bar-fill hist-bar-fill--teal" :style="{ width: barPct(b.replies, inboxMax) + '%' }"></div></div>
              <div class="hist-rankval">{{ b.replies }}<span class="hist-rankval-sub">rép.</span></div>
              <div class="hist-rankval2" v-if="b.handled">{{ b.handled }}<span class="hist-rankval-sub">pris</span></div>
            </div>
            <div v-if="inboxUnattributed" class="hist-note">+ {{ inboxUnattributed }} réponse{{ inboxUnattributed > 1 ? 's' : '' }} antérieure{{ inboxUnattributed > 1 ? 's' : '' }} non attribuée{{ inboxUnattributed > 1 ? 's' : '' }} (avant le suivi par auteur).</div>
          </div>
        </template>
      </q-tab-panel>
    </q-tab-panels>

    <!-- Historique d'assignation d'un job (journal d'audit : qui l'a eu, quand, chaque changement) -->
    <q-dialog v-model="auditOpen">
      <q-card style="min-width:340px;max-width:520px">
        <q-card-section class="row items-center q-pb-sm">
          <q-icon name="history" color="primary" size="22px" class="q-mr-sm" />
          <div class="text-subtitle1 text-weight-bold col ellipsis">{{ auditJobSubject || 'Historique d\'assignation' }}</div>
          <q-btn flat round dense icon="close" v-close-popup />
        </q-card-section>
        <q-separator />
        <q-card-section class="q-pt-sm" style="max-height:60vh;overflow:auto">
          <div v-if="auditLoading" class="text-center q-pa-md"><q-spinner-dots size="28px" color="primary" /></div>
          <div v-else-if="!auditRows.length" class="text-grey-6 text-center q-pa-md">Aucun changement d'assignation journalisé pour ce job.<div class="text-caption q-mt-xs">Le journal accumule les changements à partir du 2026-07-01.</div></div>
          <q-timeline v-else dense color="primary">
            <q-timeline-entry v-for="(e, i) in auditRows" :key="i"
                              :icon="auditIcon(e.event)" :color="auditColor(e.event)">
              <template #title><span class="text-body2 text-weight-medium">{{ auditLabel(e.event) }}</span><span v-if="e.to" class="text-body2"> → {{ e.to }}</span></template>
              <template #subtitle>{{ fmtTs(e.ts) }} · <span class="text-grey-7">{{ e.actor }}</span> <q-badge outline color="grey-6" :label="e.source" class="q-ml-xs" /></template>
              <span v-if="e.from" class="text-caption text-grey-6">depuis {{ e.from }}</span>
            </q-timeline-entry>
          </q-timeline>
        </q-card-section>
      </q-card>
    </q-dialog>
  </q-page>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue'
import { staffInitials as initials } from 'src/composables/useFormatters' // initiales 1er+dernier (source unique)
import { Notify } from 'quasar'
import { HUB_URL } from 'src/config/hub'

const tab = ref('tournees')

function localDateStr (d = new Date()) {
  // Date locale (QC = ET) en YYYY-MM-DD, sans décalage UTC
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
const today = localDateStr()

async function hubGet (path) {
  const res = await fetch(HUB_URL + path, { headers: { 'Content-Type': 'application/json' } })
  if (!res.ok) throw new Error('HTTP ' + res.status)
  return res.json()
}

// ── Affichage commun ──
// initials → useFormatters.staffInitials (1er + dernier mot, source unique)
function prettyDate (iso) {
  if (!iso) return ''
  const [y, m, d] = iso.split('-').map(Number)
  return new Date(y, m - 1, d).toLocaleDateString('fr-CA', { weekday: 'short', day: 'numeric', month: 'short' })
}
function barPct (v, max) { return max > 0 ? Math.max(4, Math.round((v / max) * 100)) : 0 }

const STATUS_META = {
  Completed: { label: 'Complété', bg: '#dcfce7', fg: '#15803d' },
  assigned: { label: 'Assigné', bg: '#dbeafe', fg: '#1d4ed8' },
  in_progress: { label: 'En cours', bg: '#fef3c7', fg: '#b45309' },
  open: { label: 'Ouvert', bg: '#e2e8f0', fg: '#475569' },
  'On Hold': { label: 'En attente', bg: '#ffedd5', fg: '#c2410c' },
  Cancelled: { label: 'Annulé', bg: '#f1f5f9', fg: '#94a3b8' },
}
function statusMeta (s) { return STATUS_META[s] || { label: s || '—', bg: '#e2e8f0', fg: '#475569' } }
function jobTime (j) {
  const t = j.start_time ? String(j.start_time).slice(11, 16) : ''
  return t || '—'
}
// Minutes réelles sur place d'après le geofence (actual_start → actual_end). Confirme la présence du tech.
function geofenceMin (j) {
  if (!j.actual_start || !j.actual_end) return 0
  const a = new Date(String(j.actual_start).replace(' ', 'T')); const b = new Date(String(j.actual_end).replace(' ', 'T'))
  const m = Math.round((b - a) / 60000)
  return (m > 0 && m < 1440) ? m : 0
}

// ── Journal d'audit d'assignation (par job) ──
const auditOpen = ref(false)
const auditRows = ref([])
const auditLoading = ref(false)
const auditJobSubject = ref('')
async function openAudit (j) {
  auditJobSubject.value = j.subject || j.name
  auditRows.value = []; auditLoading.value = true; auditOpen.value = true
  try { const r = await hubGet(`/dispatch/audit?job=${encodeURIComponent(j.name)}`); auditRows.value = r.rows || [] }
  catch (e) { Notify.create({ type: 'negative', message: 'Erreur audit : ' + e.message }) }
  finally { auditLoading.value = false }
}
const AUDIT_META = {
  assigned: { label: 'Assigné (répartiteur)', icon: 'person_add', color: 'primary' },
  'mirror-assigned': { label: 'Assigné dans F', icon: 'sync_alt', color: 'teal' },
  reassigned: { label: 'Réassigné', icon: 'swap_horiz', color: 'orange' },
  'returned-to-pool': { label: 'Retour au pool', icon: 'undo', color: 'blue-grey' },
  completed: { label: 'Complété', icon: 'check_circle', color: 'positive' },
  cancelled: { label: 'Annulé', icon: 'cancel', color: 'negative' },
  backfill: { label: 'Trace récupérée', icon: 'restore', color: 'grey' },
}
function auditLabel (e) { return (AUDIT_META[e] || {}).label || e }
function auditIcon (e) { return (AUDIT_META[e] || {}).icon || 'fiber_manual_record' }
function auditColor (e) { return (AUDIT_META[e] || {}).color || 'grey' }
function fmtTs (ts) { try { return new Date(ts).toLocaleString('fr-CA', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' }) } catch (e) { return ts } }

// ── Tab 1 : tournées par technicien ──
const histDate = ref(today)
const hist = ref(null)
const histLoading = ref(false)
function shiftDay (delta) {
  const [y, m, d] = histDate.value.split('-').map(Number)
  const nd = new Date(y, m - 1, d + delta)
  const next = localDateStr(nd)
  if (next > today) return
  histDate.value = next
  loadHistory()
}
async function loadHistory () {
  histLoading.value = true
  try { hist.value = await hubGet(`/dispatch/history?date=${histDate.value}`) }
  catch (e) { hist.value = null; Notify.create({ type: 'negative', message: 'Chargement de l\'historique : ' + e.message }) }
  finally { histLoading.value = false }
}
const histStatusSummary = computed(() => {
  if (!hist.value) return []
  const counts = {}
  for (const t of (hist.value.techs || [])) for (const j of t.jobs) counts[j.status] = (counts[j.status] || 0) + 1
  return Object.keys(counts).sort((a, b) => counts[b] - counts[a]).map(k => ({ key: k, n: counts[k], label: statusMeta(k).label.toLowerCase(), color: statusMeta(k).fg }))
})

// ── Périodes (tabs 2 & 3) ──
const rangeDays = ref(30)
const rangeFrom = computed(() => {
  const d = new Date(); d.setDate(d.getDate() - (rangeDays.value - 1))
  return localDateStr(d)
})

// ── Tab 2 : classement techniciens ──
const techBoard = ref([])
const techLoading = ref(false)
const techMax = computed(() => techBoard.value.reduce((m, b) => Math.max(m, b.completed), 0))
async function loadTechBoard () {
  techLoading.value = true
  try { const r = await hubGet(`/dispatch/leaderboard?from=${rangeFrom.value}&to=${today}`); techBoard.value = r.board || [] }
  catch (e) { techBoard.value = []; Notify.create({ type: 'negative', message: 'Classement techniciens : ' + e.message }) }
  finally { techLoading.value = false }
}

// ── Tab 3 : classement Inbox ──
const inboxBoard = ref([])
const inboxUnattributed = ref(0)
const inboxLoading = ref(false)
const inboxMax = computed(() => inboxBoard.value.reduce((m, b) => Math.max(m, b.replies), 0))
function agentName (email) {
  if (!email) return '—'
  const local = String(email).split('@')[0]
  return local.split(/[.\-_]/).filter(Boolean).map(p => p[0].toUpperCase() + p.slice(1)).join(' ') || email
}
async function loadInboxBoard () {
  inboxLoading.value = true
  try {
    const r = await hubGet(`/conversations/leaderboard?from=${rangeFrom.value}&to=${today}`)
    inboxBoard.value = (r.board || []).filter(b => b.replies > 0 || b.handled > 0)
    inboxUnattributed.value = r.unattributed || 0
  } catch (e) { inboxBoard.value = []; Notify.create({ type: 'negative', message: 'Classement Inbox : ' + e.message }) }
  finally { inboxLoading.value = false }
}

onMounted(() => { loadHistory(); loadTechBoard(); loadInboxBoard() })
</script>

<style scoped>
.hist-page { padding: 16px 20px; background: var(--ops-bg, #f8fafc); min-height: 100%; }
.hist-head { margin-bottom: 10px; }
.hist-sub { color: var(--ops-text-muted, #64748b); font-size: 13px; margin-top: 2px; }
.hist-tabs { color: var(--ops-text, #1e293b); }
.hist-panels { background: transparent; }

.hist-toolbar { display: flex; align-items: center; gap: 2px; padding: 12px 2px; flex-wrap: wrap; }
.hist-datebtn { font-weight: 600; text-transform: capitalize; }
.hist-summary { font-size: 13px; color: var(--ops-text-muted, #64748b); }
.hist-summary b { color: var(--ops-text, #1e293b); }
.hist-sumchip { margin-left: 10px; font-size: 12px; white-space: nowrap; }

.hist-loading, .hist-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 48px 16px; color: var(--ops-text-muted, #94a3b8); text-align: center; }
.hist-empty-sub { font-size: 12px; margin-top: 6px; line-height: 1.5; }

/* Tournées */
.hist-techs { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 12px; }
.hist-techcard { border-radius: 10px; background: var(--ops-surface, #fff); overflow: hidden; }
.hist-techhead { display: flex; align-items: center; padding: 10px 12px; }
.hist-techname { font-weight: 600; color: var(--ops-text, #1e293b); }
.hist-techcount { font-size: 12px; color: var(--ops-text-muted, #64748b); }
.hist-job { display: flex; align-items: center; gap: 8px; padding: 7px 12px; border-top: 1px solid var(--ops-border, #f1f5f9); font-size: 13px; }
.hist-job:first-of-type { border-top: none; }
.hist-job--cancel { opacity: 0.55; }
.hist-job--cancel .hist-jobsubj { text-decoration: line-through; }
.hist-statuschip { font-size: 11px; font-weight: 600; height: 20px; }
.hist-jobtime { font-variant-numeric: tabular-nums; color: var(--ops-text-muted, #64748b); width: 42px; flex-shrink: 0; }
.hist-jobsubj { color: var(--ops-text, #1e293b); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hist-jobdur { font-size: 12px; color: var(--ops-text-muted, #94a3b8); flex-shrink: 0; }

/* Classements */
.hist-board { max-width: 760px; }
.hist-rank { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-bottom: 1px solid var(--ops-border, #eef2f6); }
.hist-medal { width: 30px; text-align: center; font-weight: 700; font-size: 16px; color: var(--ops-text-muted, #94a3b8); flex-shrink: 0; }
.hist-medal--1, .hist-medal--2, .hist-medal--3 { font-size: 18px; }
.hist-rankname { font-weight: 600; color: var(--ops-text, #1e293b); width: 200px; max-width: 32%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hist-bar { flex: 1; height: 10px; background: var(--ops-border, #eef2f6); border-radius: 6px; overflow: hidden; margin: 0 4px; }
.hist-bar-fill { height: 100%; background: #3b82f6; border-radius: 6px; transition: width .3s; }
.hist-bar-fill--teal { background: #14b8a6; }
.hist-rankval { font-weight: 700; color: var(--ops-text, #1e293b); min-width: 38px; text-align: right; font-variant-numeric: tabular-nums; }
.hist-rankval2 { min-width: 42px; text-align: right; color: var(--ops-text-muted, #64748b); font-variant-numeric: tabular-nums; }
.hist-rankval-sub { font-size: 10px; font-weight: 400; color: var(--ops-text-muted, #94a3b8); margin-left: 2px; }
.hist-note { font-size: 12px; color: var(--ops-text-muted, #94a3b8); padding: 10px; }
</style>
