import { useEffect, useState } from 'react';
import { useMap } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet.heat';
import type { CoveragePoint } from '@/types/index.ts';

// Extend L with heat layer type
declare module 'leaflet' {
  function heatLayer(
    latlngs: Array<[number, number, number]>,
    options?: Record<string, unknown>
  ): L.Layer;
}

interface HeatmapProps {
  points: CoveragePoint[];
  visible: boolean;
  opacity?: number;
}

/**
 * Normalize RSRP to 0-1 intensity for heatmap.
 *
 * Wide range -130 to -50 dBm ensures the full gradient is used
 * and close-in strong signals don't all saturate to one color.
 *
 * -130 dBm → 0.0 (deep blue, no service)
 *  -90 dBm → 0.5 (green, fair)
 *  -50 dBm → 1.0 (red, very strong)
 */
function rsrpToIntensity(rsrp: number): number {
  const minRSRP = -130;
  const maxRSRP = -50;
  return Math.max(0, Math.min(1, (rsrp - minRSRP) / (maxRSRP - minRSRP)));
}

/**
 * Calculate adaptive heatmap visual params based on zoom level.
 *
 * Only radius and blur change with zoom (visual quality).
 * maxIntensity is CONSTANT at 1.0 so the same RSRP always
 * maps to the same color regardless of zoom level.
 *
 * Zoom 6  (country): radius=35, blur=18
 * Zoom 10 (region):  radius=25, blur=13
 * Zoom 14 (city):    radius=15, blur=9
 * Zoom 18 (street):  radius=8,  blur=6
 */
function getHeatmapParams(zoom: number) {
  const radius = Math.max(10, Math.min(40, 60 - zoom * 3));
  const blur = Math.max(8, Math.min(25, 35 - zoom * 1.5));
  return {
    radius: Math.round(radius),
    blur: Math.round(blur),
    maxIntensity: 0.75, // FIXED at 0.75 — zoom-independent colors
  };
}

export default function Heatmap({ points, visible, opacity = 0.7 }: HeatmapProps) {
  const map = useMap();
  const [mapZoom, setMapZoom] = useState(map.getZoom());

  // Track zoom changes
  useEffect(() => {
    const handleZoomEnd = () => {
      setMapZoom(map.getZoom());
    };

    map.on('zoomend', handleZoomEnd);
    return () => {
      map.off('zoomend', handleZoomEnd);
    };
  }, [map]);

  // Recreate heatmap layer when points, visibility, or zoom changes
  useEffect(() => {
    if (!visible || points.length === 0) return;

    const heatData: Array<[number, number, number]> = points.map((p) => [
      p.lat,
      p.lon,
      rsrpToIntensity(p.rsrp),
    ]);

    const { radius, blur, maxIntensity } = getHeatmapParams(mapZoom);

    // Debug: log RSRP stats and heatmap params (detailed per spec)
    if (import.meta.env.DEV && heatData.length > 0) {
      const rsrpValues = points.map((p) => p.rsrp);
      const intensityValues = heatData.map((d) => d[2]);
      const normalizedSample = points.slice(0, 5).map((p) => ({
        rsrp: p.rsrp,
        normalized: rsrpToIntensity(p.rsrp),
      }));
      console.log('🔍 Heatmap Debug:', {
        zoom: mapZoom,
        totalPoints: points.length,
        rsrpRange: `${Math.min(...rsrpValues).toFixed(1)} to ${Math.max(...rsrpValues).toFixed(1)} dBm`,
        intensityRange: `${Math.min(...intensityValues).toFixed(3)} to ${Math.max(...intensityValues).toFixed(3)}`,
        radius,
        blur,
        maxIntensity, // Should ALWAYS be 0.75
        sample: normalizedSample,
      });
    }

    const heatLayer = L.heatLayer(heatData, {
      radius,
      blur,
      maxZoom: 17,
      max: maxIntensity,
      minOpacity: 0.3,
      gradient: {
        0.0: '#1a237e',  // Deep blue   (-130 dBm, no service)
        0.1: '#0d47a1',  // Dark blue   (-122 dBm)
        0.2: '#2196f3',  // Blue        (-114 dBm)
        0.3: '#00bcd4',  // Cyan        (-106 dBm, weak)
        0.4: '#00897b',  // Teal        ( -98 dBm)
        0.5: '#4caf50',  // Green       ( -90 dBm, fair)
        0.6: '#8bc34a',  // Light green ( -82 dBm)
        0.7: '#ffeb3b',  // Yellow      ( -74 dBm, good)
        0.8: '#ffc107',  // Amber       ( -66 dBm)
        0.9: '#ff9800',  // Orange      ( -58 dBm, excellent)
        1.0: '#f44336',  // Red         ( -50 dBm, very strong)
      },
    });

    heatLayer.addTo(map);

    // Apply opacity to the canvas element
    const container = (heatLayer as unknown as { _canvas?: HTMLCanvasElement })._canvas;
    if (container) {
      container.style.opacity = String(opacity);
    }

    return () => {
      map.removeLayer(heatLayer);
    };
  }, [map, points, visible, mapZoom, opacity]);

  return null;
}
