<template>
  <div class="graph-view" style="width: 100%; height: 100%; position: relative; background: var(--nav-bg);">
    <!-- Canvas SVG pour D3 (zone centrale, marge droite pour sidebar) -->
    <svg
      ref="svgRef"
      :style="{
        width: sidebarOpen ? 'calc(100% - 200px)' : 'calc(100% - 40px)',
        height: '100%',
        transition: 'width 0.2s ease',
      }"
    ></svg>

    <!-- Sidebar hashtags droite (repliable) -->
    <aside
      :style="{
        position: 'absolute', top: '0', right: '0', bottom: '0',
        width: sidebarOpen ? '200px' : '40px',
        background: 'var(--nav-surface)',
        borderLeft: '1px solid var(--nav-bg-alt)',
        display: 'flex', flexDirection: 'column',
        transition: 'width 0.2s ease',
        zIndex: 10,
      }"
    >
      <!-- Toggle (toujours visible) -->
      <button
        @click="sidebarOpen = !sidebarOpen"
        :title="sidebarOpen ? 'Replier la sidebar' : 'Deplier la sidebar'"
        style="
          width: 100%; height: 36px; flex-shrink: 0;
          display: flex; align-items: center; justify-content: center;
          background: var(--nav-bg-alt); border: none; cursor: pointer;
          color: var(--nav-text-muted); font-size: 0.78rem; font-weight: 700;
          border-bottom: 1px solid var(--nav-bg-alt);
        "
      >{{ sidebarOpen ? '>>' : '<<' }}</button>

      <!-- Mode replie : label vertical -->
      <div
        v-if="!sidebarOpen"
        style="
          flex: 1; display: flex; align-items: center; justify-content: center;
          writing-mode: vertical-rl; transform: rotate(180deg);
          font-size: 0.7rem; font-weight: 700; color: var(--nav-text-muted);
          letter-spacing: 0.12em; text-transform: uppercase;
        "
      >HASHTAGS ({{ activeHashtags.length }}/{{ props.allHashtags.length }})</div>

      <!-- Mode deplie : header + liste groupee -->
      <template v-if="sidebarOpen">
        <div style="padding: 8px 12px; border-bottom: 1px solid var(--nav-bg-alt); flex-shrink: 0;">
          <div style="display: flex; align-items: center; justify-content: space-between; gap: 8px;">
            <span style="font-size: 0.72rem; font-weight: 700; color: var(--nav-text); text-transform: uppercase; letter-spacing: 0.08em;">Hashtags</span>
            <span style="font-size: 0.68rem; color: var(--nav-text-muted);">{{ activeHashtags.length }} actif{{ activeHashtags.length > 1 ? 's' : '' }}</span>
          </div>
          <button
            v-if="activeHashtags.length"
            @click="activeHashtags = []"
            style="margin-top: 4px; font-size: 0.68rem; color: var(--nav-text-muted); background: none; border: none; cursor: pointer; text-decoration: underline; padding: 0;"
          >Tout effacer</button>
        </div>

        <div style="flex: 1; overflow-y: auto; padding: 6px 10px 10px;">
          <div
            v-for="group in hashtagsByFamille"
            :key="group.famille"
            style="margin-bottom: 10px;"
          >
            <div
              :style="{
                fontSize: '0.65rem', fontWeight: 700,
                color: group.color, textTransform: 'uppercase',
                letterSpacing: '0.06em', marginBottom: '4px',
                paddingLeft: '2px',
              }"
            >{{ group.label }}</div>
            <div style="display: flex; flex-wrap: wrap; gap: 3px;">
              <span
                v-for="tag in group.tags"
                :key="tag"
                style="padding: 2px 7px; border-radius: 9999px; font-size: 0.66rem; cursor: pointer; transition: all 0.12s;"
                :style="activeHashtags.includes(tag)
                  ? `background: ${group.color}; color: white; font-weight: 600;`
                  : 'background: var(--nav-bg-alt); color: var(--nav-text-muted);'"
                @click="toggleHashtag(tag)"
              >{{ tag }}</span>
            </div>
          </div>
        </div>
      </template>
    </aside>

    <!-- Tooltip -->
    <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: 220px; 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">
import type { ReseauxBifurcationData } from '~/types/structure-v2'

const props = defineProps<{
  data: ReseauxBifurcationData | null
  allHashtags: string[]
  active?: boolean
}>()

const emit = defineEmits<{
  'select-structure': [id: string]
}>()

const svgRef = ref<SVGElement | null>(null)
const tooltipRef = ref<HTMLElement | null>(null)

// Hashtag filter
const activeHashtags = ref<string[]>([])
const sidebarOpen = ref(true)

function toggleHashtag(tag: string) {
  activeHashtags.value = activeHashtags.value.includes(tag)
    ? activeHashtags.value.filter(t => t !== tag)
    : [...activeHashtags.value, tag]
}

// Mapping hashtag -> famille majoritaire
// En cas d'egalite : prendre la famille la plus petite (visibilite minoritaires)
const hashtagsByFamille = computed(() => {
  if (!props.data) return []
  // 1. Pour chaque hashtag, compter les structures par famille
  const counts: Record<string, Record<number, number>> = {}
  props.data.structures.forEach(s => {
    s.hashtags.forEach(tag => {
      if (!counts[tag]) counts[tag] = {}
      counts[tag][s.famille_principale] = (counts[tag][s.famille_principale] ?? 0) + 1
    })
  })
  // 2. Pour chaque hashtag, trouver la famille majoritaire (egalite -> + petite famille)
  // Pour preferer la famille la moins peuplee globalement, calculer la taille de chaque famille.
  const familleSize: Record<number, number> = {}
  props.data.structures.forEach(s => {
    familleSize[s.famille_principale] = (familleSize[s.famille_principale] ?? 0) + 1
  })
  const tagToFamille: Record<string, number> = {}
  for (const tag in counts) {
    const entries = Object.entries(counts[tag])
    entries.sort((a, b) => {
      const diff = (b[1] as number) - (a[1] as number)
      if (diff !== 0) return diff
      // egalite : famille avec moins de structures gagne
      return (familleSize[Number(a[0])] ?? 0) - (familleSize[Number(b[0])] ?? 0)
    })
    tagToFamille[tag] = Number(entries[0][0])
  }
  // 3. Grouper les hashtags par famille
  const groups: Record<number, string[]> = {}
  props.allHashtags.forEach(tag => {
    const fam = tagToFamille[tag]
    if (fam == null) return
    if (!groups[fam]) groups[fam] = []
    groups[fam].push(tag)
  })
  // 4. Sortie ordonnee selon ID de famille
  return [1, 2, 3, 4, 5, 6]
    .filter(famId => groups[famId]?.length)
    .map(famId => ({
      famille: famId,
      label: FAMILLE_LABELS[famId],
      color: FAMILLE_COLORS[famId],
      tags: groups[famId].sort(),
    }))
})

// IDs de structures correspondant aux hashtags actifs
const filteredStructureIds = computed(() => {
  if (!props.data || !activeHashtags.value.length) return null
  const ids = new Set(
    props.data.structures
      .filter(s => activeHashtags.value.every(h => s.hashtags.includes(h)))
      .map(s => s.id)
  )
  return ids
})

const FAMILLE_COLORS: Record<number, string> = {
  1: '#a85d3e',
  2: '#c4a472',
  3: '#d4a017',
  4: '#5a7a4a',
  5: '#3d6a8c',
  6: '#6b3fa0',
}

const FAMILLE_LABELS: Record<number, string> = {
  1: 'Reemploi',
  2: 'Frugalite',
  3: 'Social',
  4: 'Collectifs',
  5: 'Urbanisme',
  6: 'Recherche',
}

let simulation: any = null
let d3NodeSelection: any = null
let d3LinkSelection: any = null

async function initGraph() {
  if (!svgRef.value || !props.data) return

  const d3 = await import('d3')

  const svgEl = svgRef.value
  const width = svgEl.clientWidth || 800
  const height = svgEl.clientHeight || 600

  // Nettoyer
  d3.select(svgEl).selectAll('*').remove()

  const svg = d3.select(svgEl)
    .attr('viewBox', `0 0 ${width} ${height}`)

  // Groupe principal avec zoom
  const g = svg.append('g')
  const zoomBehavior = d3.zoom<SVGElement, unknown>()
    .scaleExtent([0.2, 4])
    .on('zoom', (event) => g.attr('transform', event.transform))

  svg.call(zoomBehavior as any)

  // Noeuds familles (centres fixes en etoile)
  const familyNodes = [1, 2, 3, 4, 5, 6].map(id => ({
    id: `family-${id}`,
    type: 'family',
    familleId: id,
    label: FAMILLE_LABELS[id],
    color: FAMILLE_COLORS[id],
    r: 32,
    x: width / 2 + Math.cos((id - 1) * Math.PI * 2 / 6) * 180,
    y: height / 2 + Math.sin((id - 1) * Math.PI * 2 / 6) * 180,
    fx: width / 2 + Math.cos((id - 1) * Math.PI * 2 / 6) * 180,
    fy: height / 2 + Math.sin((id - 1) * Math.PI * 2 / 6) * 180,
  }))

  // Noeuds structures
  const structureNodes = props.data.structures.map(s => ({
    id: s.id,
    type: 'structure',
    label: s.nom,
    famille: s.famille_principale,
    familles_secondaires: s.familles_secondaires ?? [],
    hashtags: s.hashtags,
    color: FAMILLE_COLORS[s.famille_principale] ?? '#888',
    r: 8,
    x: undefined as number | undefined,
    y: undefined as number | undefined,
    fx: undefined as number | null | undefined,
    fy: undefined as number | null | undefined,
  }))

  const allNodes: any[] = [...familyNodes, ...structureNodes]

  // Liens structures -> familles
  const links: any[] = []
  structureNodes.forEach(s => {
    links.push({
      source: s.id,
      target: `family-${s.famille}`,
      type: 'primary',
      strength: 0.55,
    })
    s.familles_secondaires.forEach((f: number) => {
      links.push({
        source: s.id,
        target: `family-${f}`,
        type: 'secondary',
        strength: 0.45,
      })
    })
  })

  // Simulation force-directed
  if (simulation) simulation.stop()
  simulation = d3.forceSimulation(allNodes)
    .force('link', d3.forceLink(links).id((d: any) => d.id).distance((d: any) => d.type === 'primary' ? 80 : 120).strength((d: any) => d.strength ?? 0.5))
    .force('charge', d3.forceManyBody().strength(-120))
    .force('center', d3.forceCenter(width / 2, height / 2))
    .force('collision', d3.forceCollide().radius((d: any) => d.r + 4))

  // Rendu liens
  d3LinkSelection = g.append('g').selectAll('line')
    .data(links)
    .join('line')
    .attr('stroke', (d: any) => d.type === 'primary' ? 'rgba(150,150,150,0.45)' : 'rgba(150,150,150,0.35)')
    .attr('stroke-width', 1.5)
    .attr('stroke-dasharray', null)

  // Rendu noeuds (groupes g)
  d3NodeSelection = g.append('g').selectAll('g')
    .data(allNodes)
    .join('g')
    .style('cursor', (d: any) => d.type === 'structure' ? 'pointer' : 'default')
    .call(
      d3.drag<any, any>()
        .on('start', (event: any, d: any) => {
          if (!event.active) simulation.alphaTarget(0.3).restart()
          d.fx = d.x
          d.fy = d.y
        })
        .on('drag', (event: any, d: any) => {
          d.fx = event.x
          d.fy = event.y
        })
        .on('end', (event: any, d: any) => {
          if (!event.active) simulation.alphaTarget(0)
          if (d.type !== 'family') {
            d.fx = null
            d.fy = null
          }
        })
    )
    .on('click', (_event: any, d: any) => {
      if (d.type === 'structure') emit('select-structure', d.id)
    })

  // Cercles
  d3NodeSelection.append('circle')
    .attr('r', (d: any) => d.r)
    .attr('fill', (d: any) => d.type === 'family' ? d.color : d.color + 'cc')
    .attr('stroke', (d: any) => d.type === 'family' ? 'white' : d.color)
    .attr('stroke-width', (d: any) => d.type === 'family' ? 3 : 1.5)

  // Labels familles
  d3NodeSelection.filter((d: any) => d.type === 'family')
    .append('text')
    .text((d: any) => d.label)
    .attr('text-anchor', 'middle')
    .attr('dy', '0.35em')
    .attr('font-size', '11px')
    .attr('font-weight', '700')
    .attr('fill', 'white')
    .style('pointer-events', 'none')

  // Tooltip hover pour structures
  d3NodeSelection.filter((d: any) => d.type === 'structure')
    .on('mouseenter', (_event: any, d: any) => {
      if (!tooltipRef.value) return
      tooltipRef.value.style.opacity = '1'
      tooltipRef.value.innerHTML = `<strong>${d.label}</strong><br><span style="opacity:0.6;font-size:0.7rem;">${FAMILLE_LABELS[d.famille] ?? ''}</span>`
    })
    .on('mousemove', (event: any) => {
      if (!tooltipRef.value || !svgEl) return
      const rect = (svgEl as HTMLElement).getBoundingClientRect()
      tooltipRef.value.style.left = (event.clientX - rect.left + 12) + 'px'
      tooltipRef.value.style.top = (event.clientY - rect.top - 10) + 'px'
    })
    .on('mouseleave', () => {
      if (tooltipRef.value) tooltipRef.value.style.opacity = '0'
    })

  // Tick - mise a jour positions
  simulation.on('tick', () => {
    d3LinkSelection
      .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)

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

    // Surlignage selon hashtags actifs
    applyHashtagFilter()
  })
}

function applyHashtagFilter() {
  if (!d3NodeSelection || !d3LinkSelection) return
  if (filteredStructureIds.value) {
    const ids = filteredStructureIds.value
    d3NodeSelection.filter((d: any) => d.type === 'structure').select('circle')
      .attr('opacity', (d: any) => ids.has(d.id) ? 1 : 0.1)
    d3LinkSelection.attr('opacity', (d: any) => {
      const srcId = typeof d.source === 'object' ? d.source.id : d.source
      return ids.has(srcId) ? 1 : 0.05
    })
  } else {
    d3NodeSelection.select('circle').attr('opacity', 1)
    d3LinkSelection.attr('opacity', 1)
  }
}

// Déclencher quand l'onglet devient visible
// Double rAF : nextTick met à jour le vdom, les 2 frames garantissent que
// le browser a calculé le layout et que clientWidth/clientHeight != 0
watch(() => props.active, (val) => {
  if (val && import.meta.client && props.data) {
    requestAnimationFrame(() => requestAnimationFrame(() => initGraph()))
  }
})

// Relancer si les données arrivent après l'activation
watch(() => props.data, (val) => {
  if (val && props.active && import.meta.client) {
    requestAnimationFrame(() => requestAnimationFrame(() => initGraph()))
  }
})

// Re-appliquer le filtre visuel sans rebuild complet
watch(activeHashtags, () => {
  applyHashtagFilter()
  if (simulation) simulation.alpha(0.01).restart()
}, { deep: true })

// Toggle sidebar : largeur SVG change -> reinit graphe apres transition CSS
watch(sidebarOpen, () => {
  if (!import.meta.client || !props.active || !props.data) return
  setTimeout(() => {
    requestAnimationFrame(() => requestAnimationFrame(() => initGraph()))
  }, 220)
})

onMounted(async () => {
  if (import.meta.client && props.data && props.active) {
    await nextTick()
    initGraph()
  }
})

onUnmounted(() => {
  if (simulation) simulation.stop()
})
</script>
