'use client';

import { useEffect, useMemo, useRef, useState } from 'react';
import type {
  Circle as LeafletCircle,
  DivIcon,
  LatLngExpression,
  LeafletMouseEvent,
  Map as LeafletMap,
  Marker as LeafletMarker,
} from 'leaflet';
import { Search, MapPin } from 'lucide-react';
import { useDebounce } from '@/hooks/useDebounce';
import { searchPlaces, type PlaceSearchResult } from '@/lib/geocoding';

interface MapPickerProps {
  latitude: number;
  longitude: number;
  onChange: (lat: number, lng: number) => void;
  onAddressSelect?: (address: string) => void;
  radius?: number;
  height?: string;
  timezone?: string;
  searchPlaceholder?: string;
  searchHint?: string;
  searchingLabel?: string;
  emptyLabel?: string;
}

const TIMEZONE_DEFAULT_CENTER: Record<string, [number, number]> = {
  'America/Los_Angeles': [34.0522, -118.2437],
  'America/Denver': [39.7392, -104.9903],
  'America/Chicago': [41.8781, -87.6298],
  'America/New_York': [40.7128, -74.006],
  'America/Sao_Paulo': [-23.5505, -46.6333],
  'Europe/London': [51.5072, -0.1276],
  'Europe/Berlin': [52.52, 13.405],
  'Europe/Moscow': [55.7558, 37.6173],
  'Asia/Dubai': [25.2048, 55.2708],
  'Asia/Shanghai': [31.2304, 121.4737],
  'Asia/Tokyo': [35.6762, 139.6503],
  'Australia/Sydney': [-33.8688, 151.2093],
  'Pacific/Auckland': [-36.8485, 174.7633],
};

function ensureLeafletCss(): Promise<void> {
  if (typeof document === 'undefined') return Promise.resolve();

  const existing = document.getElementById('leaflet-css') as HTMLLinkElement | null;
  if (existing) {
    return existing.sheet
      ? Promise.resolve()
      : new Promise((resolve) => {
          existing.addEventListener('load', () => resolve(), { once: true });
          existing.addEventListener('error', () => resolve(), { once: true });
        });
  }

  return new Promise((resolve) => {
    const link = document.createElement('link');
    link.id = 'leaflet-css';
    link.rel = 'stylesheet';
    link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
    link.onload = () => resolve();
    link.onerror = () => resolve();
    document.head.appendChild(link);
  });
}

function hasSelectedPoint(latitude: number, longitude: number): boolean {
  return Number.isFinite(latitude) && Number.isFinite(longitude) && !(latitude === 0 && longitude === 0);
}

function getFallbackCenter(timezone?: string): [number, number] {
  if (timezone && TIMEZONE_DEFAULT_CENTER[timezone]) {
    return TIMEZONE_DEFAULT_CENTER[timezone];
  }

  return [37.7749, -122.4194];
}

export function MapPicker({
  latitude,
  longitude,
  onChange,
  onAddressSelect,
  radius,
  height = '320px',
  timezone,
  searchPlaceholder = 'Search for a place',
  searchHint = 'Search for an address, city or landmark, then click the map to fine tune.',
  searchingLabel = 'Searching...',
  emptyLabel = 'No matching places found',
}: MapPickerProps) {
  const mapRef = useRef<HTMLDivElement>(null);
  const mapInstanceRef = useRef<LeafletMap | null>(null);
  const markerRef = useRef<LeafletMarker | null>(null);
  const circleRef = useRef<LeafletCircle | null>(null);
  const leafletRef = useRef<typeof import('leaflet') | null>(null);
  const searchContainerRef = useRef<HTMLDivElement>(null);
  const [ready, setReady] = useState(false);
  const [searchQuery, setSearchQuery] = useState('');
  const [searchResults, setSearchResults] = useState<PlaceSearchResult[]>([]);
  const [isSearching, setIsSearching] = useState(false);
  const [showResults, setShowResults] = useState(false);
  const debouncedSearchQuery = useDebounce(searchQuery, 350);
  const selected = useMemo(() => hasSelectedPoint(latitude, longitude), [latitude, longitude]);

  // Close search results on click outside
  useEffect(() => {
    const handleClickOutside = (e: MouseEvent) => {
      if (searchContainerRef.current && !searchContainerRef.current.contains(e.target as Node)) {
        setShowResults(false);
      }
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, []);

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

    let disposed = false;
    let resizeObserver: ResizeObserver | null = null;

    void (async () => {
      await ensureLeafletCss();
      const L = await import('leaflet');
      if (!mapRef.current || disposed) return;

      leafletRef.current = L;

      const [fallbackLat, fallbackLng] = getFallbackCenter(timezone);
      const initialLat = selected ? latitude : fallbackLat;
      const initialLng = selected ? longitude : fallbackLng;

      const map = L.map(mapRef.current, {
        center: [initialLat, initialLng],
        zoom: selected ? 15 : 12,
        zoomControl: true,
      });

      // 根据坐标判断是否在中国范围内，选择瓦片源
      const isInChina = initialLat >= 18 && initialLat <= 54 && initialLng >= 73 && initialLng <= 135;
      if (isInChina) {
        L.tileLayer('https://wprd0{s}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7', {
          attribution: '&copy; 高德地图', maxZoom: 18, subdomains: '1234',
        }).addTo(map);
      } else {
        L.tileLayer('https://mt{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&hl=zh-CN', {
          attribution: '&copy; Google Maps', maxZoom: 20, subdomains: '0123',
        }).addTo(map);
      }

      const icon = L.divIcon({
        className: '',
        html: '<div style="width:24px;height:24px;background:#2563eb;border:3px solid white;border-radius:50%;box-shadow:0 8px 24px rgba(37,99,235,0.35)"></div>',
        iconSize: [24, 24],
        iconAnchor: [12, 12],
      }) as DivIcon;

      if (selected) {
        markerRef.current = L.marker([latitude, longitude], {
          draggable: true,
          icon,
        }).addTo(map);
      }

      const bindMarkerEvents = (marker: LeafletMarker) => {
        marker.on('dragend', () => {
          const pos = marker.getLatLng();
          onChange(
            Math.round(pos.lat * 1000000) / 1000000,
            Math.round(pos.lng * 1000000) / 1000000,
          );
          circleRef.current?.setLatLng(pos);
        });
      };

      if (markerRef.current) {
        bindMarkerEvents(markerRef.current);
      }

      map.on('click', (e: LeafletMouseEvent) => {
        if (!leafletRef.current) return;
        if (!markerRef.current) {
          markerRef.current = leafletRef.current.marker(e.latlng, { draggable: true, icon }).addTo(map);
          bindMarkerEvents(markerRef.current);
        } else {
          markerRef.current.setLatLng(e.latlng);
        }

        onChange(
          Math.round(e.latlng.lat * 1000000) / 1000000,
          Math.round(e.latlng.lng * 1000000) / 1000000,
        );
        circleRef.current?.setLatLng(e.latlng);
      });

      mapInstanceRef.current = map;
      setReady(true);

      resizeObserver = new ResizeObserver(() => {
        map.invalidateSize();
      });
      resizeObserver.observe(mapRef.current);

      requestAnimationFrame(() => {
        map.invalidateSize();
      });
    })();

    return () => {
      disposed = true;
      resizeObserver?.disconnect();
      circleRef.current?.remove();
      circleRef.current = null;
      markerRef.current?.remove();
      markerRef.current = null;
      if (mapInstanceRef.current) {
        mapInstanceRef.current.remove();
        mapInstanceRef.current = null;
      }
    };
  }, [latitude, longitude, onChange, selected, timezone]);

  useEffect(() => {
    if (!debouncedSearchQuery.trim()) {
      setSearchResults([]);
      setIsSearching(false);
      return;
    }

    let cancelled = false;
    setIsSearching(true);

    void searchPlaces(debouncedSearchQuery)
      .then((results) => {
        if (!cancelled) setSearchResults(results);
      })
      .catch(() => {
        if (!cancelled) setSearchResults([]);
      })
      .finally(() => {
        if (!cancelled) setIsSearching(false);
      });

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

  useEffect(() => {
    if (!ready || !mapInstanceRef.current || !leafletRef.current) return;

    if (!selected) {
      const [fallbackLat, fallbackLng] = getFallbackCenter(timezone);
      mapInstanceRef.current.setView([fallbackLat, fallbackLng], 12);
      markerRef.current?.remove();
      markerRef.current = null;
      circleRef.current?.remove();
      circleRef.current = null;
      return;
    }

    const pos = leafletRef.current.latLng(latitude, longitude) as LatLngExpression;
    if (!markerRef.current) {
      markerRef.current = leafletRef.current.marker(pos, {
        draggable: true,
        icon: leafletRef.current.divIcon({
          className: '',
          html: '<div style="width:24px;height:24px;background:#2563eb;border:3px solid white;border-radius:50%;box-shadow:0 8px 24px rgba(37,99,235,0.35)"></div>',
          iconSize: [24, 24],
          iconAnchor: [12, 12],
        }) as DivIcon,
      }).addTo(mapInstanceRef.current);

      markerRef.current.on('dragend', () => {
        const next = markerRef.current?.getLatLng();
        if (!next) return;
        onChange(
          Math.round(next.lat * 1000000) / 1000000,
          Math.round(next.lng * 1000000) / 1000000,
        );
        circleRef.current?.setLatLng(next);
      });
    } else {
      markerRef.current.setLatLng(pos);
    }
    mapInstanceRef.current.setView(pos, Math.max(mapInstanceRef.current.getZoom(), 15));
  }, [latitude, longitude, onChange, ready, selected, timezone]);

  useEffect(() => {
    if (!ready || !mapInstanceRef.current || !leafletRef.current) return;

    circleRef.current?.remove();
    circleRef.current = null;
    if (radius && radius > 0 && selected) {
      circleRef.current = leafletRef.current.circle([latitude, longitude], {
        radius,
        color: '#2563eb',
        fillColor: '#2563eb',
        fillOpacity: 0.1,
        weight: 1,
      }).addTo(mapInstanceRef.current);
    }
  }, [radius, latitude, longitude, ready, selected]);

  const handleSelectResult = (result: PlaceSearchResult) => {
    setSearchQuery(result.displayName);
    setSearchResults([]);
    setShowResults(false);
    onChange(result.latitude, result.longitude);
    onAddressSelect?.(result.displayName);

    if (mapInstanceRef.current) {
      mapInstanceRef.current.setView([result.latitude, result.longitude], 16);
    }
  };

  return (
    <div className="space-y-3">
      <div ref={searchContainerRef} className="relative">
        <div className="relative">
          <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
          <input
            type="text"
            value={searchQuery}
            onChange={(event) => {
              setSearchQuery(event.target.value);
              setShowResults(true);
            }}
            onFocus={() => setShowResults(true)}
            placeholder={searchPlaceholder}
            className="flex h-10 w-full rounded-md border border-gray-300 bg-white pl-10 pr-3 py-2 text-sm placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
          />
        </div>
        {showResults && (isSearching || searchResults.length > 0 || debouncedSearchQuery.trim()) && (
          <div className="absolute z-[1000] mt-2 max-h-64 w-full overflow-y-auto rounded-xl border border-gray-200 bg-white shadow-lg">
            {isSearching && (
              <div className="px-4 py-3 text-sm text-gray-500">{searchingLabel}</div>
            )}
            {!isSearching && searchResults.map((result) => (
              <button
                key={`${result.latitude}-${result.longitude}-${result.displayName}`}
                type="button"
                onClick={() => handleSelectResult(result)}
                className="flex w-full items-start gap-3 px-4 py-3 text-left hover:bg-gray-50"
              >
                <MapPin className="mt-0.5 h-4 w-4 flex-shrink-0 text-blue-600" />
                <div className="min-w-0">
                  <div className="truncate text-sm font-medium text-gray-900">{result.title}</div>
                  <div className="mt-0.5 text-xs text-gray-500 line-clamp-2">{result.displayName}</div>
                </div>
              </button>
            ))}
            {!isSearching && debouncedSearchQuery.trim() && searchResults.length === 0 && (
              <div className="px-4 py-3 text-sm text-gray-500">{emptyLabel}</div>
            )}
          </div>
        )}
      </div>

      <div className="flex items-center justify-between text-xs text-gray-500">
        <span>{searchHint}</span>
        <span className="font-mono text-gray-400">
          {selected ? `${latitude.toFixed(5)}, ${longitude.toFixed(5)}` : '--'}
        </span>
      </div>

      {/* Leaflet tile fix: Tailwind preflight sets img { max-width:100%; display:block } which breaks tile rendering */}
      <style>{`.leaflet-container img { max-width: none !important; } .leaflet-container .leaflet-tile { width: 256px; height: 256px; }`}</style>
      <div
        ref={mapRef}
        style={{ height, width: '100%', borderRadius: '14px', zIndex: 0, position: 'relative' }}
        className="overflow-hidden border border-gray-200 bg-slate-50 shadow-inner"
      />
    </div>
  );
}
