<template>
  <div style="width: 100%; height: 100%; position: relative; background: #f5f3f0;">
    <svg ref="svgRef" style="width: 100%; height: 100%;"></svg>
    <div ref="tooltipRef" style="
      position: absolute; pointer-events: none;
      background: var(--nav-surface); border: 1px solid var(--nav-bg-alt);
      border-radius: 6px; padding: 8px 12px; font-size: 0.78rem;
      color: var(--nav-text); max-width: 240px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);
      opacity: 0; transition: opacity 0.15s; z-index: 100;
    "></div>
  </div>
</template>

<script setup lang="ts">
interface EcoleData { id: string; label: string; description: string; color: string; x_hint: number; y_hint: number }
interface LivreRag { slug: string; titre: string; annee: number; couches: string[] }
interface AuteurData { id: string; nom: string; dates: string; ecoles: string[]; ecole_principale: string; ingere: boolean; livres_rag: LivreRag[]; theses_cles: string[]; bio_courte: string; bio_courte_provisoire?: string }
interface PenseesData { ecoles: EcoleData[]; auteurs: AuteurData[] }

// Liens d'influence inter-ecoles (Phase 7 - matrice de filiation)
const LINKS_INFLUENCE = [
  // filiations directes
  { source: 'eco-anarchisme', target: 'technocritique', auteurs_passerelle: ['Bookchin', 'Illich'], type: 'filiation' },
  { source: 'eco-anarchisme', target: 'decroissance', auteurs_passerelle: ['Latouche', 'Kropotkine'], type: 'filiation' },
  { source: 'ecosocialisme', target: 'decroissance', auteurs_passerelle: ['Saito', 'Gorz'], type: 'filiation' },
  { source: 'ecosocialisme', target: 'ecologies-decoloniales', auteurs_passerelle: ['Klein', 'Ferdinand'], type: 'filiation' },
  { source: 'ecofeminismes', target: 'ecologies-decoloniales', auteurs_passerelle: ['Shiva', 'Ouassak'], type: 'filiation' },
  { source: 'ecofeminismes', target: 'pensees-vivant', auteurs_passerelle: ['Haraway', 'Despret'], type: 'filiation' },
  { source: 'technocritique', target: 'decroissance', auteurs_passerelle: ['Ellul', 'Latouche'], type: 'filiation' },
  { source: 'decroissance', target: 'pensees-vivant', auteurs_passerelle: ['Servigne', 'Despret'], type: 'filiation' },
  { source: 'pensees-vivant', target: 'ethiques-environnementales', auteurs_passerelle: ['Naess', 'Latour'], type: 'filiation' },
  { source: 'ecosocialisme', target: 'eco-anarchisme', auteurs_passerelle: ['Gorz', 'Graeber'], type: 'filiation' },
  // liens de critique (toutes les ecoles progressistes vs cap-vert / ecofascismes)
  { source: 'ecosocialisme', target: 'capitalisme-vert', auteurs_passerelle: ['Klein', 'Malm'], type: 'critique' },
  { source: 'decroissance', target: 'capitalisme-vert', auteurs_passerelle: ['Latouche', 'Meadows'], type: 'critique' },
  { source: 'eco-anarchisme', target: 'capitalisme-vert', auteurs_passerelle: ['Bookchin'], type: 'critique' },
  { source: 'ethiques-environnementales', target: 'ecofascismes', auteurs_passerelle: ['Naess'], type: 'critique' },
  { source: 'capitalisme-vert', target: 'ecofascismes', auteurs_passerelle: [], type: 'critique' },
]

const props = defineProps<{ data: PenseesData | null; active?: boolean }>()
const emit = defineEmits<{ 'select-auteur': [id: string]; 'select-ecole': [id: string] }>()

const svgRef = ref<SVGElement | null>(null)
const tooltipRef = ref<HTMLElement | null>(null)
let simulation: any = null
let d3LinkSel: any = null
let d3InfluenceSel: any = null
let d3NodeSel: any = null
let d3EdgeLabelSel: any = null

async function initGraph() {
  if (!svgRef.value || !props.data) return
  const d3 = await import('d3')

  const svgEl = svgRef.value
  const W = svgEl.clientWidth || 900
  const H = svgEl.clientHeight || 600

  d3.select(svgEl).selectAll('*').remove()
  const svg = d3.select(svgEl).attr('viewBox', `0 0 ${W} ${H}`)
  const g = svg.append('g')

  svg.call(d3.zoom<SVGElement, unknown>().scaleExtent([0.3, 4]).on('zoom', (e) => g.attr('transform', e.transform)) as any)

  const ecoleMap = new Map<string, EcoleData>(props.data.ecoles.map(e => [e.id, e]))

  // Positions fixes des ecoles (base pour forces D3)
  const ecolePositions = new Map<string, { tx: number; ty: number }>()
  props.data.ecoles.forEach(e => {
    ecolePositions.set(e.id, { tx: W * e.x_hint, ty: H * e.y_hint })
  })

  // ---- LIENS D'INFLUENCE INTER-ECOLES (couche 3) ----
  const gInfluence = g.append('g').attr('class', 'links-influence')

  LINKS_INFLUENCE.forEach(link => {
    const src = ecolePositions.get(link.source)
    const tgt = ecolePositions.get(link.target)
    if (!src || !tgt) return

    const isCritique = link.type === 'critique'
    const lineEl = gInfluence.append('line')
      .attr('class', 'influence-link')
      .attr('x1', src.tx).attr('y1', src.ty)
      .attr('x2', tgt.tx).attr('y2', tgt.ty)
      .attr('stroke', isCritique ? '#d99' : '#9aa')
      .attr('stroke-width', 1)
      .attr('stroke-dasharray', isCritique ? '4,3' : '6,4')
      .attr('stroke-opacity', isCritique ? 0.2 : 0.22)

    if (link.auteurs_passerelle && link.auteurs_passerelle.length > 0) {
      lineEl
        .on('mouseenter', (e: any) => {
          if (!tooltipRef.value) return
          tooltipRef.value.innerHTML = `<strong>Influence</strong><br><span style="opacity:0.8;font-size:0.72rem;">Passerelles : ${link.auteurs_passerelle.join(', ')}</span>`
          tooltipRef.value.style.opacity = '1'
        })
        .on('mousemove', (e: any) => {
          if (!tooltipRef.value || !svgEl) return
          const rect = (svgEl as HTMLElement).getBoundingClientRect()
          tooltipRef.value.style.left = (e.clientX - rect.left + 14) + 'px'
          tooltipRef.value.style.top = (e.clientY - rect.top - 10) + 'px'
        })
        .on('mouseleave', () => { if (tooltipRef.value) tooltipRef.value.style.opacity = '0' })
    }
  })

  // ---- SIMULATION D3 (auteurs) ----
  // Pre-positionner chaque auteur pres de son ecole + jitter aleatoire pour eviter le rush initial vers la droite
  const auteurNodes: any[] = props.data.auteurs.map(a => {
    const ecole = ecoleMap.get(a.ecole_principale)
    const jitter = () => (Math.random() - 0.5) * 80
    return {
      id: a.id, type: 'auteur', nom: a.nom, dates: a.dates,
      bio_courte: a.bio_courte,
      bio_provisoire: a.bio_courte_provisoire ?? '',
      ingere: a.ingere,
      ecole_principale: a.ecole_principale,
      color: ecole?.color ?? '#888', r: 11,
      x: W * (ecole?.x_hint ?? 0.5) + jitter(),
      y: H * (ecole?.y_hint ?? 0.5) + jitter(),
    }
  })

  // Liens appartenance auteur -> ecole (vers centroid fixe)
  const links: any[] = []
  props.data.auteurs.forEach(a => {
    links.push({ source: a.id, target: a.ecole_principale, strength: 0.65, isSubcourant: false })
    a.ecoles.filter(e => e !== a.ecole_principale).forEach(e => {
      links.push({ source: a.id, target: e, strength: 0.25, isSubcourant: true })
    })
  })

  // Nodes fictifs fixes pour les ecoles (cibles des liens appartenance)
  const ecoleFixedNodes: any[] = props.data.ecoles.map(e => ({
    id: e.id, type: 'ecole-fixed', ecoleId: e.id,
    x: W * e.x_hint, y: H * e.y_hint,
    fx: W * e.x_hint, fy: H * e.y_hint,
  }))

  // Rayon proportionnel au nombre d'auteurs de l'ecole
  const ecoleAuteurCounts = new Map<string, number>()
  props.data.ecoles.forEach(e => ecoleAuteurCounts.set(e.id, 0))
  props.data.auteurs.forEach(a => ecoleAuteurCounts.set(a.ecole_principale, (ecoleAuteurCounts.get(a.ecole_principale) ?? 0) + 1))
  const ecoleRadius = (count: number) => Math.max(16, Math.min(36, 13 + count * 1.5))

  const allNodes = [...ecoleFixedNodes, ...auteurNodes]

  if (simulation) simulation.stop()
  // Phase 8.D : sim ajustee pour 171 auteurs (vs 28 v2.1, densite 6x)
  simulation = d3.forceSimulation(allNodes)
    .force('link', d3.forceLink(links).id((d: any) => d.id).distance(120).strength((d: any) => d.strength ?? 0.5))
    .force('charge', d3.forceManyBody().strength(-70))
    .force('center', d3.forceCenter(W / 2, H / 2).strength(0.02))
    .force('collision', d3.forceCollide().radius((d: any) => d.type === 'ecole-fixed' ? ecoleRadius(ecoleAuteurCounts.get(d.ecoleId) ?? 0) + 4 : 12))
    .force('forceX', d3.forceX<any>((d: any) => {
      if (d.type === 'auteur') {
        const pos = ecolePositions.get(d.ecole_principale)
        return pos ? pos.tx : W / 2
      }
      return W / 2
    }).strength(0.15))
    .force('forceY', d3.forceY<any>((d: any) => {
      if (d.type === 'auteur') {
        const pos = ecolePositions.get(d.ecole_principale)
        return pos ? pos.ty : H / 2
      }
      return H / 2
    }).strength(0.15))

  // ---- NOEUDS ECOLES visibles (couche 3.5) ----
  // Cercles proportionnels au count d'auteurs, fixes aux centroids Bonpote, cliquables
  const gEcoles = g.append('g').attr('class', 'ecoles-nodes')
  ecoleFixedNodes.forEach(eNode => {
    const ecole = ecoleMap.get(eNode.ecoleId)
    if (!ecole) return
    const count = ecoleAuteurCounts.get(eNode.ecoleId) ?? 0
    const r = ecoleRadius(count)
    gEcoles.append('circle')
      .attr('cx', eNode.fx).attr('cy', eNode.fy).attr('r', r)
      .attr('fill', ecole.color).attr('fill-opacity', 0.82).attr('stroke', ecole.color).attr('stroke-width', 2)
      .attr('class', 'ecole-node').style('cursor', 'pointer')
      .on('mouseenter', (e: any) => {
        if (!tooltipRef.value) return
        tooltipRef.value.innerHTML = `<strong>${ecole.label}</strong> <span style="opacity:0.6;font-size:0.7rem;">${count} auteur${count > 1 ? 's' : ''}</span><br><span style="opacity:0.75;font-size:0.72rem;">${ecole.description}</span>`
        tooltipRef.value.style.opacity = '1'
      })
      .on('mousemove', (e: any) => {
        if (!tooltipRef.value || !svgEl) return
        const rect = (svgEl as HTMLElement).getBoundingClientRect()
        tooltipRef.value.style.left = (e.clientX - rect.left + 14) + 'px'
        tooltipRef.value.style.top = (e.clientY - rect.top - 10) + 'px'
      })
      .on('mouseleave', () => { if (tooltipRef.value) tooltipRef.value.style.opacity = '0' })
      .on('click', (e: any) => { e.stopPropagation(); emit('select-ecole', eNode.ecoleId) })
  })

  // ---- LIENS APPARTENANCE (couche 4) ----
  const gLinks = g.append('g').attr('class', 'links-appartenance')
  d3LinkSel = gLinks.selectAll('line').data(links).join('line')
    .attr('stroke', 'rgba(150,150,150,0.28)').attr('stroke-width', 1.2)

  // ---- EDGE LABELS - sous-courants (couche 4b) ----
  // Afficher label "decroissance" sur lien Servigne (sous-courant specifique - option C)
  const subcourantLinks = links.filter((l: any) => l.isSubcourant)
  d3EdgeLabelSel = gLinks.selectAll('text.pensees-edge-label')
    .data(subcourantLinks)
    .join('text')
    .attr('class', 'pensees-edge-label')

  // ---- NODES AUTEURS (couche 5) ----
  const gAuteurs = g.append('g').attr('class', 'auteurs')
  d3NodeSel = gAuteurs.selectAll('g').data(auteurNodes).join('g')
    .style('cursor', (d: any) => d.ingere ? 'pointer' : 'default')
    .call(d3.drag<any, any>()
      .on('start', (e: any, d: any) => { if (!e.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y })
      .on('drag', (e: any, d: any) => { d.fx = e.x; d.fy = e.y })
      .on('end', (e: any, d: any) => { if (!e.active) simulation.alphaTarget(0); d.fx = null; d.fy = null }))
    .on('click', (e: any, d: any) => {
      if (!d.ingere) return
      e.stopPropagation()
      emit('select-auteur', d.id)
    })

  // Phase 8.D : grisage conditionnel auteurs non-ingeres (ingere:false)
  d3NodeSel.append('circle')
    .attr('r', (d: any) => d.r)
    .attr('fill', (d: any) => d.ingere ? (d.color + 'cc') : '#bbbbbb')
    .attr('stroke', (d: any) => d.ingere ? d.color : '#999999')
    .attr('stroke-width', 1.5)
    .attr('opacity', (d: any) => d.ingere ? 1 : 0.35)

  // ---- LABELS AUTEURS (couche 6 - fix 7.1 : drop-shadow blanc) ----
  d3NodeSel.append('text')
    .attr('class', 'pensees-auteur-label')
    .text((d: any) => d.nom.split(' ').pop() ?? d.nom)
    .attr('text-anchor', 'middle')
    .attr('dy', (d: any) => -(d.r + 4))
    .style('pointer-events', 'none')
    .style('opacity', (d: any) => d.ingere ? 1 : 0.3)
    .style('fill', (d: any) => d.ingere ? '#1a1a1a' : '#777777')

  d3NodeSel
    .on('mouseenter', (e: any, d: any) => {
      if (!tooltipRef.value) return
      let tooltipHtml = ''
      if (d.ingere) {
        const bio = d.bio_courte.length > 90 ? d.bio_courte.slice(0, 87) + '...' : d.bio_courte
        tooltipHtml = `<strong>${d.nom}</strong> <span style="opacity:0.6;font-size:0.7rem;">${d.dates}</span><br><span style="opacity:0.75;font-size:0.72rem;">${bio}</span>`
      } else {
        tooltipHtml = `<strong>${d.nom}</strong> <span style="opacity:0.6;font-size:0.7rem;">${d.dates}</span><br><span style="opacity:0.65;font-size:0.72rem;font-style:italic;">Présent dans Bonpote, pas encore ingéré dans le RAG ATIS.</span>`
      }
      tooltipRef.value.innerHTML = tooltipHtml
      tooltipRef.value.style.opacity = '1'
    })
    .on('mousemove', (e: any) => {
      if (!tooltipRef.value || !svgEl) return
      const rect = (svgEl as HTMLElement).getBoundingClientRect()
      tooltipRef.value.style.left = (e.clientX - rect.left + 14) + 'px'
      tooltipRef.value.style.top = (e.clientY - rect.top - 10) + 'px'
    })
    .on('mouseleave', () => { if (tooltipRef.value) tooltipRef.value.style.opacity = '0' })

  simulation.on('tick', () => {
    d3LinkSel
      .attr('x1', (d: any) => d.source.x).attr('y1', (d: any) => d.source.y)
      .attr('x2', (d: any) => d.target.x).attr('y2', (d: any) => d.target.y)

    // Edge labels positions (milieu du lien)
    d3EdgeLabelSel
      .attr('x', (d: any) => (d.source.x + d.target.x) / 2)
      .attr('y', (d: any) => (d.source.y + d.target.y) / 2)
      .text((d: any) => {
        const targetId = typeof d.target === 'object' ? d.target.id : d.target
        return targetId
      })

    d3NodeSel.attr('transform', (d: any) => `translate(${d.x},${d.y})`)
  })
}

watch(() => props.active, (val) => { if (val && import.meta.client && props.data) requestAnimationFrame(() => requestAnimationFrame(() => initGraph())) })
watch(() => props.data, (val) => { if (val && props.active && import.meta.client) requestAnimationFrame(() => requestAnimationFrame(() => initGraph())) })
onMounted(async () => { if (import.meta.client && props.data && props.active) { await nextTick(); initGraph() } })
onUnmounted(() => { if (simulation) simulation.stop() })

function triggerResize() {
  if (simulation) {
    simulation.alpha(0.3).restart()
  } else if (import.meta.client && props.data && props.active) {
    initGraph()
  }
}
defineExpose({ triggerResize })
</script>

<style>
/* ---- Labels auteurs : fix 7.1 drop-shadow blanc pour lisibilite sur pastel ---- */
.pensees-auteur-label {
  fill: #1a1a1a;
  font-weight: 600;
  font-size: 10px;
  filter: drop-shadow(0 0 2.5px rgba(255,255,255,0.95));
  user-select: none;
}

/* ---- Labels edge sous-courants (option C : seulement les liens secondaires) ---- */
.pensees-edge-label {
  fill: #555;
  font-size: 8.5px;
  font-style: italic;
  opacity: 0.7;
  text-anchor: middle;
  dominant-baseline: middle;
  user-select: none;
  pointer-events: none;
}

/* ---- Voronoi cellules : non-blurre Phase 8.F (revert Phase 8.D) ---- */
/* Blur retire ; les cellules colorees Bonpote-aligned suffisent visuellement. */

.ecole-node {
  transition: opacity 0.15s, r 0.15s;
}
.ecole-node:hover {
  opacity: 0.75;
}

</style>
