import { useCallback, useEffect, useRef, useState } from 'react';
import Map, { Layer, Marker, NavigationControl, Source } from 'react-map-gl/maplibre';
import 'maplibre-gl/dist/maplibre-gl.css';
import { api } from '../../api/client';
import './SargassesMap.css';

const INITIAL_VIEW = { longitude: -61.55, latitude: 16.25, zoom: 9 };
const FALLBACK_STYLE = 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';

// ── Cartoon style transformer ──────────────────────────────────────────────
function cartoonizeStyle(style) {
  const OCEAN = '#3ab5e6', LAND = '#7ec850', LAND_DARK = '#4a7c2f';
  const LAND_MID = '#5fb040', BEACH = '#f0e9b0';
  const ROAD_MAIN = '#f5c842', ROAD_SEC = '#f0e0a0';

  const p = (layer) => layer.paint ?? {};

  const layers = style.layers.map(layer => {
    const id = (layer.id || '').toLowerCase();
    const sl = (layer['source-layer'] || '').toLowerCase();

    if (layer.type === 'background') {
      return { ...layer, paint: { 'background-color': LAND } };
    }

    const isWaterFill = layer.type === 'fill' && (
      sl === 'water' || id === 'water' || id.startsWith('water-') ||
      id.includes('-water') || id.includes('ocean') || id.includes('lake')
    );
    if (isWaterFill) {
      return { ...layer, paint: { 'fill-color': OCEAN, 'fill-opacity': 1 } };
    }

    const isLandFill = layer.type === 'fill' && (
      id === 'earth' || id === 'land' || sl === 'earth' || id.includes('landmass')
    );
    if (isLandFill) return { ...layer, paint: { ...p(layer), 'fill-color': LAND } };

    const isGreenLanduse = layer.type === 'fill' && (
      id.includes('park') || id.includes('forest') || id.includes('wood') ||
      id.includes('national') || id.includes('grass') || id.includes('vegetation')
    );
    if (isGreenLanduse) return { ...layer, paint: { ...p(layer), 'fill-color': LAND_MID, 'fill-opacity': 0.8 } };

    if (layer.type === 'fill' && (id.includes('beach') || id.includes('sand'))) {
      return { ...layer, paint: { ...p(layer), 'fill-color': BEACH } };
    }

    const isCoastLine = layer.type === 'line' && (
      sl === 'water' || id.includes('coast') || id.includes('shore') || id.includes('water-')
    );
    if (isCoastLine) {
      return { ...layer, paint: { 'line-color': LAND_DARK, 'line-width': 3 } };
    }

    if (layer.type === 'line' && (id.includes('motorway') || id.includes('trunk') || id.includes('primary'))) {
      return { ...layer, paint: { ...p(layer), 'line-color': ROAD_MAIN } };
    }
    if (layer.type === 'line' && (id.includes('secondary') || id.includes('tertiary'))) {
      return { ...layer, paint: { ...p(layer), 'line-color': ROAD_SEC } };
    }

    return layer;
  });

  return { ...style, layers };
}

// Filtre : affiche uniquement les features avec sargasses (afai > 0),
// ou si la propriété est absente (données legacy sans afaiIndex).
const OBS_FILTER = ['any', ['!', ['has', 'afai']], ['>', ['get', 'afai'], 0]];

// ── Layer style factories ──────────────────────────────────────────────────
const makeLayerStyles = (isPrediction, opacity) => ({
  fill: {
    id:     isPrediction ? 'forecasts-fill'  : 'observations-fill',
    type:   'fill',
    source: isPrediction ? 'forecasts'       : 'observations',
    paint: {
      // Prévisions : même couleur sargasses que l'observation, juste atténuée
      'fill-color':   '#f4a020',
      'fill-opacity': (isPrediction ? 0.20 : 0.50) * opacity,
      'fill-opacity-transition': { duration: 350 },
    },
  },
  line: {
    id:     isPrediction ? 'forecasts-line'  : 'observations-line',
    type:   'line',
    source: isPrediction ? 'forecasts'       : 'observations',
    paint: {
      'line-color':   '#c47a10',
      'line-width':   2.5,
      'line-opacity': opacity * (isPrediction ? 0.65 : 0.85),
      'line-opacity-transition': { duration: 350 },
      ...(isPrediction ? { 'line-dasharray': [6, 4] } : {}),
    },
  },
});

// ── SVG Markers ───────────────────────────────────────────────────────────
function SpotMarker({ level, isSelected, name }) {
  const size = isSelected ? 36 : 28;
  const shadow = 'filter: drop-shadow(2px 3px 0px rgba(0,0,0,0.35))';

  if (level === 'high') return (
    <div className={`smap-marker smap-marker--high${isSelected ? ' smap-marker--selected' : ''}`}
         title={name} style={{ width: size, height: size }}>
      <svg width={size} height={size} viewBox="0 0 28 28" style={{ filter: 'drop-shadow(2px 3px 0px rgba(0,0,0,0.35))' }}>
        <circle cx="14" cy="14" r="12" fill="#ef4444" stroke="#fff" strokeWidth="2.5"/>
        <text x="14" y="20" textAnchor="middle" fontSize="14" fontWeight="bold" fill="white" fontFamily="sans-serif">✕</text>
      </svg>
    </div>
  );

  if (level === 'medium') return (
    <div className={`smap-marker smap-marker--medium${isSelected ? ' smap-marker--selected' : ''}`}
         title={name} style={{ width: size, height: size }}>
      <svg width={size} height={size} viewBox="0 0 28 28" style={{ filter: 'drop-shadow(2px 3px 0px rgba(0,0,0,0.35))' }}>
        <circle cx="14" cy="14" r="12" fill="#f59e0b" stroke="#fff" strokeWidth="2.5"/>
        <text x="14" y="21" textAnchor="middle" fontSize="17" fontWeight="bold" fill="white" fontFamily="sans-serif">!</text>
      </svg>
    </div>
  );

  if (level === 'low') return (
    <div className={`smap-marker smap-marker--low${isSelected ? ' smap-marker--selected' : ''}`}
         title={name} style={{ width: size, height: size }}>
      <svg width={size} height={size} viewBox="0 0 28 28" style={{ filter: 'drop-shadow(2px 3px 0px rgba(0,0,0,0.35))' }}>
        <circle cx="14" cy="14" r="12" fill="#22c55e" stroke="#fff" strokeWidth="2.5"/>
        <text x="14" y="19" textAnchor="middle" fontSize="14" fill="white" fontFamily="sans-serif">★</text>
      </svg>
    </div>
  );

  // No score yet
  const s = isSelected ? 22 : 14;
  return (
    <div className={`smap-marker${isSelected ? ' smap-marker--selected' : ''}`}
         title={name} style={{ width: s, height: s }}>
      <svg width={s} height={s} viewBox="0 0 14 14" style={{ filter: 'drop-shadow(1px 2px 0px rgba(0,0,0,0.3))' }}>
        <circle cx="7" cy="7" r="5" fill={isSelected ? '#38bdf8' : '#fff'}
          stroke={isSelected ? '#0ea5e9' : '#94a3b8'} strokeWidth="2"/>
      </svg>
    </div>
  );
}

const horizonToInt = (h) => parseInt(h.replace('h', ''), 10);

export default function SargassesMap({ onSpotSelect, selectedSpotId, activeStep, flyToTarget }) {
  const mapRef = useRef(null);
  const [spots,        setSpots]        = useState([]);
  const [spotScores,   setSpotScores]   = useState({});
  const [obsGeoJSON,   setObsGeoJSON]   = useState(null);
  const [foreGeoJSON,  setForeGeoJSON]  = useState(null);
  const [viewState,    setViewState]    = useState(INITIAL_VIEW);
  const [layerOpacity, setLayerOpacity] = useState(1);
  const [mapStyle,     setMapStyle]     = useState(FALLBACK_STYLE);

  // Load cartoon map style
  useEffect(() => {
    fetch('https://tiles.openfreemap.org/styles/bright')
      .then(r => r.json())
      .then(style => setMapStyle(cartoonizeStyle(style)))
      .catch(() => {}); // keep positron on error
  }, []);

  // Load coastal spots once
  useEffect(() => {
    api.spots.list()
      .then(data => setSpots(Array.isArray(data) ? data : (data['hydra:member'] ?? data.member ?? [])))
      .catch(console.error);
  }, []);

  // Load scores progressively for marker colors
  useEffect(() => {
    if (!spots.length) return;
    spots.forEach(s => {
      api.spots.score(s.id)
        .then(d => {
          const level = d?.score?.level;
          if (level) setSpotScores(prev => ({ ...prev, [s.id]: level }));
        })
        .catch(() => {});
    });
  }, [spots]);

  // Fade transition on step change
  useEffect(() => {
    setLayerOpacity(0);
    const t = setTimeout(() => setLayerOpacity(1), 60);
    return () => clearTimeout(t);
  }, [activeStep]);

  // flyTo on island selection
  useEffect(() => {
    if (!flyToTarget) return;
    mapRef.current?.getMap()?.flyTo({ center: flyToTarget.center, zoom: flyToTarget.zoom, speed: 1.4 });
  }, [flyToTarget]);

  const loadLayers = useCallback(() => {
    const map = mapRef.current?.getMap();
    if (!map) return;
    const b    = map.getBounds();
    const bbox = [b.getWest(), b.getSouth(), b.getEast(), b.getNorth()].map(v => v.toFixed(4)).join(',');

    if (activeStep === 'now') {
      api.observations.list(bbox)
        .then(data => setObsGeoJSON(toFeatureCollection(data.items)))
        .catch(console.error);
      setForeGeoJSON(null);
    } else {
      api.forecasts.list(bbox, horizonToInt(activeStep))
        .then(data => { setForeGeoJSON(toFeatureCollection(data.items)); setObsGeoJSON(null); })
        .catch(console.error);
    }
  }, [activeStep]);

  useEffect(() => { loadLayers(); }, [loadLayers]);

  const toFeatureCollection = (items = []) => {
    const features = (items ?? []).filter(i => i.geometry)
      .map(i => ({
        type: 'Feature',
        geometry: i.geometry,
        // Ne pas injecter afai si absent : MapLibre traite null comme "has=true"
        // ce qui ferait échouer le filtre > 0 et masquerait le polygone.
        properties: typeof i.afaiIndex === 'number' ? { afai: i.afaiIndex } : {},
      }));
    return features.length ? { type: 'FeatureCollection', features } : null;
  };

  const getCoords = (spot) =>
    spot.longitude != null && spot.latitude != null
      ? { lng: spot.longitude, lat: spot.latitude }
      : null;

  const handleMapLoad = useCallback(() => {
    loadLayers();
  }, [loadLayers]);

  const OBS_STYLES  = makeLayerStyles(false, layerOpacity);
  const FORE_STYLES = makeLayerStyles(true,  layerOpacity);

  return (
    <Map
      ref={mapRef}
      {...viewState}
      onMove={e => setViewState(e.viewState)}
      onMoveEnd={loadLayers}
      onLoad={handleMapLoad}
      mapStyle={mapStyle}
      style={{ width: '100%', height: '100vh' }}
    >
      <NavigationControl position="bottom-right" />

      {obsGeoJSON && (
        <Source id="observations" type="geojson" data={obsGeoJSON}>
          <Layer {...OBS_STYLES.fill} />
          <Layer {...OBS_STYLES.line} />
        </Source>
      )}

      {foreGeoJSON && (
        <Source id="forecasts" type="geojson" data={foreGeoJSON}>
          <Layer {...FORE_STYLES.fill} />
          <Layer {...FORE_STYLES.line} />
        </Source>
      )}

      {spots.map(spot => {
        const coords = getCoords(spot);
        if (!coords) return null;
        const isSelected = spot.id === selectedSpotId;
        const level      = spotScores[spot.id];

        return (
          <Marker
            key={spot.id}
            longitude={coords.lng}
            latitude={coords.lat}
            anchor="center"
            onClick={() => onSpotSelect(spot)}
          >
            <SpotMarker level={level} isSelected={isSelected} name={spot.name} />
          </Marker>
        );
      })}
    </Map>
  );
}
