import { memo, useEffect, useRef, useState, useCallback } from 'react';
import { Skeleton } from '@/components/ui';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { geocodedApi } from '@/services/api';
import type { CaseGrid, GeocodedCase } from '@/types';

interface CaseMapProps {
  height?: string;
}

type ViewMode = 'grid' | 'point';

// Grid is 100m x 100m at Wuhan latitude (~30.5°N)
const GRID_HALF_SIZE_LAT = 0.00045;  // ~50m in degrees
const GRID_HALF_SIZE_LON = 0.00052;  // ~50m in degrees

function getGridBounds(g: { latitude: number; longitude: number }) {
  if (typeof g.latitude !== 'number' || typeof g.longitude !== 'number') {
    return null;
  }
  return {
    lat_min: g.latitude - GRID_HALF_SIZE_LAT,
    lat_max: g.latitude + GRID_HALF_SIZE_LAT,
    lon_min: g.longitude - GRID_HALF_SIZE_LON,
    lon_max: g.longitude + GRID_HALF_SIZE_LON,
  };
}

const RISK_COLORS = {
  high: '#ff4444',
  medium: '#ffaa44',
  low: '#44bb44',
};

function getRiskColor(riskIndex: number): string {
  if (riskIndex >= 0.67) return RISK_COLORS.high;
  if (riskIndex >= 0.33) return RISK_COLORS.medium;
  return RISK_COLORS.low;
}

function getRiskLabel(riskIndex: number): string {
  if (riskIndex >= 0.67) return '高风险';
  if (riskIndex >= 0.33) return '中风险';
  return '低风险';
}

function debounce<T extends (...args: any[]) => void>(fn: T, ms: number) {
  let timer: ReturnType<typeof setTimeout> | null = null;
  return (...args: Parameters<T>) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => fn(...args), ms);
  };
}

function CaseMapComponent({ height = '480px' }: CaseMapProps) {
  const mapDivRef = useRef<HTMLDivElement>(null);
  const mapRef = useRef<any>(null);
  const gridLayerRef = useRef<any>(null);
  const pointLayerRef = useRef<any>(null);

  const [viewMode, setViewMode] = useState<ViewMode>('grid');
  const [grids, setGrids] = useState<CaseGrid[]>([]);
  const [cases, setCases] = useState<GeocodedCase[]>([]);
  const [totalCases, setTotalCases] = useState(0);
  const [gridCount, setGridCount] = useState(0);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let cancelled = false;

    async function fetchData() {
      setIsLoading(true);
      setError(null);
      try {
        const [gridRes, geoRes] = await Promise.all([
          geocodedApi.getGrid(),
          geocodedApi.getGeocoded({ limit: 5000 }),
        ]);
        if (cancelled) return;
        setGrids(gridRes.grids || []);
        setGridCount(gridRes.total_count || 0);
        setTotalCases(gridRes.total_cases || 0);
        setCases(geoRes.cases || []);
      } catch (err) {
        if (cancelled) return;
        setError(err instanceof Error ? err.message : '加载失败');
      } finally {
        if (!cancelled) setIsLoading(false);
      }
    }

    fetchData();
    return () => { cancelled = true; };
  }, []);

  useEffect(() => {
    if (!mapDivRef.current || mapRef.current) return;

    const map = L.map(mapDivRef.current, {
      center: [30.59, 114.31],
      zoom: 11,
      zoomControl: true,
      preferCanvas: false,
    });

    L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
      maxZoom: 19,
    }).addTo(map);

    mapRef.current = map;

    const handleZoom = debounce(() => renderLayers(), 150);
    const handleMove = debounce(() => renderLayers(), 150);

    map.on('zoomend', handleZoom);
    map.on('moveend', handleMove);

    return () => {
      if (mapRef.current) {
        mapRef.current.remove();
        mapRef.current = null;
        gridLayerRef.current = null;
        pointLayerRef.current = null;
      }
    };
  }, []);

  useEffect(() => {
    if (!mapRef.current) return;
    renderLayers();
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [grids, cases, viewMode]);

  const renderLayers = useCallback(() => {
    if (!mapRef.current) return;
    const map = mapRef.current;

    if (gridLayerRef.current) {
      try { map.removeLayer(gridLayerRef.current); } catch { /* silent */ }
      gridLayerRef.current = null;
    }
    if (pointLayerRef.current) {
      try { map.removeLayer(pointLayerRef.current); } catch { /* silent */ }
      pointLayerRef.current = null;
    }

    const zoom = map.getZoom();

    if (viewMode === 'grid') {
      const gridLayer = L.layerGroup();
      const bounds = map.getBounds();

      let rendered = 0;
      const maxRender = 5000;

      for (const g of grids) {
        if (rendered >= maxRender) break;

        const gBounds = getGridBounds(g);
        if (!gBounds) continue;

        if (
          gBounds.lat_max < bounds.getSouth() ||
          gBounds.lat_min > bounds.getNorth() ||
          gBounds.lon_max < bounds.getWest() ||
          gBounds.lon_min > bounds.getEast()
        ) {
          continue;
        }

        const color = getRiskColor(g.risk_index);
        const opacity = 0.5 + g.risk_index * 0.35;

        const rect = L.rectangle(
          [[gBounds.lat_min, gBounds.lon_min], [gBounds.lat_max, gBounds.lon_max]],
          {
            fillColor: color,
            fillOpacity: opacity,
            color: color,
            weight: zoom >= 14 ? 1 : 0,
            opacity: 0.3,
          }
        );

        rect.bindTooltip(
          `<div style="font-size: 12px;">
            <strong>网格 ${g.grid_id}</strong><br/>
            病例数: ${g.total_cases.toLocaleString()}<br/>
            风险指数: ${(g.risk_index * 100).toFixed(1)}%<br/>
            <span style="color: ${color}; font-weight: 600;">${getRiskLabel(g.risk_index)}</span>
          </div>`,
          { direction: 'top', offset: [0, -5] }
        );

        rect.addTo(gridLayer);
        rendered++;
      }

      gridLayer.addTo(map);
      gridLayerRef.current = gridLayer;
    } else {
      const pointLayer = L.layerGroup();
      const bounds = map.getBounds();

      const caseColor = (c: GeocodedCase) =>
        c.case_type === 'inpatient' ? '#DC2626' : '#2563EB';

      // Viewport culling + maxRender to avoid Leaflet canvas intersects bug
      const maxRender = 500;
      let rendered = 0;

      for (const c of cases) {
        if (rendered >= maxRender) break;
        if (typeof c.latitude !== 'number' || typeof c.longitude !== 'number') continue;

        // Viewport culling - skip points outside visible area
        if (
          c.latitude < bounds.getSouth() ||
          c.latitude > bounds.getNorth() ||
          c.longitude < bounds.getWest() ||
          c.longitude > bounds.getEast()
        ) {
          continue;
        }

        // Use tiny rectangles instead of circleMarker to avoid Leaflet 1.9.4 intersects bug
        const size = zoom >= 14 ? 0.00005 : zoom >= 12 ? 0.00003 : 0.00002;
        const rect = L.rectangle(
          [[c.latitude - size, c.longitude - size], [c.latitude + size, c.longitude + size]],
          {
            fillColor: caseColor(c),
            fillOpacity: 0.8,
            color: '#FFFFFF',
            weight: 0.5,
          }
        );

        rect.bindTooltip(
          `<div style="font-size: 12px;">
            <strong>${c.case_type === 'inpatient' ? '住院' : '门诊'}病例</strong><br/>
            坐标：${c.latitude.toFixed(5)}, ${c.longitude.toFixed(5)}
          </div>`,
          { direction: 'top', offset: [0, -5] }
        );

        rect.addTo(pointLayer);
        rendered++;
      }

      pointLayer.addTo(map);
      pointLayerRef.current = pointLayer;
    }
  }, [grids, cases, viewMode]);

  const handleToggle = useCallback((mode: ViewMode) => {
    setViewMode(mode);
  }, []);

  return (
    <div className="card">
      <div className="flex items-center justify-between px-4 py-3 border-b border-border-light">
        <div className="flex items-center gap-2">
          <svg className="w-4 h-4 text-primary" viewBox="0 0 24 24" fill="currentColor">
            <path d="M20.5 3l-.16.03L15 5.1 9 3 3.36 4.9c-.21.07-.36.25-.36.48V20.5c0 .28.22.5.5.5l.16-.03L9 18.9l6 2.1 5.64-1.9c.21-.07.36-.25.36-.48V3.5c0-.28-.22-.5-.5-.5zM15 19l-6-2.11V5l6 2.11V19z"/>
          </svg>
          <span className="font-medium text-[14px]">病例空间分布</span>
        </div>

        <div className="flex items-center gap-3">
          <div className="flex gap-0.5 bg-bg-page p-0.5 rounded">
            <button
              onClick={() => handleToggle('grid')}
              className={`px-3 py-1 text-[12px] font-medium rounded transition-colors ${
                viewMode === 'grid'
                  ? 'bg-bg-card text-primary shadow-sm'
                  : 'text-text-secondary hover:text-text-primary'
              }`}
            >
              网格视图
            </button>
            <button
              onClick={() => handleToggle('point')}
              className={`px-3 py-1 text-[12px] font-medium rounded transition-colors ${
                viewMode === 'point'
                  ? 'bg-bg-card text-primary shadow-sm'
                  : 'text-text-secondary hover:text-text-primary'
              }`}
            >
              点分布
            </button>
          </div>

          <div className="text-[11px] text-text-muted">
            {viewMode === 'grid' ? '100×100m 网格' : '个体病例定位'}
          </div>
        </div>
      </div>

      <div className="relative" style={{ height }}>
        <div ref={mapDivRef} className="w-full h-full overflow-hidden rounded-lg" />

        <div className="absolute bottom-4 right-4 bg-bg-card/90 backdrop-blur rounded-lg border border-border-light shadow-sm z-[1000] px-4 py-3">
          {viewMode === 'grid' ? (
            <>
              <div className="text-[11px] font-semibold text-text-secondary mb-2">风险等级</div>
              <div className="space-y-1.5">
                <div className="flex items-center gap-2">
                  <div className="w-4 h-4 rounded" style={{ backgroundColor: RISK_COLORS.high }} />
                  <span className="text-[11px] text-text-secondary">高风险 (&gt;67%)</span>
                </div>
                <div className="flex items-center gap-2">
                  <div className="w-4 h-4 rounded" style={{ backgroundColor: RISK_COLORS.medium }} />
                  <span className="text-[11px] text-text-secondary">中风险 (33-67%)</span>
                </div>
                <div className="flex items-center gap-2">
                  <div className="w-4 h-4 rounded" style={{ backgroundColor: RISK_COLORS.low }} />
                  <span className="text-[11px] text-text-secondary">低风险 (&lt;33%)</span>
                </div>
              </div>
            </>
          ) : (
            <>
              <div className="text-[11px] font-semibold text-text-secondary mb-2">病例类型</div>
              <div className="space-y-1.5">
                <div className="flex items-center gap-2">
                  <div className="w-4 h-4 rounded-full" style={{ backgroundColor: '#DC2626' }} />
                  <span className="text-[11px] text-text-secondary">住院病例</span>
                </div>
                <div className="flex items-center gap-2">
                  <div className="w-4 h-4 rounded-full" style={{ backgroundColor: '#2563EB' }} />
                  <span className="text-[11px] text-text-secondary">门诊病例</span>
                </div>
              </div>
            </>
          )}
        </div>

        <div className="absolute top-4 left-4 space-y-2 z-[1000]">
          <div className="bg-bg-card/90 backdrop-blur rounded-lg border border-border-light shadow-sm px-3 py-2">
            <div className="text-[11px] text-text-secondary">
              {isLoading ? (
                <Skeleton className="h-3 w-20 inline-block align-middle" />
              ) : error ? (
                <span className="text-danger">加载失败: {error}</span>
              ) : (
                <>
                  <span className="font-semibold text-text-primary">{totalCases.toLocaleString()}</span> 例病例
                  <span className="mx-2 text-border">|</span>
                  {viewMode === 'grid' ? (
                    <>
                      <span className="font-semibold text-text-primary">{gridCount.toLocaleString()}</span> 个网格
                    </>
                  ) : (
                    <>
                      <span className="font-semibold text-text-primary">{cases.length.toLocaleString()}</span> 个定位点
                    </>
                  )}
                </>
              )}
            </div>
          </div>
          {!isLoading && !error && viewMode === 'grid' && (
            <div className="bg-success/10 backdrop-blur rounded-lg border border-success/30 shadow-sm px-3 py-2">
              <div className="text-[11px] text-success font-medium">
                基于真实病例地理编码数据
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

export const CaseMap = memo(CaseMapComponent);
