import { ref, computed } from 'vue'

/**
 * useJobPool — cerveau UNIQUE des pools « Jobs à assigner » (panneau flottant, colonne Jour/kanban, liste mobile).
 *
 * Historique : trois réimplémentations divergentes (assignJobsFiltered/assignGroups, kanbanPoolView, poolJobs) filtraient
 * et triaient le MÊME tableau (assignPanel.jobs). Ce composable les fusionne : une instance PAR surface (créée et détenue
 * par la page → la carte du panneau flottant garde sa source de vérité en lisant `filteredJobs`). Le composant JobPool.vue
 * ne fait que rendre `groups`/`filteredJobs` + les badges. Toute la logique d'assignation/drag reste dans la page.
 *
 * @param {Function} jobsGetter  () => Array  — source réactive (ex. () => assignPanel.jobs)
 * @param {Object}   deps        { jobCity, jobLocKey, todayISO, fmtDueLabel, distOf? } — helpers PURS de la page (source unique)
 * @param {Object}   opts        { variant:'floating'|'docked'|'mobile', defaultSort?, selectedDay?:()=>iso }
 */
export function useJobPool (jobsGetter, deps, opts = {}) {
  const { jobCity, jobLocKey, todayISO, fmtDueLabel } = deps
  const distOf = deps.distOf || null // (j) => km depuis le dépôt — seulement le variant « docked » l'utilise
  const variant = opts.variant || 'floating'
  const selectedDay = opts.selectedDay || (() => null)

  const PRIO_RANK = { urgent: 0, high: 1, 'élevée': 1, elevee: 1, medium: 2, moyenne: 2, normal: 2, low: 3, basse: 3 }
  const ASSIGN_PRIO = { urgent: 0, high: 1, medium: 2, low: 3 }
  const pr = j => (PRIO_RANK[String(j.priority || '').toLowerCase()] ?? 2)
  const durMin = j => (+j.est_min || (+j.duration_h || 1) * 60)

  // État de vue (PAR instance, vit dans la page → survit au démontage du composant lors d'un changement de vue).
  const skillFilter = ref([]) // compétences cochées (vide = toutes)
  const dateFilter = ref([]) // isos de date DUE cochés ('__overdue__' = toutes passées)
  const sort = ref(opts.defaultSort || (variant === 'mobile' ? 'smart' : variant === 'docked' ? 'prio' : 'date'))
  const sortDir = ref('asc')
  const search = ref('')
  const collapsed = ref(new Set())

  const jobs = computed(() => jobsGetter() || [])

  // Chips compétence : {k,n} par required_skill (le plus fréquent d'abord).
  const skillChips = computed(() => {
    const m = {}
    for (const j of jobs.value) { const k = j.required_skill || 'autre'; m[k] = (m[k] || 0) + 1 }
    return Object.entries(m).map(([k, n]) => ({ k, n })).sort((a, b) => b.n - a.n)
  })

  // Chips date DUE : dates passées regroupées en « ⏰ en retard », futures + « Sans date » séparées.
  const dateChips = computed(() => {
    const t = todayISO(); const m = {}
    for (const j of jobs.value) { const d = (j.scheduled_date && j.scheduled_date !== 'Sans date') ? j.scheduled_date : 'Sans date'; m[d] = (m[d] || 0) + 1 }
    const out = []; let overdueN = 0
    for (const [iso, n] of Object.entries(m)) {
      if (iso !== 'Sans date' && iso < t) { overdueN += n; continue }
      out.push({ iso, n, label: iso === 'Sans date' ? 'Sans date' : fmtDueLabel(iso), overdue: false })
    }
    out.sort((a, b) => String(a.iso).localeCompare(String(b.iso)))
    if (overdueN) out.unshift({ iso: '__overdue__', n: overdueN, label: '⏰ en retard', overdue: true })
    return out
  })

  function jobMatchesDateChips (j) {
    const df = dateFilter.value; if (!df.length) return true
    const ds = new Set(df); const t = todayISO()
    const d = (j.scheduled_date && j.scheduled_date !== 'Sans date') ? j.scheduled_date : 'Sans date'
    return ds.has(d) || (ds.has('__overdue__') && d !== 'Sans date' && d < t)
  }

  // Filtre : date d'abord (borne le périmètre) → compétence PAR ADRESSE (garde les jobs co-localisés d'un job filtré) → recherche texte.
  const filteredJobs = computed(() => {
    let arr = jobs.value
    if (dateFilter.value.length) arr = arr.filter(jobMatchesDateChips)
    const f = skillFilter.value
    if (f.length) {
      const set = new Set(f); const matchAddr = new Set()
      for (const j of arr) if (set.has(j.required_skill || 'autre')) { const k = jobLocKey(j); if (k) matchAddr.add(k) }
      arr = arr.filter(j => set.has(j.required_skill || 'autre') || matchAddr.has(jobLocKey(j)))
    }
    const q = search.value.trim().toLowerCase()
    if (q) arr = arr.filter(j => [j.subject, j.service_type, j.customer_name, j.address, j.required_skill].some(x => String(x || '').toLowerCase().includes(q)))
    return arr
  })

  // Info d'adresse (périmètre du jour, AVANT filtre compétence) : nb de jobs + compétences distinctes → badges.
  const addrInfo = computed(() => {
    const base = dateFilter.value.length ? jobs.value.filter(jobMatchesDateChips) : jobs.value
    const m = {}
    for (const j of base) { const k = jobLocKey(j); if (!k) continue; const g = m[k] || (m[k] = { n: 0, skills: new Set() }); g.n++; if (j.required_skill) g.skills.add(j.required_skill) }
    return m
  })
  function jobAddrBadge (j) { const k = jobLocKey(j); const g = k && addrInfo.value[k]; if (!g || g.n < 2) return null; const skills = [...g.skills]; return { n: g.n, skills, multi: skills.length > 1 } }
  function jobAssocOnly (j) { const f = skillFilter.value; return f.length ? !new Set(f).has(j.required_skill || 'autre') : false }

  // Modes de tri qui PRODUISENT des en-têtes de groupe (par variant) ; les autres = une seule liste à plat.
  const GROUPING = new Set(opts.groupingModes || (variant === 'mobile' ? ['sector'] : variant === 'docked' ? ['city', 'skill'] : ['group', 'skill', 'date', 'city', 'sector', 'priority']))
  const secOf = j => jobCity(j) || 'Sans secteur'
  const byDate = (a, b) => { const da = a.scheduled_date && a.scheduled_date !== 'Sans date' ? a.scheduled_date : '9999-99-99'; const db = b.scheduled_date && b.scheduled_date !== 'Sans date' ? b.scheduled_date : '9999-99-99'; return String(da).localeCompare(String(db)) }
  const dueRank = j => { const t = todayISO(); const sel = selectedDay(); const d = j.scheduled_date; if (d && d === sel) return 0; if (!d || d === 'Sans date') return 4; if (d < t) return 1; if (d === t) return 2; return 3 }

  const groups = computed(() => {
    const list = filteredJobs.value; const mode = sort.value
    // Groupe parent-enfant (installation → activation…), ordonné par step_order.
    if (mode === 'group') {
      const g = {}; for (const j of list) { const k = j.parent_job || j.name; (g[k] = g[k] || []).push(j) }
      return Object.keys(g).map(k => ({ key: k, label: null, jobs: g[k].slice().sort((a, b) => (a.step_order || 0) - (b.step_order || 0)) }))
    }
    // Tris À PLAT (une seule liste, pas d'en-tête) : smart / prio / dist / dur.
    if (!GROUPING.has(mode)) {
      const cmp = mode === 'prio' ? (a, b) => pr(a) - pr(b) || byDate(a, b)
        : mode === 'dist' && distOf ? (a, b) => (distOf(a) ?? 9e9) - (distOf(b) ?? 9e9)
          : mode === 'dur' ? (a, b) => durMin(b) - durMin(a) || dueRank(a) - dueRank(b)
            : (a, b) => dueRank(a) - dueRank(b) || pr(a) - pr(b) || byDate(a, b) // 'smart'
      return [{ key: 'all', label: null, jobs: [...list].sort(cmp) }]
    }
    // Tris GROUPÉS : skill / date / city / sector / priority.
    const keyOf = j => mode === 'skill' ? (j.required_skill || 'Sans compétence')
      : (mode === 'city' || mode === 'sector') ? secOf(j)
        : mode === 'priority' ? (j.priority || 'low')
          : (j.scheduled_date || 'Sans date')
    const labelOf = k => mode === 'priority' ? (({ urgent: '🔴 Urgent', high: '🟠 Élevée', medium: '🔵 Moyenne', low: '⚪ Basse' })[k] || k)
      : mode === 'date' ? '📅 ' + fmtDueLabel(k) : k
    const g = {}; for (const j of list) { const k = keyOf(j); (g[k] = g[k] || []).push(j) }
    const dir = sortDir.value === 'desc' ? -1 : 1
    const keys = Object.keys(g).sort((a, b) => (mode === 'priority' ? (ASSIGN_PRIO[a] ?? 9) - (ASSIGN_PRIO[b] ?? 9) : a.localeCompare(b)) * dir)
    return keys.map(k => {
      const gj = g[k]
      const mins = gj.reduce((s, j) => s + durMin(j), 0)
      return { key: k, label: labelOf(k), jobs: gj, n: gj.length, mins }
    })
  })

  const allCollapsed = computed(() => { const gs = groups.value; return gs.length > 0 && gs.every(g => g.label != null && collapsed.value.has(g.key)) })

  // Actions sur l'état de vue
  function toggleSkill (k) { const i = skillFilter.value.indexOf(k); if (i >= 0) skillFilter.value.splice(i, 1); else skillFilter.value.push(k) }
  function clearSkills () { skillFilter.value = [] }
  function toggleDate (iso) { const s = new Set(dateFilter.value); s.has(iso) ? s.delete(iso) : s.add(iso); dateFilter.value = [...s] }
  function clearDates () { dateFilter.value = [] }
  function toggleCollapse (key) { const s = new Set(collapsed.value); s.has(key) ? s.delete(key) : s.add(key); collapsed.value = s }
  function toggleCollapseAll () { collapsed.value = allCollapsed.value ? new Set() : new Set(groups.value.filter(g => g.label != null).map(g => g.key)) }

  return {
    variant,
    // état (writable)
    skillFilter, dateFilter, sort, sortDir, search, collapsed,
    // dérivés
    jobs, skillChips, dateChips, filteredJobs, groups, addrInfo, allCollapsed,
    // helpers
    jobMatchesDateChips, jobAddrBadge, jobAssocOnly,
    toggleSkill, clearSkills, toggleDate, clearDates, toggleCollapse, toggleCollapseAll,
  }
}
