<script setup>
/**
 * OccupancyBands — bandes verticales « qui a de la place », FILTRÉES par compétence.
 *
 * Grille tech × jours : chaque case = mini-timeline du quart (blocs occupé bleu / réservé orange,
 * week-end/absence hachuré) + barre d'occupation par tech (vert<50%<ambre<80%<rouge), triée le moins
 * occupé d'abord. Le DÉNOMINATEUR (heures dispo) ne compte QUE les techs qui ont la compétence `skill`
 * → « 70 h installation » au lieu de « 150 h tous techs ».
 *
 * Extrait de SuggestSlotsDialog pour être RÉUTILISABLE (SuggestSlotsDialog + AvailabilityByReason).
 * Se recharge seul quand skill/afterDate/days changent ; émet 'loaded' (occ) pour les résumés du parent
 * et 'day' (cell) au clic sur un jour.
 */
import { ref, watch, onMounted } from 'vue'
import { Notify } from 'quasar'
import { techOccupancy } from 'src/api/dispatch'

const props = defineProps({
  skill: { type: String, default: '' },
  afterDate: { type: String, default: () => new Date().toLocaleDateString('en-CA', { timeZone: 'America/Toronto' }) },
  days: { type: Number, default: 7 },
  active: { type: Boolean, default: true }, // ne charge que si actif (ex. panneau replié → false)
})
const emit = defineEmits(['loaded', 'day'])

const occ = ref(null)
const occLoading = ref(false)
let occTimer = null

async function load () {
  if (!props.active) return
  occLoading.value = true
  try {
    occ.value = await techOccupancy({ after_date: props.afterDate, days: props.days, skill: props.skill })
    emit('loaded', occ.value)
  } catch (e) {
    occ.value = null; emit('loaded', null)
    Notify.create({ type: 'negative', message: 'Occupation indisponible : ' + (e.message || e), timeout: 3000 })
  } finally { occLoading.value = false }
}
function schedule () { clearTimeout(occTimer); occTimer = setTimeout(load, 250) }

watch(() => [props.skill, props.afterDate, props.days], schedule)
watch(() => props.active, (v) => { if (v && !occ.value) load() })
onMounted(() => { if (props.active) load() })

// Rendu (identique à l'ancienne grille de SuggestSlotsDialog).
const DOW = ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam']
const pct = (o) => (o == null ? '—' : Math.round(o * 100) + '%')
const OFF_LABEL = { weekend: 'week-end', no_shift: 'aucun quart', absence: 'absent', vacation: 'congé' }
function offLabel (c) { return OFF_LABEL[c.reason] || 'indisponible' }
function occColor (o) { if (o == null) return '#cbd5e1'; if (o < 0.5) return '#22c55e'; if (o < 0.8) return '#f59e0b'; return '#ef4444' }
function occShort (iso) { const d = new Date(iso + 'T12:00:00'); return DOW[d.getDay()] + ' ' + iso.slice(8) }
function onDay (cell) { if (!cell || cell.off) return; emit('day', cell) }

defineExpose({ reload: load })
</script>

<template>
  <div>
    <div v-if="occLoading" class="text-center q-pa-sm"><q-spinner size="22px" color="primary" /></div>
    <div v-else-if="!occ || !occ.techs.length" class="text-caption text-grey-6 q-pa-sm">
      Aucune ressource{{ skill ? ' pour « ' + skill + ' »' : '' }} sur l'horizon.
    </div>
    <template v-else>
      <div class="occ-grid" :style="{ '--cols': occ.dates.length }">
        <div class="occ-name occ-corner"></div>
        <div v-for="d in occ.dates" :key="'h' + d" class="occ-dayh" :class="{ we: [0,6].includes(new Date(d + 'T12:00:00').getDay()) }">{{ occShort(d) }}</div>
        <template v-for="t in occ.techs" :key="t.tech_id">
          <div class="occ-name">
            <div class="ellipsis text-weight-medium">{{ t.tech_name }}</div>
            <div class="occ-bar"><div class="occ-bar-fill" :style="{ width: ((t.occupancy || 0) * 100) + '%', background: occColor(t.occupancy) }"></div></div>
            <div class="occ-sub">{{ pct(t.occupancy) }} · {{ t.free_h }} h libre</div>
          </div>
          <div v-for="(c, i) in t.days" :key="t.tech_id + i" class="occ-cell" @click="onDay(c)">
            <div v-if="c.off" class="occ-strip off"><q-tooltip>{{ occShort(c.date) }} · {{ offLabel(c) }}</q-tooltip></div>
            <div v-else class="occ-strip clickable">
              <div v-for="(b, bi) in c.blocks" :key="bi" class="occ-block" :class="{ reserved: b.reserved, untimed: b.untimed }"
                   :style="{ top: (b.top * 100) + '%', height: Math.max(3, b.height * 100) + '%' }"></div>
              <q-tooltip>{{ occShort(c.date) }} · {{ c.shift_start }}–{{ c.shift_end }}<br>{{ pct(c.occupancy) }} occupé · {{ c.free_h }} h libre · {{ c.jobs }} job{{ c.jobs > 1 ? 's' : '' }}<span v-if="c.untimed_jobs"><br>⚠ {{ c.untimed_jobs }} sans heure fixée</span></q-tooltip>
            </div>
          </div>
        </template>
      </div>
      <div class="row items-center q-gutter-md q-mt-xs occ-legend">
        <span><i class="occ-dot job"></i> occupé</span>
        <span><i class="occ-dot reserved"></i> réservé</span>
        <span><i class="occ-dot free"></i> libre</span>
        <span><i class="occ-dot offd"></i> pas de quart</span>
        <q-space /><span class="text-grey-6">clic sur un jour → cale la recherche</span>
      </div>
    </template>
  </div>
</template>

<style scoped>
.occ-grid { display: grid; grid-template-columns: 118px repeat(var(--cols), 1fr); gap: 3px; align-items: stretch; max-height: 42vh; overflow: auto; padding: 2px; }
.occ-corner { background: transparent; }
.occ-dayh { font-size: 10px; text-align: center; color: #64748b; font-weight: 600; padding-bottom: 2px; text-transform: capitalize; }
.occ-dayh.we { color: #b45309; }
.occ-name { min-width: 0; padding: 2px 4px 2px 0; display: flex; flex-direction: column; justify-content: center; font-size: 11.5px; }
.occ-name .ellipsis { max-width: 112px; }
.occ-bar { height: 4px; background: #e5e7eb; border-radius: 3px; overflow: hidden; margin: 2px 0; }
.occ-bar-fill { height: 100%; border-radius: 3px; transition: width .2s; }
.occ-sub { font-size: 9.5px; color: #64748b; }
.occ-cell { display: flex; }
.occ-strip { position: relative; flex: 1; min-height: 46px; border-radius: 4px; background: #dcfce7; border: 1px solid #bbf7d0; overflow: hidden; cursor: pointer; }
.occ-strip.off { background: repeating-linear-gradient(45deg, #f1f5f9, #f1f5f9 4px, #e2e8f0 4px, #e2e8f0 8px); border-color: #e2e8f0; cursor: default; }
.occ-block { position: absolute; left: 1px; right: 1px; background: #2563eb; border-radius: 2px; }
.occ-block.reserved { background: #f59e0b; }
/* Jobs assignés SANS heure fixée (legacy) : occupent la journée mais heure non planifiée → hachuré. */
.occ-block.untimed { background: repeating-linear-gradient(45deg, #2563eb, #2563eb 3px, #60a5fa 3px, #60a5fa 6px); opacity: .85; }
.occ-legend { font-size: 10.5px; color: #475569; }
.occ-dot { display: inline-block; width: 9px; height: 9px; border-radius: 2px; vertical-align: middle; margin-right: 3px; }
.occ-dot.job { background: #2563eb; }
.occ-dot.reserved { background: #f59e0b; }
.occ-dot.free { background: #dcfce7; border: 1px solid #bbf7d0; }
.occ-dot.offd { background: #e2e8f0; }
</style>
