import React, { useEffect, useRef, useState } from 'react';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { lv95ToWGS84, wgs84ToLV95 } from './LV95Converter';
import type { MapViewProps } from './MapView';
import styles from './MapView.module.css';
import api from '../../../api';

// Zurich canton bounds (LV95) for default initial view
const ZURICH_BOUNDS_LV95 = { minX: 2669500, minY: 1240500, maxX: 2695500, maxY: 1295500 };
const WFS_DEBOUNCE_MS = 150;
const WFS_MIN_ZOOM = 14; // Don't load parcels when zoomed out – they render as gray blobs

// Base layers: detail (OSM) vs minimal (for parcel view)
const TILE_LAYER_DETAIL = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© OpenStreetMap contributors',
  maxZoom: 19
});
const TILE_LAYER_MINIMAL = L.tileLayer(
  'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
  { attribution: '© OpenStreetMap © CARTO', maxZoom: 19 }
);

// Fix for default marker icons in Leaflet
import icon from 'leaflet/dist/images/marker-icon.png';
import iconShadow from 'leaflet/dist/images/marker-shadow.png';

import { useLanguage } from '../../../providers/language/LanguageContext';

const DefaultIcon = L.icon({
  iconUrl: icon,
  shadowUrl: iconShadow,
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34],
  shadowSize: [41, 41]
});

L.Marker.prototype.options.icon = DefaultIcon;

const SELECTED_STYLE = { color: '#3b82f6', weight: 3, fillColor: '#3b82f6', fillOpacity: 0.3 };

const MapViewLeaflet: React.FC<MapViewProps> = ({ parcels = [],
  combinedOutline,
  center,
  zoomBounds,
  onMapClick,
  onParcelClick,
  height = '600px',
  className = '',
  emptyMessage,
  showWfsParcels = false,
  parcelsApiBaseUrl = ''
}) => {
  const { t } = useLanguage();
  const mapRef = useRef<L.Map | null>(null);
  const mapContainerRef = useRef<HTMLDivElement>(null);
  const layersRef = useRef<L.Layer[]>([]);
  const centerMarkerRef = useRef<L.Marker | null>(null);
  const parcelWfsLayerRef = useRef<L.GeoJSON | null>(null);
  const baseLayerRef = useRef<L.TileLayer | null>(null);
  const abortControllerRef = useRef<AbortController | null>(null);
  const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const lastBboxRef = useRef<string | null>(null);
  const [isWfsLoading, setIsWfsLoading] = useState(false);

  // Initialize map
  useEffect(() => {
    if (!mapContainerRef.current || mapRef.current) return;

    // Default center: Zurich canton (converted from LV95)
    const defaultCenterLV95 = center || { x: 2682500, y: 1248000 };
    const defaultCenter = lv95ToWGS84(defaultCenterLV95.x, defaultCenterLV95.y);

    // Create map (preferCanvas for smoother rendering of many parcel polygons)
    const map = L.map(mapContainerRef.current, {
      center: [defaultCenter.lat, defaultCenter.lon],
      zoom: zoomBounds ? 15 : 10,
      zoomControl: true,
      attributionControl: true,
      preferCanvas: true
    });

    const initialLayer = TILE_LAYER_DETAIL;
    initialLayer.addTo(map);
    baseLayerRef.current = initialLayer;

    // Create pane for WFS parcels (background layer, below selected parcels)
    const wfsPane = map.createPane('wfs-parcels');
    wfsPane.style.zIndex = '200';

    mapRef.current = map;

    // Cleanup
    return () => {
      map.remove();
      mapRef.current = null;
      if (debounceTimerRef.current) {
        clearTimeout(debounceTimerRef.current);
        debounceTimerRef.current = null;
      }
      abortControllerRef.current?.abort();
      abortControllerRef.current = null;
    };
  }, []); // Only run once on mount

  // Swap base layer when parcel view toggled: minimal (no shops/labels) vs full detail
  useEffect(() => {
    if (!mapRef.current || !baseLayerRef.current) return;
    const map = mapRef.current;
    const current = baseLayerRef.current;
    const next = showWfsParcels ? TILE_LAYER_MINIMAL : TILE_LAYER_DETAIL;
    if (current === next) return;
    map.removeLayer(current);
    next.addTo(map);
    baseLayerRef.current = next;
  }, [showWfsParcels]);

  // Update map center and zoom when center or zoomBounds change
  useEffect(() => {
    if (!mapRef.current) return;

    const map = mapRef.current;

    // Remove existing center marker
    if (centerMarkerRef.current) {
      map.removeLayer(centerMarkerRef.current);
      centerMarkerRef.current = null;
    }

    if (zoomBounds && parcels.length > 0) {
      // Convert zoom bounds to WGS84
      const sw = lv95ToWGS84(zoomBounds.min_x, zoomBounds.min_y);
      const ne = lv95ToWGS84(zoomBounds.max_x, zoomBounds.max_y);
      
      // Fit map to bounds
      map.fitBounds([[sw.lat, sw.lon], [ne.lat, ne.lon]], {
        padding: [20, 20],
        maxZoom: 18
      });
    } else if (center) {
      // Center on point and add marker
      const centerWGS84 = lv95ToWGS84(center.x, center.y);
      map.setView([centerWGS84.lat, centerWGS84.lon], map.getZoom() || 15);
      
      // Add center marker
      const marker = L.marker([centerWGS84.lat, centerWGS84.lon], {
        icon: L.icon({
          iconUrl: 'data:image/svg+xml;base64,' + btoa(`
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
              <circle cx="12" cy="12" r="8" fill="#ef4444" stroke="#fff" stroke-width="2"/>
            </svg>
          `),
          iconSize: [24, 24],
          iconAnchor: [12, 12]
        })
      }).addTo(map);
      centerMarkerRef.current = marker;
    } else {
      // Default: fit to canton Zurich
      const sw = lv95ToWGS84(ZURICH_BOUNDS_LV95.minX, ZURICH_BOUNDS_LV95.minY);
      const ne = lv95ToWGS84(ZURICH_BOUNDS_LV95.maxX, ZURICH_BOUNDS_LV95.maxY);
      map.fitBounds([[sw.lat, sw.lon], [ne.lat, ne.lon]], {
        padding: [20, 20],
        maxZoom: 12
      });
    }
  }, [center, zoomBounds, parcels.length]);

  // Draw parcels (only selected - all blue) or combined outline when 2+
  useEffect(() => {
    if (!mapRef.current) return;

    const map = mapRef.current;

    // Remove existing parcel layers
    layersRef.current.forEach((layer) => {
      map.removeLayer(layer);
    });
    layersRef.current = [];

    const toWgs84 = (x: number, y: number) => {
      const w = lv95ToWGS84(x, y);
      return [w.lat, w.lon] as [number, number];
    };

    // When 2+ parcels and we have combined outline from backend, draw that only
    if (combinedOutline && parcels.length > 1 && combinedOutline.coordinates?.length) {
      const geo = combinedOutline;
      if (geo.type === 'MultiPolygon') {
        const coords = geo.coordinates as number[][][][];
        coords.forEach((polyCoords) => {
          const ring = polyCoords[0];
          if (ring && ring.length >= 3) {
            const latLngs = ring.map(([x, y]) => toWgs84(x, y));
            const polygon = L.polygon(latLngs, SELECTED_STYLE);
            polygon.bindPopup(`<div><strong>${t('Ausgewählte Fläche')}</strong><br/><em>${t('Zum Entfernen Parzelle im Panel auswählen')}</em></div>`);
            if (onParcelClick) {
              polygon.on('click', () => onParcelClick('combined'));
            }
            polygon.addTo(map);
            layersRef.current.push(polygon);
          }
        });
      } else {
        const ring = (geo.coordinates as number[][][])[0];
        if (ring && ring.length >= 3) {
          const latLngs = ring.map(([x, y]) => toWgs84(x, y));
          const polygon = L.polygon(latLngs, SELECTED_STYLE);
          polygon.bindPopup(`<div><strong>${t('Ausgewählte Fläche')}</strong><br/><em>${t('Zum Entfernen Parzelle im Panel auswählen')}</em></div>`);
          if (onParcelClick) {
            polygon.on('click', () => onParcelClick('combined'));
          }
          polygon.addTo(map);
          layersRef.current.push(polygon);
        }
      }
    } else {
      // Single parcel or no combined outline: draw individual parcels (all selected)
      parcels.forEach((parcel) => {
        if (parcel.coordinates.length < 3) return;
        const latLngs = parcel.coordinates.map((coord) => toWgs84(coord.x, coord.y));
        const polygon = L.polygon(latLngs, SELECTED_STYLE);
        polygon.bindPopup(`
          <div>
            <strong>${t('Parzelle')} ${parcel.number || parcel.id}</strong><br/>
            ${parcel.egrid ? `EGRID: ${parcel.egrid}<br/>` : ''}
            <em>${t('Ausgewählt')}</em>
          </div>
        `);
        if (onParcelClick) {
          polygon.on('click', () => onParcelClick(parcel.id));
        }
        polygon.addTo(map);
        layersRef.current.push(polygon);
      });
    }
  }, [parcels, combinedOutline, onParcelClick, t]);

  // Handle map clicks
  useEffect(() => {
    if (!mapRef.current || !onMapClick) return;

    const map = mapRef.current;

    const handleMapClick = (e: L.LeafletMouseEvent) => {
      // Convert WGS84 to LV95
      const lv95 = wgs84ToLV95(e.latlng.lat, e.latlng.lng);
      onMapClick({ x: lv95.x, y: lv95.y });
    };

    map.on('click', handleMapClick);

    return () => {
      map.off('click', handleMapClick);
    };
  }, [onMapClick]);

  // Dynamic WFS parcel layer: load on moveend/zoomend when showWfsParcels
  useEffect(() => {
    if (!mapRef.current || !showWfsParcels) {
      if (mapRef.current && parcelWfsLayerRef.current) {
        mapRef.current.removeLayer(parcelWfsLayerRef.current);
        parcelWfsLayerRef.current = null;
      }
      lastBboxRef.current = null;
      setIsWfsLoading(false);
      return;
    }

    const map = mapRef.current;

    const fetchWfsParcels = () => {
      const zoom = map.getZoom();
      if (zoom < WFS_MIN_ZOOM) {
        if (parcelWfsLayerRef.current) {
          map.removeLayer(parcelWfsLayerRef.current);
          parcelWfsLayerRef.current = null;
        }
        lastBboxRef.current = null;
        setIsWfsLoading(false);
        return;
      }

      const bounds = map.getBounds();
      const sw = bounds.getSouthWest();
      const ne = bounds.getNorthEast();
      const swLv95 = wgs84ToLV95(sw.lat, sw.lng);
      const neLv95 = wgs84ToLV95(ne.lat, ne.lng);
      const minX = Math.min(swLv95.x, neLv95.x);
      const minY = Math.min(swLv95.y, neLv95.y);
      const maxX = Math.max(swLv95.x, neLv95.x);
      const maxY = Math.max(swLv95.y, neLv95.y);
      const bbox = `${minX},${minY},${maxX},${maxY}`;

      if (bbox === lastBboxRef.current) return;

      abortControllerRef.current?.abort();
      abortControllerRef.current = new AbortController();
      const signal = abortControllerRef.current.signal;
      setIsWfsLoading(true);

      const url = `${parcelsApiBaseUrl}/api/realestate/parcel/wfs?bbox=${encodeURIComponent(bbox)}`;
      api
        .get(url, { signal })
        .then((res) => {
          const data = res.data as GeoJSON.FeatureCollection;
          if (!data || data.type !== 'FeatureCollection') {
            setIsWfsLoading(false);
            return;
          }

          const geoJsonLayer = L.geoJSON(data as GeoJSON.GeoJsonObject, {
            style: { color: '#666', weight: 1, fillOpacity: 0.05 },
            interactive: false,
            pane: 'wfs-parcels'
          });
          geoJsonLayer.addTo(map);
          const prevLayer = parcelWfsLayerRef.current;
          if (prevLayer) map.removeLayer(prevLayer);
          parcelWfsLayerRef.current = geoJsonLayer;
          lastBboxRef.current = bbox;
          setIsWfsLoading(false);
        })
        .catch((err) => {
          if (err?.name !== 'CanceledError' && err?.message !== 'canceled') {
            if (import.meta.env.DEV) console.warn('WFS parcels fetch failed:', err);
          }
          setIsWfsLoading(false);
        });
    };

    const scheduleFetch = () => {
      if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
      debounceTimerRef.current = setTimeout(fetchWfsParcels, WFS_DEBOUNCE_MS);
    };

    scheduleFetch();

    map.on('moveend', scheduleFetch);
    map.on('zoomend', scheduleFetch);

    return () => {
      map.off('moveend', scheduleFetch);
      map.off('zoomend', scheduleFetch);
      if (debounceTimerRef.current) {
        clearTimeout(debounceTimerRef.current);
        debounceTimerRef.current = null;
      }
      abortControllerRef.current?.abort();
      if (parcelWfsLayerRef.current) {
        map.removeLayer(parcelWfsLayerRef.current);
        parcelWfsLayerRef.current = null;
      }
      setIsWfsLoading(false);
    };
  }, [showWfsParcels, parcelsApiBaseUrl]);

  return (
    <div className={`${styles.mapViewContainer} ${className}`} style={{ height, position: 'relative' }}>
      <div ref={mapContainerRef} style={{ width: '100%', height: '100%' }} />
      {parcels.length === 0 && !center && (
        <div className={styles.emptyStateOverlay}>
          <p>{emptyMessage || t('Klicken Sie auf die Karte, um einen Standort auszuwählen')}</p>
        </div>
      )}
      {showWfsParcels && isWfsLoading && (
        <div className={styles.wfsLoadingIndicator} aria-hidden>
          <span className={styles.wfsLoadingSpinner} />
        </div>
      )}
    </div>
  );
};

export default MapViewLeaflet;

