<template>
  <q-dialog :model-value="modelValue" @update:model-value="v => emit('update:modelValue', v)">
    <q-card style="width:640px;max-width:96vw">
      <q-card-section class="row items-center q-pb-none">
        <q-icon name="event" color="primary" size="22px" class="q-mr-sm" />
        <div>
          <div class="text-subtitle1 text-weight-bold">Horaire — {{ tech && tech.name }}</div>
          <div class="text-caption text-grey-6">Horaire récurrent · pause indéfinie · congés du mois</div>
        </div>
        <q-space />
        <q-btn flat round dense icon="close" @click="emit('update:modelValue', false)" />
      </q-card-section>

      <q-card-section class="q-gutter-md">
        <!-- 1. Pause indéfinie (maladie / maternité / épuisement…) -->
        <div class="ts-block">
          <div class="row items-center">
            <q-icon name="pause_circle" :color="paused ? 'orange-8' : 'grey-6'" size="20px" class="q-mr-sm" />
            <div class="col">
              <div class="text-weight-medium">Pause indéfinie</div>
              <div class="text-caption text-grey-6">Retire le tech du dispatch et du solveur jusqu'à réactivation (arrêt maladie, maternité, épuisement…).</div>
            </div>
            <q-toggle :model-value="paused" color="orange-8" :disable="pauseBusy" @update:model-value="onTogglePause" />
          </div>
          <div v-if="paused" class="row items-center q-gutter-sm q-mt-sm">
            <q-select dense outlined v-model="pauseReason" :options="PAUSE_REASONS" label="Motif" style="width:200px" @update:model-value="savePauseReason" />
            <q-input dense outlined v-model="pauseNote" label="Note (optionnel)" class="col" @blur="savePauseReason" @keyup.enter="savePauseReason" />
          </div>
        </div>

        <!-- 2. Horaire récurrent (réutilise WeeklyScheduleEditor via le parent) -->
        <div class="ts-block row items-center">
          <q-icon name="repeat" color="teal-7" size="20px" class="q-mr-sm" />
          <div class="col">
            <div class="text-weight-medium">Horaire récurrent</div>
            <div class="text-caption text-grey-6">Patron hebdo (jours & heures) qui matérialise les quarts sur plusieurs semaines.</div>
          </div>
          <q-btn unelevated no-caps color="teal-7" icon="edit_calendar" label="Modifier" :disable="paused" @click="emit('edit-schedule', tech)" />
        </div>

        <!-- 3. Calendrier du mois : cases carrées, glisser-sélectionner + pinceau -->
        <div class="ts-block">
          <div class="row items-center q-mb-sm">
            <q-btn flat round dense icon="chevron_left" @click="shiftMonth(-1)" />
            <div class="text-weight-medium" style="min-width:150px;text-align:center;text-transform:capitalize">{{ monthLabel }}</div>
            <q-btn flat round dense icon="chevron_right" @click="shiftMonth(1)" />
            <q-space />
            <q-spinner v-if="calLoading" size="18px" color="primary" />
          </div>
          <!-- Pinceau -->
          <div class="row items-center q-gutter-xs q-mb-xs">
            <span class="text-caption text-grey-7 q-mr-xs">Pinceau :</span>
            <span v-for="b in BRUSHES" :key="b.type" class="ts-brush" :class="{ on: brush === b.type }"
                  :style="brush === b.type ? { background: b.color, borderColor: b.color, color: '#fff' } : { borderColor: b.color, color: b.color }"
                  @click="brush = b.type">{{ b.label }}</span>
            <q-space /><span class="text-caption text-grey-5">glisse pour marquer · re-clique pour annuler · <b>à publier</b></span>
          </div>
          <!-- Grille — clic simple = menu de cellule PARTAGÉ (Jour/Soir/Garde/Absent/heures, comme la semaine/jour) ;
               glisser = pinceau rapide (congés multi-jours pour les vacances). -->
          <div class="ts-cal" @mouseleave="cancelDrag" @mouseup="endDrag">
            <div v-for="d in DOW_LABELS" :key="'h' + d" class="ts-dowh">{{ d }}</div>
            <div v-for="(c, i) in monthCells" :key="i"
                 class="ts-cell" :class="cellClass(c)"
                 @mousedown.prevent="c.inMonth && startDrag(c, $event)"
                 @mouseenter="c.inMonth && overDrag(c)">
              <template v-if="c.inMonth">
                <span class="ts-daynum">{{ c.day }}</span>
                <span v-if="c.absType" class="ts-abs" :style="{ background: absColor(c.absType) }"><q-tooltip>{{ c.absType }}</q-tooltip></span>
                <!-- Jour AVEC quart (et sans absence) : mince barre d'occupation (vert→ambre→rouge). Jour sans quart = fond gris. -->
                <template v-else-if="c.hasShift">
                  <span class="ts-occ"><span class="ts-occ-fill" :style="{ width: Math.round((c.occupancy || 0) * 100) + '%', background: occColor(c.occupancy) }"></span></span>
                  <q-tooltip>Quart · {{ c.occupancy != null ? Math.round(c.occupancy * 100) + '% occupé' : 'libre' }}</q-tooltip>
                </template>
                <span v-if="c.holiday" class="ts-holi" title="Férié">★</span>
              </template>
            </div>
          </div>
          <!-- Menu de cellule PARTAGÉ (même composant que la grille) — ouvert au clic simple sur un jour. -->
          <div ref="cellAnchor" style="position:fixed;width:1px;height:1px;pointer-events:none;z-index:0" :style="{ left: cellMenu.x + 'px', top: cellMenu.y + 'px' }"></div>
          <q-menu v-model="cellMenu.show" :target="cellAnchor" anchor="bottom right" self="top left" max-height="85vh">
            <PlanifCellMenu
              :cell-key="cellMenu.iso"
              :title="(tech && tech.name) + ' — ' + fmtDayLabel(cellMenu.iso)"
              :shifts="menuShifts" :is-garde="menuIsGarde" :is-absent="menuIsAbsent" :show-copy="false"
              @window="onMenuWindow" @toggle-garde="onMenuGarde" @toggle-absent="onMenuAbsent"
              @remove-shift="onMenuRemoveShift" @clear="onMenuClear" />
          </q-menu>
          <div class="row items-center q-gutter-md q-mt-xs ts-legend">
            <span v-for="b in BRUSHES.filter(x => x.type !== 'clear')" :key="'l' + b.type"><i class="ts-dot" :style="{ background: b.color }"></i>{{ b.label }}</span>
            <span><i class="ts-dot ts-dot-today"></i>aujourd'hui</span>
            <q-space /><span class="text-grey-6" v-if="selCount">{{ selCount }} jour(s) sélectionné(s)…</span>
          </div>
        </div>
      </q-card-section>

      <q-separator />
      <q-card-actions align="left" class="q-pa-md">
        <q-btn flat no-caps color="negative" icon="archive" label="Archiver le technicien" :loading="archiveBusy" @click="confirmArchive" />
        <q-space />
        <q-btn flat no-caps color="grey-7" label="Fermer" @click="emit('update:modelValue', false)" />
      </q-card-actions>
    </q-card>
  </q-dialog>
</template>

<script setup>
// Écran UNIQUE de gestion d'horaire d'un technicien : pause indéfinie (motif) + horaire récurrent (délègue à
// WeeklyScheduleEditor du parent) + calendrier du mois (cases carrées, glisser-sélectionner + pinceau) pour les
// congés/absences ponctuels, et archivage (réversible). Réutilise les endpoints roster déjà déployés.
import { ref, reactive, computed, watch } from 'vue'
import { useQuasar } from 'quasar'
import * as roster from 'src/api/roster'
import { techOccupancy } from 'src/api/dispatch' // occupation/quart par jour du tech → grise les jours sans quart, barre d'occupation les jours avec quart
import PlanifCellMenu from 'src/components/planif/PlanifCellMenu.vue' // menu de cellule PARTAGÉ (identique à la grille semaine/jour)

const props = defineProps({
  modelValue: { type: Boolean, default: false },
  tech: { type: Object, default: null }, // { id, name, status, ... }
  pendingAbs: { type: Object, default: () => ({}) }, // congés EN ATTENTE (delta local du parent, publish-required) → superposés à l'état serveur
  pendingPaused: { type: Boolean, default: false }, // pause indéfinie EN ATTENTE (staged par le parent, publish-required)
  gardeMap: { type: Object, default: () => ({}) }, // garde EFFECTIVE du parent (gardeEffective) : clé tech|iso → shift
  refreshKey: { type: Number, default: 0 }, // ↑ par le parent après une écriture de quart → recharge le mois
})
const emit = defineEmits(['update:modelValue', 'edit-schedule', 'changed', 'stage-abs', 'stage-pause', 'set-shift', 'remove-shift', 'clear-shifts', 'toggle-garde'])
const $q = useQuasar()
const err = (e) => $q.notify({ type: 'negative', message: '' + (e.message || e) })

const PAUSE_REASONS = ['Arrêt maladie', 'Maternité', 'Épuisement', 'Invalidité', 'Autre']
const BRUSHES = [
  { type: 'Congé', label: 'Congé', color: '#0ea5e9' },
  { type: 'Maladie', label: 'Maladie', color: '#ef4444' },
  { type: 'Indisponible', label: 'Indispo', color: '#a855f7' },
  { type: 'clear', label: 'Effacer', color: '#94a3b8' },
]
const DOW_LABELS = ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim']

// ── Pause ────────────────────────────────────────────────────────────────────
const paused = ref(false)
const pauseReason = ref('Arrêt maladie')
const pauseNote = ref('')
const pauseBusy = ref(false)
function reasonPayload () { return [pauseReason.value, pauseNote.value].filter(Boolean).join(' — ') }
// La pause est STAGÉE par le parent (publish-required) : on émet l'intention, le parent l'applique au Publier
// (retire les quarts futurs + relève les jobs). Réactivation d'un tech déjà en pause = gérée immédiatement côté parent.
function onTogglePause (v) {
  if (!props.tech) return
  paused.value = v // reflet optimiste dans le dialogue
  emit('stage-pause', { techId: props.tech.id, want: v, reason: v ? reasonPayload() : '' })
}
function savePauseReason () {
  if (!props.tech || !paused.value) return
  emit('stage-pause', { techId: props.tech.id, want: true, reason: reasonPayload() })
}

// ── Calendrier du mois ───────────────────────────────────────────────────────
const brush = ref('Congé')
const month = ref('') // 'YYYY-MM'
const calLoading = ref(false)
const absMap = ref({}) // 'YYYY-MM-DD' → type d'absence
const shiftMap = ref({}) // 'YYYY-MM-DD' → { shift:bool, occupancy:0..1 } (quart réel du tech ce jour) → gris = pas de quart
const asgByDate = ref({}) // 'YYYY-MM-DD' → [{ name, shift, shift_name, hours }] (quarts réguliers du tech ce jour) → menu « shifts en place »
const holiSet = ref(new Set())
const drag = reactive({ on: false, sel: new Set(), moved: false })
const selCount = computed(() => drag.sel.size)
// Menu de cellule (clic simple) — ancré au curseur (comme la grille).
const cellAnchor = ref(null)
const cellMenu = reactive({ show: false, iso: '', x: 0, y: 0 })
const menuShifts = computed(() => asgByDate.value[cellMenu.iso] || [])
const menuIsGarde = computed(() => !!(props.tech && props.gardeMap && props.gardeMap[props.tech.id + '|' + cellMenu.iso]))
const menuIsAbsent = computed(() => !!effAbsType(cellMenu.iso))
function fmtDayLabel (iso) { if (!iso) return ''; return iso.slice(8) + '/' + iso.slice(5, 7) }

// Type d'absence EFFECTIF pour un jour = serveur (absMap) écrasé par le delta EN ATTENTE du parent. null = pas absent.
function effAbsType (iso) {
  const p = props.pendingAbs && props.tech && props.pendingAbs[props.tech.id + '|' + iso]
  if (p) return p.op === 'set' ? (p.type || 'Congé') : null
  return absMap.value[iso] || null
}
const MO = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre']
const monthLabel = computed(() => { const [y, m] = month.value.split('-').map(Number); return MO[m - 1] + ' ' + y })
function todayISO () { return new Date().toLocaleDateString('en-CA', { timeZone: 'America/Toronto' }) }
function pad (n) { return String(n).padStart(2, '0') }

const monthCells = computed(() => {
  if (!month.value) return []
  const [y, m] = month.value.split('-').map(Number)
  const first = new Date(Date.UTC(y, m - 1, 1))
  const lead = (first.getUTCDay() + 6) % 7 // lundi = 0
  const daysInMonth = new Date(Date.UTC(y, m, 0)).getUTCDate()
  const cells = []
  for (let i = 0; i < lead; i++) cells.push({ inMonth: false })
  const t = todayISO()
  for (let day = 1; day <= daysInMonth; day++) {
    const iso = y + '-' + pad(m) + '-' + pad(day)
    const dow = new Date(iso + 'T12:00:00').getUTCDay()
    const sh = shiftMap.value[iso]
    cells.push({ inMonth: true, day, iso, dow, weekend: dow === 0 || dow === 6, isToday: iso === t, absType: effAbsType(iso), holiday: holiSet.value.has(iso), sel: drag.sel.has(iso), hasShift: !!(sh && sh.shift), occupancy: sh ? sh.occupancy : null })
  }
  while (cells.length % 7) cells.push({ inMonth: false })
  return cells
})

function cellClass (c) {
  if (!c.inMonth) return 'ts-cell-empty'
  // Gris = pas de quart ce jour (comme un week-end) → montre la vraie dispo. Blanc = quart (barre d'occupation).
  // Une absence (congé/maladie) garde son fond ambré et sa barre colorée par-dessus.
  return { today: c.isToday, sel: drag.sel.has(c.iso), off: !!c.absType, noshift: !c.absType && !c.hasShift }
}
function absColor (type) { const b = BRUSHES.find(x => x.type === type); return b ? b.color : '#94a3b8' }
function occColor (o) { if (o == null) return '#94a3b8'; if (o < 0.5) return '#22c55e'; if (o < 0.85) return '#f59e0b'; return '#ef4444' } // occupation : vert < ambre < rouge

function shiftMonth (n) {
  const [y, m] = month.value.split('-').map(Number)
  const d = new Date(Date.UTC(y, m - 1 + n, 1))
  month.value = d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1)
  loadCal()
}
async function loadCal () {
  if (!props.tech || !month.value) return
  calLoading.value = true
  try {
    const [y, m] = month.value.split('-').map(Number)
    const start = y + '-' + pad(m) + '-01'
    const days = new Date(Date.UTC(y, m, 0)).getUTCDate()
    const [absRes, holiRes, occRes, asgRes] = await Promise.all([
      roster.getAbsences(start, days),
      roster.holidays(start, y + '-' + pad(m) + '-' + pad(days)),
      techOccupancy({ after_date: start, days, skill: '' }).catch(() => null), // quart + occupation par jour (tous techs) → on garde le nôtre
      roster.listAssignments(start, days).catch(() => null), // quarts réguliers du mois → menu « shifts en place » / retrait
    ])
    const out = {}
    const abs = (absRes && absRes.absences) || {}
    for (const [k, type] of Object.entries(abs)) {
      const [tid, date] = k.split('|')
      if (tid === props.tech.id || tid === props.tech.name) out[date] = type
    }
    absMap.value = out
    holiSet.value = new Set((holiRes && (holiRes.holidays || [])).map(h => h.date || h))
    // Quart réel par jour pour CE tech (jour « off » = pas de quart → grisé ; sinon barre d'occupation).
    const sh = {}
    const mine = occRes && (occRes.techs || []).find(x => x.tech_id === props.tech.id || x.tech_name === props.tech.name)
    if (mine) for (const d of (mine.days || [])) sh[d.date] = { shift: !d.off, occupancy: d.occupancy }
    shiftMap.value = sh
    // Quarts réguliers du mois pour CE tech → menu « shifts en place » + retrait par docname.
    const abd = {}
    for (const a of ((asgRes && asgRes.assignments) || [])) {
      if (a.tech !== props.tech.id && a.tech !== props.tech.name) continue
      ;(abd[a.date] || (abd[a.date] = [])).push({ name: a.name, shift: a.shift, shift_name: a.shift_name || a.shift, hours: a.hours })
    }
    asgByDate.value = abd
  } catch (e) { err(e) } finally { calLoading.value = false }
}

// Interaction : CLIC SIMPLE (sans glisser) = menu de cellule partagé (Jour/Soir/Garde/Absent/heures) ; GLISSER = pinceau
// rapide de congés (multi-jours, vacances). Le congé reste STAGÉ (publish-required) : on émet le delta au parent, 0 écriture ici.
function startDrag (c, ev) { drag.on = true; drag.moved = false; drag.sel = new Set([c.iso]); if (ev) { cellMenu.x = ev.clientX; cellMenu.y = ev.clientY } }
function overDrag (c) { if (!drag.on) return; if (!drag.sel.has(c.iso)) drag.moved = true; drag.sel.add(c.iso) }
function cancelDrag () { drag.on = false; drag.sel = new Set(); drag.moved = false }
function endDrag () {
  if (!drag.on) return
  drag.on = false
  const days = [...drag.sel]; drag.sel = new Set()
  if (!days.length || !props.tech) return
  if (!drag.moved) { openCellMenu(days[0]); return } // clic simple → menu (pas d'application du pinceau)
  const changes = days.map(iso => {
    if (brush.value === 'clear') return { iso, type: '' }
    const cur = effAbsType(iso)
    return { iso, type: cur === brush.value ? '' : brush.value } // même type → toggle off
  })
  emit('stage-abs', { techId: props.tech.id, changes })
}
// ── Menu de cellule (clic simple) — émet des intentions au parent (single source of logic). ──
function openCellMenu (iso) { cellMenu.iso = iso; cellMenu.show = true }
function onMenuWindow (r) { if (props.tech && r) emit('set-shift', { techId: props.tech.id, iso: cellMenu.iso, min: r.min, max: r.max }); cellMenu.show = false }
function onMenuGarde () { if (props.tech) emit('toggle-garde', { techId: props.tech.id, iso: cellMenu.iso }); cellMenu.show = false }
function onMenuAbsent () { if (props.tech) emit('stage-abs', { techId: props.tech.id, changes: [{ iso: cellMenu.iso, type: menuIsAbsent.value ? '' : 'Congé' }] }); cellMenu.show = false }
function onMenuRemoveShift (a) { if (a && a.name) emit('remove-shift', { name: a.name }); cellMenu.show = false }
function onMenuClear () { if (props.tech) emit('clear-shifts', { techId: props.tech.id, iso: cellMenu.iso, names: (asgByDate.value[cellMenu.iso] || []).map(a => a.name) }); cellMenu.show = false }

// ── Archivage (réversible) ───────────────────────────────────────────────────
const archiveBusy = ref(false)
function confirmArchive () {
  $q.dialog({
    title: 'Archiver le technicien',
    message: `${props.tech?.name} sera masqué du dispatch, du solveur et de la recherche de créneaux. L'historique est conservé — réversible depuis « Congés & disponibilités ».`,
    cancel: { label: 'Annuler', flat: true }, ok: { label: 'Archiver', color: 'negative', unelevated: true }, persistent: true,
  }).onOk(async () => {
    archiveBusy.value = true
    try {
      await roster.archiveTechnician(props.tech.id, true)
      $q.notify({ type: 'info', message: props.tech.name + ' archivé' })
      emit('changed', { id: props.tech.id, archived: true })
      emit('update:modelValue', false)
    } catch (e) { err(e) } finally { archiveBusy.value = false }
  })
}

// Ouverture : synchronise l'état pause + charge le mois courant.
watch(() => props.modelValue, (o) => {
  if (!o) return
  paused.value = (props.tech && props.tech.status === 'En pause') || props.pendingPaused // serveur OU en attente (staged)
  pauseNote.value = ''
  const t = todayISO()
  month.value = t.slice(0, 7)
  absMap.value = {}; shiftMap.value = {}; asgByDate.value = {}; holiSet.value = new Set(); drag.on = false; drag.sel = new Set(); cellMenu.show = false
  loadCal()
})
// Le parent a persisté un quart (createShift/deleteAssignment) → recharger le mois pour refléter les shifts.
watch(() => props.refreshKey, () => { if (props.modelValue) loadCal() })
</script>

<style scoped>
.ts-block { border: 1px solid #e2e8f0; border-radius: 10px; padding: 12px; }
.ts-brush { display: inline-flex; align-items: center; padding: 2px 10px; border: 1.5px solid #cbd5e1; border-radius: 12px; font-size: 12px; font-weight: 600; cursor: pointer; user-select: none; background: #fff; }
.ts-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; user-select: none; }
.ts-dowh { text-align: center; font-size: 10.5px; color: #64748b; font-weight: 700; padding-bottom: 2px; }
.ts-cell { position: relative; aspect-ratio: 1 / 1; border: 1px solid #e2e8f0; border-radius: 6px; cursor: pointer; display: flex; align-items: flex-start; justify-content: flex-start; padding: 3px 5px; background: #fff; transition: background .08s, border-color .08s; }
.ts-cell:hover { border-color: #2563eb; }
.ts-cell.noshift { background: #eef1f4; border-color: #e2e8f0; } /* pas de quart ce jour → grisé (montre la vraie dispo) */
.ts-cell.noshift:hover { border-color: #cbd5e1; }
.ts-cell.today { border-color: #2563eb; box-shadow: inset 0 0 0 1px #2563eb; }
.ts-cell.sel { background: #dbeafe; border-color: #2563eb; }
.ts-cell.off { background: #fff7ed; }
.ts-cell-empty { border: none; background: transparent; cursor: default; }
.ts-daynum { font-size: 11.5px; color: #334155; font-weight: 600; }
.ts-abs { position: absolute; left: 4px; right: 4px; bottom: 4px; height: 5px; border-radius: 3px; }
.ts-occ { position: absolute; left: 4px; right: 4px; bottom: 4px; height: 4px; border-radius: 3px; background: #e5e7eb; overflow: hidden; }
.ts-occ-fill { display: block; height: 100%; border-radius: 3px; }
.ts-holi { position: absolute; top: 2px; right: 3px; color: #b45309; font-size: 10px; }
.ts-legend { font-size: 10.5px; color: #475569; }
.ts-dot { display: inline-block; width: 9px; height: 9px; border-radius: 2px; vertical-align: middle; margin-right: 3px; }
.ts-dot-today { background: #fff; border: 1.5px solid #2563eb; }
</style>
