<script setup lang="ts">
/**
 * Visualisation Donut — SVG custom, version maquette « Capture d'écran 2026-05-28 205310 ».
 *
 * Anneaux concentriques sobres (pas de couleurs vives sur les secteurs) :
 *   - Anneau extérieur gris  = "Plafond écologique" (limites planétaires)
 *   - Espace sûr cream       = zone où l'on veut que vivent les projets
 *   - Anneau intérieur gris  = "Plancher social" (besoins fondamentaux)
 *
 * Positionnement des projets :
 *   - Angle : par catégorie (6 secteurs de 60°), jitter angulaire
 *   - Rayon : déterminé par la DIMENSION
 *       social      -> bande intérieure (proche du plancher)
 *       ecologique  -> bande extérieure (proche du plafond)
 *       transversal -> milieu de la zone sûre
 *     puis modulé par le PROGRESS (plus progressé -> plus éloigné du centre,
 *     dans les limites de la bande de la dimension)
 *   - Projets archivés rendus en POINTS CREUX (stroke only).
 */

import { ref, computed, onMounted } from 'vue'
import { RouterLink, useRouter } from 'vue-router'
import { api, ApiError } from '@/api/client'
import type { Category, Dimension, Paginated, ProjectListRow, Wrapped } from '@/types/api'
import LoadingState from '@/components/LoadingState.vue'
import ErrorState from '@/components/ErrorState.vue'

const router = useRouter()
const categories = ref<Category[]>([])
const projects = ref<ProjectListRow[]>([])
const loading = ref(true)
const error = ref<string | null>(null)
const hoveredProject = ref<ProjectListRow | null>(null)

async function load() {
  loading.value = true
  error.value = null
  try {
    // /api/projects sans filtre renvoie déjà tous les non soft-deletés
    // (active + completed + archived) — utile pour les points creux des archives.
    const [cats, projs] = await Promise.all([
      api.get<Wrapped<Category>>('/api/categories'),
      api.get<Paginated<ProjectListRow>>('/api/projects', { query: { per_page: 100 } }),
    ])
    categories.value = [...cats.data].sort((a, b) => a.sort_order - b.sort_order)
    projects.value = projs.data
  } catch (e) {
    error.value = e instanceof ApiError ? (e.payload.message ?? e.code) : 'Erreur réseau'
  } finally {
    loading.value = false
  }
}
onMounted(load)

// --- Géométrie (en unités du viewBox) ---
const R_PLANET_LABEL  = 252  // étiquettes des limites planétaires (extérieur)
const R_CEILING_OUTER = 225
const R_CEILING_INNER = 200
const R_FLOOR_OUTER   = 100
const R_FLOOR_INNER   = 78
const R_SOCIAL_LABEL  = 67   // étiquettes des dimensions du plancher social (intérieur)
const R_CENTER        = 50

/** Les 9 limites planétaires de Kate Raworth, placées autour de l'anneau extérieur. */
const PLANETARY_BOUNDARIES = [
  'Changement climatique',
  'Acidification des océans',
  'Pollution chimique',
  'Cycles azote & phosphore',
  'Usage des sols',
  'Eau douce',
  'Biodiversité',
  "Pollution de l'air",
  "Couche d'ozone",
]

/** Les 12 dimensions du plancher social de Kate Raworth, placées entre le centre et l'anneau intérieur. */
const SOCIAL_FOUNDATIONS = [
  'Alimentation',
  'Santé',
  'Éducation',
  'Revenu & travail',
  'Eau',
  'Énergie',
  'Liens sociaux',
  'Logement',
  'Égalité de genre',
  'Équité sociale',
  'Voix politique',
  'Paix & justice',
]

/**
 * Génère pour chaque label un petit arc de cercle (utilisé par <textPath>).
 * Le texte épouse alors la courbure de l'anneau au lieu d'être posé en ligne droite.
 *
 * Astuce de lisibilité : on inverse le sens du path (CW vs CCW) selon la moitié
 * du cercle pour que les labels du bas restent lisibles à l'endroit. Sans ça,
 * les labels du bas seraient écrits à l'envers ou inversés (lus de droite à gauche).
 *
 *  - Moitié haute (angles proches de 0°) : path va de θ-δ à θ+δ, sweep=1 (CW visuellement)
 *    → direction RIGHT au sommet → texte "left of right" = UP = OUTSIDE
 *  - Moitié basse (angles proches de 180°) : path va de θ+δ à θ-δ, sweep=0 (CCW visuellement)
 *    → direction RIGHT au bas → texte "left of right" = UP = vers le CENTRE
 *    (= lisible à l'endroit pour un lecteur fixe en face de l'écran)
 *
 * @param labels    Liste des libellés
 * @param radius    Rayon de l'arc de chaque label
 * @param idPrefix  Préfixe pour générer les `id` des paths (chaque path doit être unique en DOM)
 * @param halfSpanDeg  Demi-largeur angulaire du path (= halfSpan ≈ 90 % du segment alloué au label)
 */
function arcLabelPaths(labels: readonly string[], radius: number, idPrefix: string, halfSpanDeg: number) {
  const n = labels.length
  const stepDeg = 360 / n
  return labels.map((label, i) => {
    const centerDeg = i * stepDeg + stepDeg / 2
    const startDeg = centerDeg - halfSpanDeg
    const endDeg = centerDeg + halfSpanDeg

    // Top half ⇨ chemin CW (sweep=1) ; bottom half ⇨ chemin CCW (sweep=0).
    const normalized = ((centerDeg % 360) + 360) % 360
    const isTopHalf = normalized <= 90 || normalized >= 270

    const fromDeg = isTopHalf ? startDeg : endDeg
    const toDeg   = isTopHalf ? endDeg   : startDeg
    const sweep   = isTopHalf ? 1 : 0

    const [x1, y1] = pt(fromDeg, radius)
    const [x2, y2] = pt(toDeg, radius)
    const d = `M ${x1.toFixed(2)} ${y1.toFixed(2)} A ${radius} ${radius} 0 0 ${sweep} ${x2.toFixed(2)} ${y2.toFixed(2)}`

    return { label, pathId: `${idPrefix}-${i}`, d, key: i }
  })
}

/** deg → rad avec offset -90° (0° en haut). */
function rad(deg: number): number {
  return (deg - 90) * Math.PI / 180
}
function pt(angleDeg: number, r: number): [number, number] {
  const a = rad(angleDeg)
  return [Math.cos(a) * r, Math.sin(a) * r]
}

/** Hash 32-bit basique pour stabiliser le jitter par projet. */
function strHash(s: string): number {
  let h = 0
  for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0
  return Math.abs(h)
}

/** Construit le path SVG d'un anneau-secteur (donut wedge) entre 2 rayons. */
function wedgePath(startDeg: number, endDeg: number, rInner: number, rOuter: number): string {
  const [x1, y1] = pt(startDeg, rOuter)
  const [x2, y2] = pt(endDeg, rOuter)
  const [x3, y3] = pt(endDeg, rInner)
  const [x4, y4] = pt(startDeg, rInner)
  const largeArc = endDeg - startDeg > 180 ? 1 : 0
  return [
    `M ${x1.toFixed(2)} ${y1.toFixed(2)}`,
    `A ${rOuter} ${rOuter} 0 ${largeArc} 1 ${x2.toFixed(2)} ${y2.toFixed(2)}`,
    `L ${x3.toFixed(2)} ${y3.toFixed(2)}`,
    `A ${rInner} ${rInner} 0 ${largeArc} 0 ${x4.toFixed(2)} ${y4.toFixed(2)}`,
    'Z',
  ].join(' ')
}

/** Secteurs colorés (tint subtil) pour chaque catégorie, dans la zone sûre. */
const thematicWedges = computed(() => {
  const n = categories.value.length || 6
  const step = 360 / n
  return categories.value.map((cat, i) => ({
    cat,
    path: wedgePath(i * step, (i + 1) * step, R_FLOOR_OUTER, R_CEILING_INNER),
    key: cat.id,
  }))
})

/** Lignes radiales de séparation des secteurs (entre plancher et plafond). */
const wedgeDividers = computed(() => {
  const n = categories.value.length || 6
  const step = 360 / n
  return Array.from({ length: n }, (_, i) => {
    const angle = i * step
    const [x1, y1] = pt(angle, R_FLOOR_OUTER)
    const [x2, y2] = pt(angle, R_CEILING_INNER)
    return { x1, y1, x2, y2, key: i }
  })
})

/** Position du label catégorie (juste sous l'anneau écologique, sur la zone sûre). */
const categoryLabels = computed(() => {
  const n = categories.value.length || 6
  const step = 360 / n
  return categories.value.map((cat, i) => {
    const center = i * step + step / 2
    const [x, y] = pt(center, (R_CEILING_INNER + R_FLOOR_OUTER) / 2)
    return { cat, x, y }
  })
})

/** Paths d'arc pour les 9 limites planétaires (anneau extérieur). */
const planetaryArcs = computed(() => arcLabelPaths(PLANETARY_BOUNDARIES, R_PLANET_LABEL, 'pb', 18))

/** Paths d'arc pour les 12 dimensions du plancher social (anneau intérieur). */
const socialArcs = computed(() => arcLabelPaths(SOCIAL_FOUNDATIONS, R_SOCIAL_LABEL, 'sf', 14))

interface DotView {
  project: ProjectListRow
  x: number
  y: number
  isHollow: boolean
  color: string
}

const dots = computed<DotView[]>(() => {
  if (categories.value.length === 0) return []
  // index catégorie par slug -> position du secteur (0..5)
  const catIndex = new Map<string, number>()
  categories.value.forEach((c, i) => catIndex.set(c.slug, i))
  const n = categories.value.length
  const step = 360 / n

  // Bands selon la dimension (intérieur/extérieur/milieu).
  const BANDS: Record<Dimension, [number, number]> = {
    social:      [R_FLOOR_OUTER + 8,  R_FLOOR_OUTER + 40],     // 108..140
    ecologique:  [R_CEILING_INNER - 40, R_CEILING_INNER - 8],  // 160..192
    transversal: [R_FLOOR_OUTER + 42, R_CEILING_INNER - 42],   // 142..158
  }

  // Groupage par catégorie pour répartir angulairement.
  const byCat = new Map<string, ProjectListRow[]>()
  for (const p of projects.value) {
    const list = byCat.get(p.category.slug) ?? []
    list.push(p)
    byCat.set(p.category.slug, list)
  }

  const out: DotView[] = []
  for (const [slug, list] of byCat.entries()) {
    const i = catIndex.get(slug) ?? 0
    const wedgeStart = i * step + 3
    const wedgeEnd   = (i + 1) * step - 3
    const span = wedgeEnd - wedgeStart

    list.forEach((p, idx) => {
      // Angle : répartition + jitter stable
      const t = (idx + 1) / (list.length + 1)
      const angleJitter = ((strHash(p.slug + 'a') % 100) / 100 - 0.5) * Math.min(6, span / Math.max(2, list.length))
      const angle = wedgeStart + span * t + angleJitter

      // Rayon : bande de dimension + progress
      const [rMin, rMax] = BANDS[p.category.dimension] ?? BANDS.transversal
      const progressR = rMin + (rMax - rMin) * (p.progress_percent / 100)
      const rJitter = ((strHash(p.slug + 'r') % 100) / 100 - 0.5) * 4
      const r = Math.max(rMin, Math.min(rMax, progressR + rJitter))

      const [x, y] = pt(angle, r)
      out.push({
        project: p,
        x, y,
        isHollow: p.status === 'archived',
        color: p.category.color_hex,
      })
    })
  }
  return out
})

// Légende — catégories regroupées par dimension avec leur count.
const projectsByCategorySlug = computed(() => {
  const m: Record<string, number> = {}
  for (const p of projects.value) {
    m[p.category.slug] = (m[p.category.slug] ?? 0) + 1
  }
  return m
})

const DIMENSION_ORDER: Dimension[] = ['social', 'ecologique', 'transversal']
const DIMENSION_META: Record<Dimension, { label: string; color: string }> = {
  social:      { label: 'Plancher social',    color: '#F97316' },
  ecologique:  { label: 'Plafond écologique', color: '#22C55E' },
  transversal: { label: 'Transversal',        color: '#6B7280' },
}

const categoriesByDimension = computed(() => {
  const map: Record<Dimension, Category[]> = { social: [], ecologique: [], transversal: [] }
  for (const c of categories.value) map[c.dimension].push(c)
  return map
})

function gotoProject(p: ProjectListRow) {
  router.push({ name: 'project-detail', params: { slug: p.slug } })
}
</script>

<template>
  <section class="px-10 py-8 max-w-7xl">
    <header class="text-center max-w-3xl mx-auto">
      <h1 class="text-3xl font-semibold text-donut-text">Le Donut des Projets Citoyens</h1>
      <p class="text-donut-muted mt-2">
        Inspiré du modèle de l'économie du Donut de Kate Raworth. L'Infolab des projets citoyens
        explore les initiatives qui poussent une vie marseillaise dans l'espace sûr et juste : entre
        plancher social et plafond écologique.
      </p>
    </header>

    <LoadingState v-if="loading" class="mt-12 justify-center" />
    <ErrorState v-else-if="error" :message="error" :retry="load" class="mt-12" />

    <div v-else class="mt-10 grid grid-cols-1 lg:grid-cols-5 gap-10 items-start">
      <!-- Donut SVG (3/5 sur grands écrans) -->
      <div class="lg:col-span-3 flex flex-col items-center">
        <svg viewBox="-280 -280 560 560" class="w-full max-w-[640px] aspect-square">
          <defs>
            <!-- Anneaux gris légèrement striés -->
            <pattern id="ring-stripes" patternUnits="userSpaceOnUse" width="6" height="6" patternTransform="rotate(45)">
              <rect width="6" height="6" fill="#ECECE7" />
              <line x1="0" y1="0" x2="0" y2="6" stroke="#D1D5DB" stroke-width="0.9" />
            </pattern>
            <!-- Arcs invisibles pour textPath : un par limite planétaire et par dimension sociale -->
            <path
              v-for="p in planetaryArcs" :key="p.pathId"
              :id="p.pathId" :d="p.d" fill="none"
            />
            <path
              v-for="s in socialArcs" :key="s.pathId"
              :id="s.pathId" :d="s.d" fill="none"
            />
          </defs>

          <!-- 1) ZONE D'OVERSHOOT (au-delà du plafond) -->
          <!--    fond cream clair pour soutenir les labels limites planétaires -->
          <circle r="270" fill="#F5F2EA" />

          <!-- 2) Étiquettes des 9 LIMITES PLANÉTAIRES, le long de leur arc -->
          <g>
            <text
              v-for="p in planetaryArcs" :key="`pb-text-${p.key}`"
              font-size="8" fill="#16A34A" font-weight="600"
            >
              <textPath :href="`#${p.pathId}`" startOffset="50%" text-anchor="middle">
                {{ p.label }}
              </textPath>
            </text>
          </g>

          <!-- 3) Anneau extérieur PLAFOND ÉCOLOGIQUE (gris strié) -->
          <circle :r="R_CEILING_OUTER" fill="url(#ring-stripes)" />
          <circle :r="R_CEILING_INNER" fill="#FAFAF6" />

          <!-- 4) Secteurs thématiques colorés (tint subtil, sur la zone sûre) -->
          <g>
            <path
              v-for="w in thematicWedges" :key="w.key"
              :d="w.path"
              :fill="w.cat.color_hex + '1A'"
              :stroke="w.cat.color_hex + '33'"
              stroke-width="0.5"
            />
          </g>

          <!-- 5) Anneau intérieur PLANCHER SOCIAL (gris strié) -->
          <circle :r="R_FLOOR_OUTER" fill="url(#ring-stripes)" />
          <circle :r="R_FLOOR_INNER" fill="#FAFAF6" />

          <!-- 6) Séparateurs radiaux entre secteurs -->
          <g stroke="#FFFFFF" stroke-width="1.5">
            <line
              v-for="d in wedgeDividers" :key="d.key"
              :x1="d.x1" :y1="d.y1" :x2="d.x2" :y2="d.y2"
            />
          </g>

          <!-- 7) Étiquettes catégories au milieu de leur wedge -->
          <g>
            <text
              v-for="l in categoryLabels" :key="`label-${l.cat.id}`"
              :x="l.x" :y="l.y"
              text-anchor="middle"
              font-size="9"
              :fill="l.cat.color_hex"
              font-weight="700"
              opacity="0.85"
            >{{ l.cat.name }}</text>
          </g>

          <!-- 8) Étiquettes des 12 DIMENSIONS DU PLANCHER SOCIAL, le long de leur arc -->
          <g>
            <text
              v-for="s in socialArcs" :key="`sf-text-${s.key}`"
              font-size="6" fill="#EA580C" font-weight="600"
            >
              <textPath :href="`#${s.pathId}`" startOffset="50%" text-anchor="middle">
                {{ s.label }}
              </textPath>
            </text>
          </g>

          <!-- 9) Points-projets : la classe `dot-circle` corrige le transform-origin -->
          <g>
            <circle
              v-for="d in dots" :key="d.project.id"
              :cx="d.x" :cy="d.y" r="6"
              :fill="d.isHollow ? '#FAFAF6' : d.color"
              :stroke="d.color"
              :stroke-width="d.isHollow ? 2 : 1.5"
              class="dot-circle"
              :class="hoveredProject?.id === d.project.id ? 'is-hover' : ''"
              @mouseenter="hoveredProject = d.project"
              @mouseleave="hoveredProject = null"
              @click="gotoProject(d.project)"
            >
              <title>{{ d.project.title }} — {{ d.project.progress_percent }}%</title>
            </circle>
          </g>

          <!-- 10) Centre -->
          <circle :r="R_CENTER" fill="#FFFFFF" stroke="#E5E7EB" />
          <text x="0" y="-4" text-anchor="middle" font-size="13" fill="#111827" font-weight="700"
                style="letter-spacing:2px">LE DONUT</text>
          <text x="0" y="12" text-anchor="middle" font-size="9" fill="#6B7280">
            {{ projects.length }} projet{{ projects.length > 1 ? 's' : '' }}
          </text>
        </svg>

        <!-- Tooltip projet survolé -->
        <div class="mt-2 h-12 flex items-center justify-center">
          <transition name="fade">
            <RouterLink
              v-if="hoveredProject"
              :to="{ name: 'project-detail', params: { slug: hoveredProject.slug } }"
              class="px-3 py-1.5 rounded-full bg-white border border-donut-border shadow-sm text-sm flex items-center gap-2 hover:shadow-md transition-shadow"
            >
              <span
                class="w-2 h-2 rounded-full"
                :style="{ background: hoveredProject.category.color_hex }"
              />
              <span class="font-medium">{{ hoveredProject.title }}</span>
              <span class="text-donut-muted text-xs">
                — {{ hoveredProject.progress_percent }}%
                <template v-if="hoveredProject.status === 'archived'"> · archivé</template>
                <template v-else-if="hoveredProject.status === 'completed'"> · réalisé</template>
              </span>
            </RouterLink>
            <div v-else class="text-xs text-donut-muted">
              Survolez un point pour voir le projet · cliquez pour ouvrir
            </div>
          </transition>
        </div>
      </div>

      <!-- Légende -->
      <aside class="lg:col-span-2 space-y-6">
        <div class="rounded-xl border border-donut-border bg-donut-surface p-5">
          <h2 class="text-base font-semibold text-donut-text">Comment lire ce donut ?</h2>
          <ul class="mt-3 space-y-2.5 text-sm text-donut-muted leading-relaxed">
            <li>L'<strong class="text-donut-text">anneau intérieur</strong> représente le
              <span class="font-medium" style="color:#F97316">plancher social</span>, qui définit les besoins fondamentaux.</li>
            <li>L'<strong class="text-donut-text">anneau extérieur</strong> représente le
              <span class="font-medium" style="color:#22C55E">plafond écologique</span>, qui définit les limites planétaires.</li>
            <li>L'espace entre les deux est l'<strong class="text-donut-text">espace sûr et juste</strong>.</li>
            <li>Les <strong class="text-donut-text">projets sociaux</strong> sont positionnés vers l'intérieur.</li>
            <li>Les <strong class="text-donut-text">projets écologiques</strong> sont positionnés vers l'extérieur.</li>
            <li>Plus un point est <strong class="text-donut-text">éloigné du centre</strong>, plus le projet est progressé.</li>
            <li>Les <strong class="text-donut-text">points creux</strong> sont les projets historiques.</li>
          </ul>
        </div>

        <div
          v-for="dim in DIMENSION_ORDER"
          :key="dim"
          class="rounded-xl border border-donut-border bg-donut-surface p-5"
        >
          <div class="flex items-center gap-2 mb-3">
            <span class="w-2.5 h-2.5 rounded-full" :style="{ background: DIMENSION_META[dim].color }" />
            <h3 class="text-xs font-semibold uppercase tracking-wider" :style="{ color: DIMENSION_META[dim].color }">
              {{ DIMENSION_META[dim].label }}
            </h3>
          </div>
          <ul v-if="categoriesByDimension[dim].length" class="space-y-2">
            <li
              v-for="c in categoriesByDimension[dim]"
              :key="c.id"
            >
              <RouterLink
                :to="{ name: 'projects', query: { category: c.slug } }"
                class="flex items-center justify-between gap-2 text-sm group"
              >
                <span class="flex items-center gap-2 min-w-0">
                  <span class="w-2.5 h-2.5 rounded-full shrink-0" :style="{ background: c.color_hex }" />
                  <span class="text-donut-text truncate group-hover:underline">{{ c.name }}</span>
                </span>
                <span class="text-donut-muted tabular-nums shrink-0">
                  {{ projectsByCategorySlug[c.slug] ?? 0 }}
                </span>
              </RouterLink>
            </li>
          </ul>
          <p v-else class="text-xs text-donut-muted">Aucune catégorie dans cette dimension.</p>
        </div>
      </aside>
    </div>
  </section>
</template>

<style scoped>
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.15s ease;
}
.fade-enter-from, .fade-leave-to {
  opacity: 0;
}

/* Fix anti-jitter sur les points-projets.
 *
 * Sans ces 2 propriétés, `transform: scale()` en SVG prend pour origine le (0,0)
 * du viewBox -> le cercle scalé est déplacé vers le centre, la souris n'est plus
 * dessus, mouseleave -> retour à la position d'origine -> mouseenter -> re-déplacé.
 * Boucle infinie de jitter.
 *
 * `transform-box: fill-box` change le référentiel à la bounding box de la forme,
 * et `transform-origin: center` met l'origine au centre de cette box.
 * Le scale se fait alors "sur place".
 */
.dot-circle {
  cursor: pointer;
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.15s ease;
}
.dot-circle:hover,
.dot-circle.is-hover {
  transform: scale(1.6);
}
</style>
