// Echte Deutschlandkarte — lädt Bundesländer-GeoJSON beim Mount und projiziert sauber.
// Daten: isellsoap/deutschlandGeoJSON (öffentliches Repo, CC0).

const GEOJSON_URL = 'https://cdn.jsdelivr.net/gh/isellsoap/deutschlandGeoJSON@main/2_bundeslaender/4_niedrig.geo.json';

const BL_NAME_TO_CODE = {
  'Schleswig-Holstein': 'SH',
  'Hamburg': 'HH',
  'Mecklenburg-Vorpommern': 'MV',
  'Niedersachsen': 'NI',
  'Bremen': 'HB',
  'Brandenburg': 'BB',
  'Berlin': 'BE',
  'Sachsen-Anhalt': 'ST',
  'Nordrhein-Westfalen': 'NW',
  'Hessen': 'HE',
  'Thüringen': 'TH',
  'Sachsen': 'SN',
  'Rheinland-Pfalz': 'RP',
  'Saarland': 'SL',
  'Baden-Württemberg': 'BW',
  'Bayern': 'BY',
};

// Forest → richer green ramp; pure lime is reserved for pins so they stay readable.
function getHeatColor(intensity) {
  const stops = [
    { t: 0.0, c: [0x1c, 0x3a, 0x2d] },   // deep forest
    { t: 0.4, c: [0x2e, 0x5a, 0x3f] },
    { t: 0.7, c: [0x4f, 0x80, 0x52] },
    { t: 1.0, c: [0x76, 0xa8, 0x5a] },   // mid-lime, never pure lime
  ];
  const t = Math.max(0, Math.min(1, intensity));
  let a = stops[0], b = stops[stops.length - 1];
  for (let i = 0; i < stops.length - 1; i++) {
    if (t >= stops[i].t && t <= stops[i + 1].t) { a = stops[i]; b = stops[i + 1]; break; }
  }
  const span = b.t - a.t || 1;
  const lt = (t - a.t) / span;
  const r = Math.round(a.c[0] + (b.c[0] - a.c[0]) * lt);
  const g = Math.round(a.c[1] + (b.c[1] - a.c[1]) * lt);
  const bl = Math.round(a.c[2] + (b.c[2] - a.c[2]) * lt);
  return `rgb(${r},${g},${bl})`;
}

function computeBounds(features) {
  let minLng = Infinity, maxLng = -Infinity, minLat = Infinity, maxLat = -Infinity;
  const visit = (ring) => {
    ring.forEach(([lng, lat]) => {
      if (lng < minLng) minLng = lng;
      if (lng > maxLng) maxLng = lng;
      if (lat < minLat) minLat = lat;
      if (lat > maxLat) maxLat = lat;
    });
  };
  features.forEach(f => {
    const g = f.geometry;
    if (!g) return;
    if (g.type === 'Polygon') g.coordinates.forEach(visit);
    else if (g.type === 'MultiPolygon') g.coordinates.forEach(poly => poly.forEach(visit));
  });
  return { minLng, maxLng, minLat, maxLat };
}

function makeProjection(bounds, w, h, pad = 24) {
  const meanLat = (bounds.minLat + bounds.maxLat) / 2;
  const lngScale = Math.cos(meanLat * Math.PI / 180);
  const xExtent = (bounds.maxLng - bounds.minLng) * lngScale;
  const yExtent = (bounds.maxLat - bounds.minLat);
  const scale = Math.min((w - pad * 2) / xExtent, (h - pad * 2) / yExtent);
  const ox = (w - xExtent * scale) / 2;
  const oy = (h - yExtent * scale) / 2;
  return (lng, lat) => ({
    x: ox + (lng - bounds.minLng) * lngScale * scale,
    y: oy + (bounds.maxLat - lat) * scale,
  });
}

function ringToPath(ring, project) {
  let d = '';
  ring.forEach(([lng, lat], i) => {
    const { x, y } = project(lng, lat);
    d += (i === 0 ? 'M ' : 'L ') + x.toFixed(2) + ' ' + y.toFixed(2) + ' ';
  });
  return d + 'Z';
}

function featureToPath(feature, project) {
  const g = feature.geometry;
  if (!g) return '';
  if (g.type === 'Polygon') {
    return g.coordinates.map(r => ringToPath(r, project)).join(' ');
  }
  if (g.type === 'MultiPolygon') {
    return g.coordinates.map(poly => poly.map(r => ringToPath(r, project)).join(' ')).join(' ');
  }
  return '';
}

function getFeatureName(feature) {
  const p = feature.properties || {};
  return p.name || p.NAME_1 || p.GEN || p.bundesland || '';
}

function GermanyMap({ properties, hoveredLand, onLandHover, onPinClick, selectedRegion, heat }) {
  const [data, setData] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [hoveredPin, setHoveredPin] = React.useState(null);

  const VB_W = 500;
  const VB_H = 620;

  React.useEffect(() => {
    let cancelled = false;
    fetch(GEOJSON_URL)
      .then(r => { if (!r.ok) throw new Error('fetch failed'); return r.json(); })
      .then(json => { if (!cancelled) setData(json); })
      .catch(err => { if (!cancelled) setError(err.message || 'failed'); });
    return () => { cancelled = true; };
  }, []);

  const { paths, project } = React.useMemo(() => {
    if (!data || !data.features) return { paths: [], project: null };
    const bounds = computeBounds(data.features);
    const proj = makeProjection(bounds, VB_W, VB_H, 24);
    const paths = data.features.map(f => ({
      feature: f,
      name: getFeatureName(f),
      code: BL_NAME_TO_CODE[getFeatureName(f)] || '',
      d: featureToPath(f, proj),
    }));
    return { paths, project: proj };
  }, [data]);

  const selectedCode = React.useMemo(() => selectedRegion ? BL_NAME_TO_CODE[selectedRegion] : null, [selectedRegion]);

  if (error) {
    return (
      <div style={{ aspectRatio: '500 / 620', display: 'grid', placeItems: 'center', color: 'rgba(248,247,242,0.5)', fontFamily: 'JetBrains Mono, monospace', fontSize: 12, padding: 32, textAlign: 'center' }}>
        Karte konnte nicht geladen werden.<br />Bitte Seite neu laden.
      </div>
    );
  }

  return (
    <svg viewBox={`0 0 ${VB_W} ${VB_H}`} style={{ width: '100%', height: 'auto', display: 'block' }}>
      <defs>
        <radialGradient id="pinPulse" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stopColor="#A8E062" stopOpacity="0.7" />
          <stop offset="100%" stopColor="#A8E062" stopOpacity="0" />
        </radialGradient>
        <filter id="pathShadow" x="-5%" y="-5%" width="110%" height="110%">
          <feDropShadow dx="0" dy="1" stdDeviation="0.5" floodColor="#000" floodOpacity="0.3" />
        </filter>
      </defs>

      {/* Loading skeleton */}
      {!data && !error && (
        <g>
          {Array.from({ length: 4 }).map((_, i) => (
            <rect
              key={i}
              x={60 + (i % 2) * 220}
              y={80 + Math.floor(i / 2) * 220}
              width="200" height="200" rx="20"
              fill="rgba(255,255,255,0.04)"
            >
              <animate attributeName="opacity" values="0.3;0.7;0.3" dur="1.6s" begin={`${i * 0.2}s`} repeatCount="indefinite" />
            </rect>
          ))}
        </g>
      )}

      {/* Bundesländer */}
      <g>
        {paths.map((p, i) => {
          if (!p.code) return null;
          const intensity = (heat && heat[p.code]) || 0.08;
          const fill = getHeatColor(intensity);
          const isHovered = hoveredLand === p.code;
          const isSelected = selectedCode === p.code;
          return (
            <path
              key={p.code || i}
              d={p.d}
              fill={fill}
              stroke={isSelected || isHovered ? '#A8E062' : 'rgba(248,247,242,0.18)'}
              strokeWidth={isSelected ? 2.4 : (isHovered ? 1.8 : 0.6)}
              strokeLinejoin="round"
              style={{
                transition: 'fill 0.25s ease, stroke 0.2s ease, stroke-width 0.2s ease, filter 0.25s ease',
                cursor: 'pointer',
                filter: isHovered ? 'brightness(1.2)' : 'none',
              }}
              onMouseEnter={() => onLandHover && onLandHover(p.code)}
              onMouseLeave={() => onLandHover && onLandHover(null)}
            />
          );
        })}
      </g>

      {/* Hovered Bundesland label */}
      {hoveredLand && (
        <g style={{ pointerEvents: 'none' }}>
          {(() => {
            const entry = Object.entries(BL_NAME_TO_CODE).find(([, code]) => code === hoveredLand);
            const name = entry ? entry[0] : '';
            return (
              <g>
                <rect x={VB_W / 2 - 100} y={6} width="200" height="24" rx="12" fill="rgba(18,38,32,0.85)" stroke="rgba(168,224,98,0.4)" strokeWidth="0.6" />
                <text
                  x={VB_W / 2} y="22"
                  textAnchor="middle"
                  fill="#A8E062"
                  style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 11, letterSpacing: '0.1em' }}
                >
                  {name.toUpperCase()}
                </text>
              </g>
            );
          })()}
        </g>
      )}

      {/* Pins */}
      {project && (
        <g>
          {properties.map((loc, i) => {
            const { x, y } = project(loc.lng, loc.lat);
            const isHovered = hoveredPin === i;
            return (
              <g
                key={loc.id || i}
                transform={`translate(${x},${y})`}
                style={{ cursor: 'pointer' }}
                onMouseEnter={() => setHoveredPin(i)}
                onMouseLeave={() => setHoveredPin(null)}
                onClick={() => onPinClick && onPinClick(loc)}
              >
                {/* Pulse */}
                <circle r={isHovered ? 16 : 11} fill="url(#pinPulse)">
                  <animate
                    attributeName="r"
                    values="7;16;7"
                    dur="2.4s"
                    begin={`${(i * 0.12) % 2}s`}
                    repeatCount="indefinite"
                  />
                  <animate
                    attributeName="opacity"
                    values="0.85;0;0.85"
                    dur="2.4s"
                    begin={`${(i * 0.12) % 2}s`}
                    repeatCount="indefinite"
                  />
                </circle>
                {/* Pin */}
                <circle r={isHovered ? 6.5 : 5.2} fill="#A8E062" stroke="#122620" strokeWidth="1.4" />
                <circle r={isHovered ? 2.4 : 1.9} fill="#122620" />
                {/* Tooltip */}
                {isHovered && (
                  <g transform="translate(0,-18)" style={{ pointerEvents: 'none' }}>
                    <rect x="-52" y="-13" width="104" height="20" rx="10" fill="#122620" stroke="#A8E062" strokeWidth="0.4" />
                    <text
                      x="0" y="1.5"
                      textAnchor="middle"
                      fill="#A8E062"
                      style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10, letterSpacing: '0.04em' }}
                    >
                      {loc.city.toUpperCase()}
                    </text>
                  </g>
                )}
              </g>
            );
          })}
        </g>
      )}
    </svg>
  );
}

Object.assign(window, { GermanyMap, BL_NAME_TO_CODE });
