import { useEffect } 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.
 *
 * Range: -140 to -40 dBm (wide range prevents color shifting)
 *
 * -140 dBm → 0.0 (deep blue, no service)
 *  -90 dBm → 0.5 (green, fair)
 *  -40 dBm → 1.0 (red, very strong)
 */
function rsrpToIntensity(rsrp: number): number {
  const minRSRP = -140;
  const maxRSRP = -40;
  return Math.max(0, Math.min(1, (rsrp - minRSRP) / (maxRSRP - minRSRP)));
}

/**
 * ALL heatmap parameters are FIXED constants.
 * NO zoom-dependent logic — this is the only way to guarantee
 * that the same RSRP → same color at any zoom level.
 *
 * radius/blur/max are constant so overlapping point contributions
 * don't change as the user zooms, which was the root cause of
 * the color-shifting bug.
 */
const HEATMAP_RADIUS = 25;
const HEATMAP_BLUR = 15;
const HEATMAP_MAX = 0.75;

const HEATMAP_GRADIENT = {
  0.0: '#1a237e',  // Deep blue   (-140 dBm, no service)
  0.1: '#0d47a1',  // Dark blue   (-130 dBm)
  0.2: '#2196f3',  // Blue        (-120 dBm)
  0.3: '#00bcd4',  // Cyan        (-110 dBm, weak)
  0.4: '#00897b',  // Teal        (-100 dBm)
  0.5: '#4caf50',  // Green       ( -90 dBm, fair)
  0.6: '#8bc34a',  // Light green ( -80 dBm)
  0.7: '#ffeb3b',  // Yellow      ( -70 dBm, good)
  0.8: '#ffc107',  // Amber       ( -60 dBm)
  0.9: '#ff9800',  // Orange      ( -50 dBm, excellent)
  1.0: '#f44336',  // Red         ( -40 dBm, very strong)
};

export default function Heatmap({ points, visible, opacity = 0.7 }: HeatmapProps) {
  const map = useMap();

  useEffect(() => {
    if (!visible || points.length === 0) return;

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

    // Debug: log RSRP stats (dev only)
    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: map.getZoom(),
        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: HEATMAP_RADIUS,
        blur: HEATMAP_BLUR,
        max: HEATMAP_MAX,
        note: 'ALL FIXED — no zoom dependency',
        sample: normalizedSample,
      });
    }

    const heatLayer = L.heatLayer(heatData, {
      radius: HEATMAP_RADIUS,
      blur: HEATMAP_BLUR,
      max: HEATMAP_MAX,
      maxZoom: 17,
      minOpacity: 0.3,
      gradient: HEATMAP_GRADIENT,
    });

    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, opacity]);

  return null;
}
