"use client";

import React, { useCallback, useState } from 'react';
import { Navigation } from 'lucide-react';
import { ChargePoint } from '@/types/chargepoint/chargepoint';
import { useNearestChargePoint } from '@/modules/chargepointModule/map/hooks/useNearestChargePoint';

interface FindNearestButtonProps {
  readonly chargePoints: ChargePoint[];
  readonly userLocation: { lat: number; lng: number } | null;
  readonly onUserLocationChange: (loc: { lat: number; lng: number }) => void;
  readonly onSelectStation: (cp: ChargePoint) => void;
  readonly mapRef?: google.maps.Map | null; // optional direct map reference if needed later
  readonly variant?: 'desktop' | 'mobile';
  readonly className?: string;
}

function haversineDistance(a: {lat:number; lng:number}, b: {lat:number; lng:number}) {
  const R = 6371e3; // metres
  const toRad = (d:number) => d * Math.PI / 180;
  const φ1 = toRad(a.lat);
  const φ2 = toRad(b.lat);
  const Δφ = toRad(b.lat - a.lat);
  const Δλ = toRad(b.lng - a.lng);
  const sinΔφ = Math.sin(Δφ/2);
  const sinΔλ = Math.sin(Δλ/2);
  const h = sinΔφ*sinΔφ + Math.cos(φ1)*Math.cos(φ2)*sinΔλ*sinΔλ;
  const c = 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1-h));
  return R * c; // meters
}

export const FindNearestButton: React.FC<FindNearestButtonProps> = ({
  chargePoints,
  userLocation,
  onUserLocationChange,
  onSelectStation,
  mapRef,
  variant = 'mobile',
  className = ''
}) => {
  const [loading, setLoading] = useState(false);
  const { fetchNearest } = useNearestChargePoint();

  const resolveLocation = useCallback((cb: (loc:{lat:number; lng:number}) => void) => {
    if (userLocation) {
      cb(userLocation);
      return;
    }
    if (!navigator.geolocation) return;
    navigator.geolocation.getCurrentPosition(
      (pos) => cb({ lat: pos.coords.latitude, lng: pos.coords.longitude }),
      () => { /* ignore */ },
      { enableHighAccuracy: true, timeout: 8000, maximumAge: 5000 }
    );
  }, [userLocation]);

  // Get location once as a Promise
  const getLocationOnce = useCallback(async (): Promise<{ lat: number; lng: number }> => {
    if (userLocation) return userLocation;
    return await new Promise((resolve) => resolveLocation((l) => resolve(l)));
  }, [resolveLocation, userLocation]);

  // Compute local fallback nearest if API returns null
  const findFallbackNearest = useCallback((loc: { lat: number; lng: number }): ChargePoint | null => {
    if (!chargePoints || chargePoints.length === 0) return null;
    let nearestLocal: ChargePoint | null = null;
    let best = Infinity;
    for (const cp of chargePoints) {
      if (!cp.coordinates) continue;
      const cpLoc = { lat: cp.coordinates[0], lng: cp.coordinates[1] };
      const d = haversineDistance(loc, cpLoc);
      if (d < best) { best = d; nearestLocal = cp; }
    }
    return nearestLocal;
  }, [chargePoints]);

  // Pan/zoom to target on map
  const panToTarget = useCallback((cp: ChargePoint) => {
    try {
      if (!mapRef || !cp.coordinates) return;
      const lat = cp.coordinates[0];
      const lng = cp.coordinates[1];
      mapRef.panTo({ lat, lng });
      const currentZoom = mapRef.getZoom() || 12;
      if (currentZoom < 15) mapRef.setZoom(15);
    } catch { /* ignore map errors */ }
  }, [mapRef]);

  const handleFind = useCallback(async () => {
    if (loading) return;
    setLoading(true);
    try {
      const loc = await getLocationOnce();
      if (!userLocation) onUserLocationChange(loc);

      // Try API-based nearest and differentiate empty results
      let target: ChargePoint | null = null;
      let apiEmpty = false;
      try {
        target = await fetchNearest(loc.lat, loc.lng, 5000, { throwOnEmpty: true });
      } catch (e: any) {
        apiEmpty = e?.message === 'NO_RESULTS';
        target = null;
      }

      // Only fallback locally if API call failed (non-empty error), not when API definitively returned empty
      if (!target && !apiEmpty) {
        target = findFallbackNearest(loc);
      }

      if (target) {
        panToTarget(target);
        onSelectStation(target);
      }
    } finally {
      setTimeout(() => setLoading(false), 300);
    }
  }, [findFallbackNearest, fetchNearest, getLocationOnce, loading, onSelectStation, onUserLocationChange, panToTarget, userLocation]);

  const commonClasses = 'transition-all duration-200 active:scale-95 focus:outline-none';
  const sizeClasses = variant === 'desktop' ? 'p-2 rounded-lg' : 'p-3 rounded-xl';
  // Use brand color for consistency with other filter buttons. Keep dark mode safe.
  const baseTheme = 'shadow-lg border text-white bg-brand-600 border-brand-600 hover:bg-brand-500 dark:bg-brand-700 dark:border-brand-700 dark:hover:bg-brand-600';
  const renderIcon = () => {
    if (loading) {
      return (
        <svg className="w-5 h-5 animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10" strokeWidth="4" className="opacity-25"/><path d="M4 12a8 8 0 018-8" strokeWidth="4" className="opacity-75"/></svg>
      );
    }
    if (variant === 'desktop') return <Navigation className="w-5 h-5" />;
    return <Navigation className="w-5 h-5" />;
  };

  return (
    <button
      onClick={handleFind}
      disabled={loading}
      title={variant === 'desktop' ? 'Find Nearest Station' : undefined}
      aria-label="Find nearest station"
      className={`${sizeClasses} ${baseTheme} ${commonClasses} disabled:opacity-50 ${className}`}
    >
      {renderIcon()}
    </button>
  );
};

