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';

// Vue initiale : archipel guadeloupéen (Les Saintes → La Désirade → Basse-Terre)
const INITIAL_VIEW = { longitude: -61.35, latitude: 16.2, zoom: 9 };
// Carte verrouillée sur l'archipel guadeloupéen (+ ~25 km de marge côtière)
const MAX_BOUNDS = [[-62.2, 15.5], [-60.5, 16.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 };
}

// ── Layer style factories ──────────────────────────────────────────────────
//
// Sentinel-2 (AFAI calibré, 10 m) → orange vif, opacité forte : signal fiable
// Sentinel-3 (MCI 300 m, pas de SWIR) → or pâle, opacité faible : signal incertain
// Prévisions → orange tirets, opacité très réduite
//
const S2_COLOR   = '#f4720a';  // orange vif
const S3_COLOR   = '#d4b830';  // or/ambre pâle
const FORE_COLOR = '#f4a020';

const makeLayerStyles = (isPrediction, opacity) => {
  const sourceKey = isPrediction ? 'forecasts' : 'observations';

  const fillColor = isPrediction
    ? FORE_COLOR
    : ['match', ['get', 'source'],
        'Sentinel-2', S2_COLOR,
        S3_COLOR,              // fallback = Sentinel-3
      ];

  const fillOpacity = isPrediction
    ? 0.20 * opacity
    : ['match', ['get', 'source'],
        'Sentinel-2', 0.65 * opacity,
        0.20 * opacity,        // S3 beaucoup plus transparent
      ];

  const lineOpacity = isPrediction
    ? 0.65 * opacity
    : ['match', ['get', 'source'],
        'Sentinel-2', 0.85 * opacity,
        0.30 * opacity,
      ];

  return {
    fill: {
      id:             isPrediction ? 'forecasts-fill' : 'observations-fill',
      type:           'fill',
      source:         sourceKey,
      'source-layer': sourceKey,
      paint: {
        'fill-color':   fillColor,
        'fill-opacity': fillOpacity,
      },
    },
    line: {
      id:             isPrediction ? 'forecasts-line' : 'observations-line',
      type:           'line',
      source:         sourceKey,
      'source-layer': sourceKey,
      paint: {
        'line-color':   isPrediction ? FORE_COLOR : ['match', ['get', 'source'], 'Sentinel-2', '#c44a05', '#a08010'],
        'line-width':   isPrediction ? 2.5 : ['match', ['get', 'source'], 'Sentinel-2', 2.5, 1.0],
        'line-opacity': lineOpacity,
        ...(isPrediction ? { 'line-dasharray': [6, 4] } : {}),
      },
    },
  };
};

// ── SVG Markers ───────────────────────────────────────────────────────────
function SpotMarker({ level, scoreValue, scoreDist, 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') {
    const isWatch = (scoreValue ?? 0) >= 15 && scoreDist != null && scoreDist < 100;
    return (
      <div className={`smap-marker smap-marker--${isWatch ? 'watch' : '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={isWatch ? '#f59e0b' : '#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>
  );
}

export default function SargassesMap({ onSpotSelect, selectedSpotId, activeStep, flyToTarget }) {
  const mapRef = useRef(null);
  // spots : [{ id, name, type, region, longitude, latitude, score, level, distance_km }]
  const [spots,        setSpots]        = useState([]);
  const [spotScores,   setSpotScores]   = useState({});  // id → { level, value, distance }
  const [viewState,    setViewState]    = useState(INITIAL_VIEW);
  const [layerOpacity, setLayerOpacity] = useState(1);
  const [mapStyle,     setMapStyle]     = useState(null);   // null = style not yet ready
  const [dataDate,     setDataDate]     = useState(null);
  const [dataStale,    setDataStale]    = useState(false);  // true = données > 6h

  const tilesOrigin = window.location.origin;

  // Fraîcheur des données
  useEffect(() => {
    api.status.get()
      .then(s => {
        if (s.lastObservation?.detectedAt) {
          setDataDate(new Date(s.lastObservation.detectedAt));
        }
        setDataStale(!s.healthy);
      })
      .catch(() => setDataStale(true));
  }, []);

  // Load cartoon map style once — map is only mounted when style is ready,
  // preventing the setStyle() race condition that silently drops custom sources.
  useEffect(() => {
    const ctrl = new AbortController();
    const timeout = setTimeout(() => ctrl.abort(), 4000); // 4 s timeout
    fetch('https://tiles.openfreemap.org/styles/bright', { signal: ctrl.signal })
      .then(r => r.json())
      .then(style => setMapStyle(cartoonizeStyle(style)))
      .catch(() => setMapStyle(FALLBACK_STYLE)) // positron fallback on error/timeout
      .finally(() => clearTimeout(timeout));
    return () => ctrl.abort();
  }, []);

  // Charge tous les spots + scores en UNE requête (cachée 30 min côté serveur)
  useEffect(() => {
    api.spots.overview()
      .then(rows => {
        if (!Array.isArray(rows)) return;
        setSpots(rows);
        // Pré-remplir spotScores depuis la même réponse — aucune requête supplémentaire
        const scores = {};
        rows.forEach(r => {
          if (r.level) {
            scores[r.id] = {
              level:    r.level,
              value:    r.score   ?? 0,
              distance: r.distance_km ?? null,
            };
          }
        });
        setSpotScores(scores);
      })
      .catch(console.error);
  }, []);

  // 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 getCoords = (spot) =>
    spot.longitude != null && spot.latitude != null
      ? { lng: spot.longitude, lat: spot.latitude }
      : null;

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

  if (!mapStyle) {
    return <div className="smap-placeholder" />;
  }

  return (
    <Map
      ref={mapRef}
      {...viewState}
      onMove={e => setViewState(e.viewState)}
      onClick={() => onSpotSelect(null)}
      mapStyle={mapStyle}
      maxBounds={MAX_BOUNDS}
      style={{ width: '100%', height: '100dvh' }}
    >
      <NavigationControl position="bottom-right" />

      {dataDate && (
        <div className="smap-freshness">
          Données du {dataDate.toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit' })}
          {' à '}
          {dataDate.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}
        </div>
      )}

      {dataStale && (
        <div className="smap-stale">
          Satellite en attente — données possiblement périmées
        </div>
      )}

      {activeStep === 'now' && (
        <Source
          id="observations"
          type="vector"
          tiles={[`${tilesOrigin}/tiles/observations/{z}/{x}/{y}.pbf`]}
          minzoom={5}
          maxzoom={12}
        >
          <Layer {...OBS_STYLES.fill} />
          <Layer {...OBS_STYLES.line} />
        </Source>
      )}

      {activeStep !== 'now' && (
        <Source
          key={activeStep}
          id="forecasts"
          type="vector"
          tiles={[`${tilesOrigin}/tiles/forecasts/${activeStep}/{z}/{x}/{y}.pbf`]}
          minzoom={5}
          maxzoom={12}
        >
          <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 sc         = spotScores[spot.id];
        const level      = sc?.level;
        const scoreValue = sc?.value ?? 0;
        const scoreDist  = sc?.distance ?? null;

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