import { useEffect, useRef, useState, useMemo } from 'react';
import L from 'leaflet';
import type { TransactionArc } from '../data/arcData';
import { buildCorridors } from '../data/arcData';

// Leaflet default marker fix (Vite asset pipeline)
import iconUrl from 'leaflet/dist/images/marker-icon.png';
import iconShadowUrl from 'leaflet/dist/images/marker-shadow.png';
L.Icon.Default.mergeOptions({ iconUrl, shadowUrl: iconShadowUrl });

interface FlowMapProps {
  arcs:        TransactionArc[];
  focusCity:   string | null;
  onCityClick: (city: string | null) => void;
}

export function FlowMap({ arcs, focusCity, onCityClick }: FlowMapProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const mapRef       = useRef<L.Map | null>(null);
  const [mapReady, setMapReady] = useState(false);
  const [tick, setTick]         = useState(0); // incrémenté sur moveend/zoomend → re-render

  // Initialisation Leaflet
  useEffect(() => {
    if (!containerRef.current || mapRef.current) return;

    const map = L.map(containerRef.current, {
      center: [46.8, 2.35],
      zoom: 6,
      zoomControl: false,
      attributionControl: true,
    });

    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
      maxZoom: 18,
    }).addTo(map);

    L.control.zoom({ position: 'bottomright' }).addTo(map);

    mapRef.current = map;
    setMapReady(true);

    return () => {
      map.remove();
      mapRef.current = null;
      setMapReady(false);
    };
  }, []);

  // Re-projette le SVG à chaque déplacement/zoom
  useEffect(() => {
    if (!mapReady || !mapRef.current) return;
    const onMove = () => setTick(t => t + 1);
    mapRef.current.on('moveend zoomend', onMove);
    return () => { mapRef.current?.off('moveend zoomend', onMove); };
  }, [mapReady]);

  // Agrégation en corridors
  const corridors = useMemo(() => buildCorridors(arcs), [arcs]);

  // Nœuds de villes (volume entrant + sortant)
  const cityNodes = useMemo(() => {
    const map = new Map<string, { lat: number; lng: number; emitted: number; received: number }>();
    for (const c of corridors) {
      if (!map.has(c.fromCity)) map.set(c.fromCity, { lat: c.fromLat, lng: c.fromLng, emitted: 0, received: 0 });
      map.get(c.fromCity)!.emitted += c.totalVolume;
      if (!map.has(c.toCity))   map.set(c.toCity,   { lat: c.toLat,   lng: c.toLng,   emitted: 0, received: 0 });
      map.get(c.toCity)!.received += c.totalVolume;
    }
    return map;
  }, [corridors]);

  // Projection SVG (recalculée sur chaque tick, changement d'arcs ou de focusCity)
  const svgElements = useMemo(() => {
    const m = mapRef.current;
    if (!m || !mapReady) return null;

    const proj = (lat: number, lng: number) => {
      const p = m.latLngToContainerPoint([lat, lng]);
      return { x: p.x, y: p.y };
    };

    const maxVol     = Math.max(...corridors.map(c => c.totalVolume), 1);
    const maxNodeVol = Math.max(...[...cityNodes.values()].map(c => c.emitted + c.received), 1);

    // ---- Arcs ----
    const arcElems = corridors.map((c, idx) => {
      const p1 = proj(c.fromLat, c.fromLng);
      const p2 = proj(c.toLat,   c.toLng);

      // Point de contrôle bezier quadratique : décalage perpendiculaire au milieu
      const mx = (p1.x + p2.x) / 2;
      const my = (p1.y + p2.y) / 2;
      const dx = p2.x - p1.x;
      const dy = p2.y - p1.y;
      const CURVE = 0.28;
      const cx = mx - dy * CURVE;
      const cy = my + dx * CURVE;

      // Flèche de direction au milieu (t = 0.5) du bezier
      const t   = 0.5;
      const ax  = (1-t)*(1-t)*p1.x + 2*(1-t)*t*cx + t*t*p2.x;
      const ay  = (1-t)*(1-t)*p1.y + 2*(1-t)*t*cy + t*t*p2.y;
      const tln = Math.sqrt(dx*dx + dy*dy) || 1;
      const nx  = dx / tln; const ny = dy / tln;   // tangente normalisée
      const px  = -ny;      const py = nx;          // perpendiculaire
      const AR  = 5;
      const arrowPts = [
        `${ax + nx*AR},${ay + ny*AR}`,
        `${ax - nx*AR*0.6 + px*AR*0.5},${ay - ny*AR*0.6 + py*AR*0.5}`,
        `${ax - nx*AR*0.6 - px*AR*0.5},${ay - ny*AR*0.6 - py*AR*0.5}`,
      ].join(' ');

      const ratio    = c.totalVolume / maxVol;
      const strokeW  = Math.max(1, 1.5 + Math.log1p(c.totalVolume) * 0.8);
      const opacity  = 0.35 + 0.55 * ratio;

      // Couleur selon focusCity
      const isFocusFrom = focusCity && c.fromCity === focusCity;
      const isFocusTo   = focusCity && c.toCity   === focusCity;
      const stroke      = !focusCity ? `url(#grad${idx})`
                        : isFocusFrom ? '#ff8f00'
                        : isFocusTo   ? '#00acc1'
                        : '#2e2f3a';
      const arrowFill   = !focusCity ? '#e65100'
                        : isFocusFrom ? '#ff8f00'
                        : isFocusTo   ? '#00acc1'
                        : '#2e2f3a';

      return {
        idx, c, p1, p2, cx, cy, arrowPts, strokeW, opacity, stroke, arrowFill,
        path: `M ${p1.x},${p1.y} Q ${cx},${cy} ${p2.x},${p2.y}`,
      };
    });

    // ---- Nœuds ----
    const nodeElems = [...cityNodes.entries()].map(([name, data]) => {
      const p   = proj(data.lat, data.lng);
      const vol = data.emitted + data.received;
      const r   = Math.max(3, Math.min(14, 3 + 9 * (vol / maxNodeVol)));
      return { name, p, r, isSelected: focusCity === name };
    });

    return { arcElems, nodeElems };
  // tick en dep pour re-projeter sur pan/zoom
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [corridors, cityNodes, focusCity, tick, mapReady]);

  return (
    <div className="w-full h-full relative" style={{ minHeight: 0 }}>
      <div ref={containerRef} className="absolute inset-0" />

      {mapReady && svgElements && (
        <svg
          className="absolute inset-0 w-full h-full"
          style={{ zIndex: 500, pointerEvents: 'none' }}
        >
          {/* Dégradés or→orange pour les arcs (aucune ville sélectionnée) */}
          <defs>
            {svgElements.arcElems.map(a => (
              <linearGradient
                key={`grad${a.idx}`}
                id={`grad${a.idx}`}
                x1={a.p1.x} y1={a.p1.y}
                x2={a.p2.x} y2={a.p2.y}
                gradientUnits="userSpaceOnUse"
              >
                <stop offset="0%"   stopColor="#d4a843" />
                <stop offset="100%" stopColor="#e65100" />
              </linearGradient>
            ))}
          </defs>

          {/* Arcs bezier */}
          {svgElements.arcElems.map(a => (
            <g key={`${a.c.fromCity}-${a.c.toCity}`} opacity={a.opacity}>
              <path
                d={a.path}
                fill="none"
                stroke={a.stroke}
                strokeWidth={a.strokeW}
                strokeLinecap="round"
              />
              <polygon points={a.arrowPts} fill={a.arrowFill} />
            </g>
          ))}

          {/* Nœuds de villes (pointer-events activés uniquement ici) */}
          <g style={{ pointerEvents: 'all' }}>
            {svgElements.nodeElems.map(node => (
              <circle
                key={node.name}
                cx={node.p.x}
                cy={node.p.y}
                r={node.r}
                fill={node.isSelected ? '#ffffff' : '#d4a843'}
                stroke={node.isSelected ? '#d4a843' : '#0a0b0f'}
                strokeWidth={1.5}
                style={{ cursor: 'pointer' }}
                onClick={() => onCityClick(focusCity === node.name ? null : node.name)}
              />
            ))}
          </g>
        </svg>
      )}
    </div>
  );
}
